GIDEMMErrorHandlerTest.m 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. // Copyright 2021 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #import <TargetConditionals.h>
  15. #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
  16. #import <UIKit/UIKit.h>
  17. #import <XCTest/XCTest.h>
  18. #import "GoogleSignIn/Sources/GIDEMMErrorHandler.h"
  19. #import "GoogleSignIn/Sources/GIDSignInStrings.h"
  20. #ifdef SWIFT_PACKAGE
  21. @import GoogleUtilities_MethodSwizzler;
  22. @import GoogleUtilities_SwizzlerTestHelpers;
  23. @import OCMock;
  24. #else
  25. #import <GoogleUtilities/GULSwizzler.h>
  26. #import <GoogleUtilities/GULSwizzler+Unswizzle.h>
  27. #import <OCMock/OCMock.h>
  28. #endif
  29. NS_ASSUME_NONNULL_BEGIN
  30. // Addtional methods added to UIAlertAction for testing.
  31. @interface UIAlertAction (Testing)
  32. // Returns the handler block for this alert action.
  33. - (void (^)(UIAlertAction *))actionHandler;
  34. @end
  35. @implementation UIAlertAction (Testing)
  36. - (void (^)(UIAlertAction *))actionHandler {
  37. return [self valueForKey:@"handler"];
  38. }
  39. @end
  40. // Unit test for GIDEMMErrorHandler.
  41. @interface GIDEMMErrorHandlerTest : XCTestCase
  42. @end
  43. @implementation GIDEMMErrorHandlerTest {
  44. // Whether or not the current device runs on iOS 10.
  45. BOOL _isIOS10;
  46. // Whether key window has been set.
  47. BOOL _keyWindowSet;
  48. // The view controller that has been presented, if any.
  49. UIViewController *_presentedViewController;
  50. }
  51. - (void)setUp {
  52. [super setUp];
  53. _isIOS10 = [UIDevice currentDevice].systemVersion.integerValue == 10;
  54. _keyWindowSet = NO;
  55. _presentedViewController = nil;
  56. UIWindow *fakeKeyWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  57. [GULSwizzler swizzleClass:[GIDEMMErrorHandler class]
  58. selector:@selector(keyWindow)
  59. isClassSelector:NO
  60. withBlock:^() { return fakeKeyWindow; }];
  61. [GULSwizzler swizzleClass:[UIWindow class]
  62. selector:@selector(makeKeyAndVisible)
  63. isClassSelector:NO
  64. withBlock:^() { self->_keyWindowSet = YES; }];
  65. [GULSwizzler swizzleClass:[UIViewController class]
  66. selector:@selector(presentViewController:animated:completion:)
  67. isClassSelector:NO
  68. withBlock:^(id obj, id arg1) { self->_presentedViewController = arg1; }];
  69. [GULSwizzler swizzleClass:[GIDSignInStrings class]
  70. selector:@selector(localizedStringForKey:text:)
  71. isClassSelector:YES
  72. withBlock:^(id obj, NSString *key, NSString *text) { return text; }];
  73. }
  74. - (void)tearDown {
  75. [GULSwizzler unswizzleClass:[GIDEMMErrorHandler class]
  76. selector:@selector(keyWindow)
  77. isClassSelector:NO];
  78. [GULSwizzler unswizzleClass:[UIWindow class]
  79. selector:@selector(makeKeyAndVisible)
  80. isClassSelector:NO];
  81. [GULSwizzler unswizzleClass:[UIViewController class]
  82. selector:@selector(presentViewController:animated:completion:)
  83. isClassSelector:NO];
  84. [GULSwizzler unswizzleClass:[GIDSignInStrings class]
  85. selector:@selector(localizedStringForKey:text:)
  86. isClassSelector:YES];
  87. _presentedViewController = nil;
  88. [super tearDown];
  89. }
  90. // Expects opening a particular URL string in performing an action.
  91. - (void)expectOpenURLString:(NSString *)urlString inAction:(void (^)(void))action {
  92. // Swizzle and mock [UIApplication sharedApplication] since it is unavailable in unit tests.
  93. id mockApplication = OCMStrictClassMock([UIApplication class]);
  94. [GULSwizzler swizzleClass:[UIApplication class]
  95. selector:@selector(sharedApplication)
  96. isClassSelector:YES
  97. withBlock:^() { return mockApplication; }];
  98. if (@available(iOS 10, *)) {
  99. [[mockApplication expect] openURL:[NSURL URLWithString:urlString] options:@{} completionHandler:nil];
  100. } else {
  101. [[mockApplication expect] openURL:[NSURL URLWithString:urlString]];
  102. }
  103. action();
  104. [mockApplication verify];
  105. [GULSwizzler unswizzleClass:[UIApplication class]
  106. selector:@selector(sharedApplication)
  107. isClassSelector:YES];
  108. }
  109. // Verifies that the handler doesn't handle non-exist error.
  110. - (void)testNoError {
  111. __block BOOL completionCalled = NO;
  112. BOOL result = [[GIDEMMErrorHandler sharedInstance] handleErrorFromResponse:@{ @"abc" : @123 }
  113. completion:^() {
  114. completionCalled = YES;
  115. }];
  116. XCTAssertFalse(result);
  117. XCTAssertTrue(completionCalled);
  118. XCTAssertFalse(_keyWindowSet);
  119. XCTAssertNil(_presentedViewController);
  120. }
  121. // Verifies that the handler doesn't handle non-EMM error.
  122. - (void)testNoEMMError {
  123. __block BOOL completionCalled = NO;
  124. NSDictionary<NSString *, NSString *> *response = @{ @"error" : @"invalid_token" };
  125. BOOL result = [[GIDEMMErrorHandler sharedInstance] handleErrorFromResponse:response
  126. completion:^() {
  127. completionCalled = YES;
  128. }];
  129. XCTAssertFalse(result);
  130. XCTAssertTrue(completionCalled);
  131. XCTAssertFalse(_keyWindowSet);
  132. XCTAssertNil(_presentedViewController);
  133. }
  134. // Verifies that the handler handles general EMM error with user tapping 'OK'.
  135. - (void)testGeneralEMMErrorOK {
  136. __block BOOL completionCalled = NO;
  137. NSDictionary<NSString *, NSString *> *response = @{ @"error" : @"emm_something_wrong" };
  138. BOOL result = [[GIDEMMErrorHandler sharedInstance] handleErrorFromResponse:response
  139. completion:^() {
  140. completionCalled = YES;
  141. }];
  142. if (![UIAlertController class]) {
  143. XCTAssertFalse(result);
  144. XCTAssertTrue(completionCalled);
  145. XCTAssertFalse(_keyWindowSet);
  146. XCTAssertNil(_presentedViewController);
  147. return;
  148. }
  149. XCTAssertTrue(result);
  150. XCTAssertFalse(completionCalled);
  151. XCTAssertFalse(_keyWindowSet);
  152. XCTAssertNil(_presentedViewController);
  153. // Should handle no more error while the previous one is being handled.
  154. __block BOOL secondCompletionCalled = NO;
  155. BOOL secondResult = [[GIDEMMErrorHandler sharedInstance] handleErrorFromResponse:response
  156. completion:^() {
  157. secondCompletionCalled = YES;
  158. }];
  159. XCTAssertFalse(secondResult);
  160. XCTAssertTrue(secondCompletionCalled);
  161. XCTAssertFalse(_keyWindowSet);
  162. XCTAssertNil(_presentedViewController);
  163. // Wait for the code under test to be executed on the main thread.
  164. XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
  165. dispatch_async(dispatch_get_main_queue(), ^() {
  166. [expectation fulfill];
  167. });
  168. [self waitForExpectationsWithTimeout:1 handler:nil];
  169. XCTAssertFalse(completionCalled);
  170. XCTAssertTrue(_keyWindowSet);
  171. XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
  172. UIAlertController *alert = (UIAlertController *)_presentedViewController;
  173. XCTAssertNotNil(alert.title);
  174. XCTAssertNotNil(alert.message);
  175. XCTAssertEqual(alert.actions.count, 1);
  176. // Pretend to touch the "OK" button.
  177. UIAlertAction *action = alert.actions[0];
  178. XCTAssertEqualObjects(action.title, @"OK");
  179. action.actionHandler(action);
  180. XCTAssertTrue(completionCalled);
  181. }
  182. // Verifies that the handler handles EMM screenlock required error with user tapping 'Cancel'.
  183. - (void)testScreenlockRequiredCancel {
  184. if (_isIOS10) {
  185. // The dialog is different on iOS 10.
  186. return;
  187. }
  188. __block BOOL completionCalled = NO;
  189. NSDictionary<NSString *, NSString *> *response = @{ @"error" : @"emm_passcode_required" };
  190. BOOL result = [[GIDEMMErrorHandler sharedInstance] handleErrorFromResponse:response
  191. completion:^() {
  192. completionCalled = YES;
  193. }];
  194. if (![UIAlertController class]) {
  195. XCTAssertFalse(result);
  196. XCTAssertTrue(completionCalled);
  197. XCTAssertFalse(_keyWindowSet);
  198. XCTAssertNil(_presentedViewController);
  199. return;
  200. }
  201. XCTAssertTrue(result);
  202. XCTAssertFalse(completionCalled);
  203. XCTAssertFalse(_keyWindowSet);
  204. XCTAssertNil(_presentedViewController);
  205. // Wait for the code under test to be executed on the main thread.
  206. XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
  207. dispatch_async(dispatch_get_main_queue(), ^() {
  208. [expectation fulfill];
  209. });
  210. [self waitForExpectationsWithTimeout:1 handler:nil];
  211. XCTAssertFalse(completionCalled);
  212. XCTAssertTrue(_keyWindowSet);
  213. XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
  214. UIAlertController *alert = (UIAlertController *)_presentedViewController;
  215. XCTAssertNotNil(alert.title);
  216. XCTAssertNotNil(alert.message);
  217. XCTAssertEqual(alert.actions.count, 2);
  218. // Pretend to touch the "Cancel" button.
  219. UIAlertAction *action = alert.actions[0];
  220. XCTAssertEqualObjects(action.title, @"Cancel");
  221. action.actionHandler(action);
  222. XCTAssertTrue(completionCalled);
  223. }
  224. // Verifies that the handler handles EMM screenlock required error with user tapping 'Settings'.
  225. - (void)testScreenlockRequiredSettings {
  226. if (_isIOS10) {
  227. // The dialog is different on iOS 10.
  228. return;
  229. }
  230. __block BOOL completionCalled = NO;
  231. NSDictionary<NSString *, NSString *> *response = @{ @"error" : @"emm_passcode_required" };
  232. BOOL result = [[GIDEMMErrorHandler sharedInstance] handleErrorFromResponse:response
  233. completion:^() {
  234. completionCalled = YES;
  235. }];
  236. if (![UIAlertController class]) {
  237. XCTAssertFalse(result);
  238. XCTAssertTrue(completionCalled);
  239. XCTAssertFalse(_keyWindowSet);
  240. XCTAssertNil(_presentedViewController);
  241. return;
  242. }
  243. XCTAssertTrue(result);
  244. XCTAssertFalse(completionCalled);
  245. XCTAssertFalse(_keyWindowSet);
  246. XCTAssertNil(_presentedViewController);
  247. // Wait for the code under test to be executed on the main thread.
  248. XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
  249. dispatch_async(dispatch_get_main_queue(), ^() {
  250. [expectation fulfill];
  251. });
  252. [self waitForExpectationsWithTimeout:1 handler:nil];
  253. XCTAssertFalse(completionCalled);
  254. XCTAssertTrue(_keyWindowSet);
  255. XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
  256. UIAlertController *alert = (UIAlertController *)_presentedViewController;
  257. XCTAssertNotNil(alert.title);
  258. XCTAssertNotNil(alert.message);
  259. XCTAssertEqual(alert.actions.count, 2);
  260. // Pretend to touch the "Settings" button.
  261. UIAlertAction *action = alert.actions[1];
  262. XCTAssertEqualObjects(action.title, @"Settings");
  263. [self expectOpenURLString:UIApplicationOpenSettingsURLString inAction:^() {
  264. action.actionHandler(action);
  265. }];
  266. XCTAssertTrue(completionCalled);
  267. }
  268. - (void)testScreenlockRequiredOkOnIOS10 {
  269. if (!_isIOS10) {
  270. // A more useful dialog is used for other iOS versions.
  271. return;
  272. }
  273. __block BOOL completionCalled = NO;
  274. NSDictionary<NSString *, NSString *> *response = @{ @"error" : @"emm_passcode_required" };
  275. BOOL result = [[GIDEMMErrorHandler sharedInstance] handleErrorFromResponse:response
  276. completion:^() {
  277. completionCalled = YES;
  278. }];
  279. if (![UIAlertController class]) {
  280. XCTAssertFalse(result);
  281. XCTAssertTrue(completionCalled);
  282. XCTAssertFalse(_keyWindowSet);
  283. XCTAssertNil(_presentedViewController);
  284. return;
  285. }
  286. XCTAssertTrue(result);
  287. XCTAssertFalse(completionCalled);
  288. XCTAssertFalse(_keyWindowSet);
  289. XCTAssertNil(_presentedViewController);
  290. // Wait for the code under test to be executed on the main thread.
  291. XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
  292. dispatch_async(dispatch_get_main_queue(), ^() {
  293. [expectation fulfill];
  294. });
  295. [self waitForExpectationsWithTimeout:1 handler:nil];
  296. XCTAssertFalse(completionCalled);
  297. XCTAssertTrue(_keyWindowSet);
  298. XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
  299. UIAlertController *alert = (UIAlertController *)_presentedViewController;
  300. XCTAssertNotNil(alert.title);
  301. XCTAssertNotNil(alert.message);
  302. XCTAssertEqual(alert.actions.count, 1);
  303. // Pretend to touch the "OK" button.
  304. UIAlertAction *action = alert.actions[0];
  305. XCTAssertEqualObjects(action.title, @"OK");
  306. action.actionHandler(action);
  307. XCTAssertTrue(completionCalled);
  308. }
  309. // Verifies that the handler handles EMM app verification required error without a URL.
  310. - (void)testAppVerificationNoURL {
  311. __block BOOL completionCalled = NO;
  312. NSDictionary<NSString *, NSString *> *response = @{ @"error" : @"emm_app_verification_required" };
  313. BOOL result = [[GIDEMMErrorHandler sharedInstance] handleErrorFromResponse:response
  314. completion:^() {
  315. completionCalled = YES;
  316. }];
  317. if (![UIAlertController class]) {
  318. XCTAssertFalse(result);
  319. XCTAssertTrue(completionCalled);
  320. XCTAssertFalse(_keyWindowSet);
  321. XCTAssertNil(_presentedViewController);
  322. return;
  323. }
  324. XCTAssertTrue(result);
  325. XCTAssertFalse(completionCalled);
  326. XCTAssertFalse(_keyWindowSet);
  327. XCTAssertNil(_presentedViewController);
  328. // Wait for the code under test to be executed on the main thread.
  329. XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
  330. dispatch_async(dispatch_get_main_queue(), ^() {
  331. [expectation fulfill];
  332. });
  333. [self waitForExpectationsWithTimeout:1 handler:nil];
  334. XCTAssertFalse(completionCalled);
  335. XCTAssertTrue(_keyWindowSet);
  336. XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
  337. UIAlertController *alert = (UIAlertController *)_presentedViewController;
  338. XCTAssertNotNil(alert.title);
  339. XCTAssertNotNil(alert.message);
  340. XCTAssertEqual(alert.actions.count, 1);
  341. // Pretend to touch the "OK" button.
  342. UIAlertAction *action = alert.actions[0];
  343. XCTAssertEqualObjects(action.title, @"OK");
  344. action.actionHandler(action);
  345. XCTAssertTrue(completionCalled);
  346. }
  347. // Verifies that the handler handles EMM app verification required error user tapping 'Cancel'.
  348. - (void)testAppVerificationCancel {
  349. __block BOOL completionCalled = NO;
  350. NSDictionary<NSString *, NSString *> *response =
  351. @{ @"error" : @"emm_app_verification_required: https://host.domain/path" };
  352. BOOL result = [[GIDEMMErrorHandler sharedInstance] handleErrorFromResponse:response
  353. completion:^() {
  354. completionCalled = YES;
  355. }];
  356. if (![UIAlertController class]) {
  357. XCTAssertFalse(result);
  358. XCTAssertTrue(completionCalled);
  359. XCTAssertFalse(_keyWindowSet);
  360. XCTAssertNil(_presentedViewController);
  361. return;
  362. }
  363. XCTAssertTrue(result);
  364. XCTAssertFalse(completionCalled);
  365. XCTAssertFalse(_keyWindowSet);
  366. XCTAssertNil(_presentedViewController);
  367. // Wait for the code under test to be executed on the main thread.
  368. XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
  369. dispatch_async(dispatch_get_main_queue(), ^() {
  370. [expectation fulfill];
  371. });
  372. [self waitForExpectationsWithTimeout:1 handler:nil];
  373. XCTAssertFalse(completionCalled);
  374. XCTAssertTrue(_keyWindowSet);
  375. XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
  376. UIAlertController *alert = (UIAlertController *)_presentedViewController;
  377. XCTAssertNotNil(alert.title);
  378. XCTAssertNotNil(alert.message);
  379. XCTAssertEqual(alert.actions.count, 2);
  380. // Pretend to touch the "Cancel" button.
  381. UIAlertAction *action = alert.actions[0];
  382. XCTAssertEqualObjects(action.title, @"Cancel");
  383. action.actionHandler(action);
  384. XCTAssertTrue(completionCalled);
  385. }
  386. // Verifies that the handler handles EMM app verification required error user tapping 'Connect'.
  387. - (void)testAppVerificationConnect {
  388. __block BOOL completionCalled = NO;
  389. NSDictionary<NSString *, NSString *> *response =
  390. @{ @"error" : @"emm_app_verification_required: https://host.domain/path" };
  391. BOOL result = [[GIDEMMErrorHandler sharedInstance] handleErrorFromResponse:response
  392. completion:^() {
  393. completionCalled = YES;
  394. }];
  395. if (![UIAlertController class]) {
  396. XCTAssertFalse(result);
  397. XCTAssertTrue(completionCalled);
  398. XCTAssertFalse(_keyWindowSet);
  399. XCTAssertNil(_presentedViewController);
  400. return;
  401. }
  402. XCTAssertTrue(result);
  403. XCTAssertFalse(completionCalled);
  404. XCTAssertFalse(_keyWindowSet);
  405. XCTAssertNil(_presentedViewController);
  406. // Wait for the code under test to be executed on the main thread.
  407. XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
  408. dispatch_async(dispatch_get_main_queue(), ^() {
  409. [expectation fulfill];
  410. });
  411. [self waitForExpectationsWithTimeout:1 handler:nil];
  412. XCTAssertFalse(completionCalled);
  413. XCTAssertTrue(_keyWindowSet);
  414. XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
  415. UIAlertController *alert = (UIAlertController *)_presentedViewController;
  416. XCTAssertNotNil(alert.title);
  417. XCTAssertNotNil(alert.message);
  418. XCTAssertEqual(alert.actions.count, 2);
  419. // Pretend to touch the "Connect" button.
  420. UIAlertAction *action = alert.actions[1];
  421. XCTAssertEqualObjects(action.title, @"Connect");
  422. [self expectOpenURLString:@"https://host.domain/path" inAction:^() {
  423. action.actionHandler(action);
  424. }];
  425. XCTAssertTrue(completionCalled);
  426. }
  427. // Verifies that the handler can handle sequential errors independently.
  428. - (void)testSequentialErrors {
  429. [self testGeneralEMMErrorOK];
  430. _keyWindowSet = NO;
  431. _presentedViewController = nil;
  432. [self testScreenlockRequiredCancel];
  433. }
  434. // Temporarily disable testKeyWindow for Xcode 12 and under due to unexplained failure.
  435. #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000
  436. // Verifies that the `keyWindow` internal method works on all OS versions as expected.
  437. - (void)testKeyWindow {
  438. // The original method has been swizzled in `setUp` so get its original implementation to test.
  439. typedef id (*KeyWindowSignature)(id, SEL);
  440. KeyWindowSignature keyWindowFunction = (KeyWindowSignature)
  441. [GULSwizzler originalImplementationForClass:[GIDEMMErrorHandler class]
  442. selector:@selector(keyWindow)
  443. isClassSelector:NO];
  444. UIWindow *mockKeyWindow = OCMClassMock([UIWindow class]);
  445. OCMStub(mockKeyWindow.isKeyWindow).andReturn(YES);
  446. UIApplication *mockApplication = OCMClassMock([UIApplication class]);
  447. [GULSwizzler swizzleClass:[UIApplication class]
  448. selector:@selector(sharedApplication)
  449. isClassSelector:YES
  450. withBlock:^{ return mockApplication; }];
  451. #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000
  452. if (@available(iOS 15, *)) {
  453. UIWindowScene *mockWindowScene = OCMClassMock([UIWindowScene class]);
  454. OCMStub(mockApplication.connectedScenes).andReturn(@[mockWindowScene]);
  455. OCMStub(mockWindowScene.activationState).andReturn(UISceneActivationStateForegroundActive);
  456. OCMStub(mockWindowScene.keyWindow).andReturn(mockKeyWindow);
  457. } else
  458. #endif // __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000
  459. {
  460. #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_15_0
  461. if (@available(iOS 13, *)) {
  462. OCMStub(mockApplication.windows).andReturn(@[mockKeyWindow]);
  463. } else {
  464. #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_13_0
  465. OCMStub(mockApplication.keyWindow).andReturn(mockKeyWindow);
  466. #endif // __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_13_0
  467. }
  468. #endif // __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_15_0
  469. }
  470. UIWindow *keyWindow =
  471. keyWindowFunction([GIDEMMErrorHandler sharedInstance], @selector(keyWindow));
  472. XCTAssertEqual(keyWindow, mockKeyWindow);
  473. [GULSwizzler unswizzleClass:[UIApplication class]
  474. selector:@selector(sharedApplication)
  475. isClassSelector:YES];
  476. }
  477. #endif // __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000
  478. @end
  479. NS_ASSUME_NONNULL_END
  480. #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST