FIRAuthAppDelegateProxyTests.m 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <XCTest/XCTest.h>
  17. #import <UIKit/UIKit.h>
  18. #import <objc/runtime.h>
  19. #import "FIRAuthAppDelegateProxy.h"
  20. #import <OCMock/OCMock.h>
  21. NS_ASSUME_NONNULL_BEGIN
  22. /** @class FIRAuthEmptyAppDelegate
  23. @brief A @c UIApplicationDelegate implementation that does nothing.
  24. */
  25. @interface FIRAuthEmptyAppDelegate : NSObject <UIApplicationDelegate>
  26. @end
  27. @implementation FIRAuthEmptyAppDelegate
  28. @end
  29. /** @class FIRAuthLegacyAppDelegate
  30. @brief A @c UIApplicationDelegate implementation that implements
  31. `application:didReceiveRemoteNotification:` and
  32. `application:openURL:sourceApplication:annotation:`.
  33. */
  34. @interface FIRAuthLegacyAppDelegate : NSObject <UIApplicationDelegate>
  35. /** @var notificationReceived
  36. @brief The last notification received, if any.
  37. */
  38. @property(nonatomic, copy, nullable) NSDictionary *notificationReceived;
  39. /** @var urlOpened
  40. @brief The last URL opened, if any.
  41. */
  42. @property(nonatomic, copy, nullable) NSURL *urlOpened;
  43. @end
  44. @implementation FIRAuthLegacyAppDelegate
  45. - (void)application:(UIApplication *)application
  46. didReceiveRemoteNotification:(NSDictionary *)userInfo {
  47. self.notificationReceived = userInfo;
  48. }
  49. - (BOOL)application:(UIApplication *)application
  50. openURL:(NSURL *)url
  51. sourceApplication:(nullable NSString *)sourceApplication
  52. annotation:(id)annotation {
  53. self.urlOpened = url;
  54. return NO;
  55. }
  56. @end
  57. /** @class FIRAuthModernAppDelegate
  58. @brief A @c UIApplicationDelegate implementation that implements both
  59. `application:didRegisterForRemoteNotificationsWithDeviceToken:`,
  60. `application:didReceiveRemoteNotification:fetchCompletionHandler:`, and
  61. `application:openURL:options:`.
  62. */
  63. @interface FIRAuthModernAppDelegate : NSObject <UIApplicationDelegate>
  64. /** @var deviceTokenReceived
  65. @brief The last device token received, if any.
  66. */
  67. @property(nonatomic, copy, nullable) NSData *deviceTokenReceived;
  68. /** @var tokenErrorReceived
  69. @brief The last token error received, if any.
  70. */
  71. @property(nonatomic, copy, nullable) NSError *tokenErrorReceived;
  72. /** @var notificationReceived
  73. @brief The last notification received, if any.
  74. */
  75. @property(nonatomic, copy, nullable) NSDictionary *notificationReceived;
  76. /** @var urlOpened
  77. @brief The last URL opened, if any.
  78. */
  79. @property(nonatomic, copy, nullable) NSURL *urlOpened;
  80. @end
  81. @implementation FIRAuthModernAppDelegate
  82. - (void)application:(UIApplication *)application
  83. didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
  84. self.deviceTokenReceived = deviceToken;
  85. }
  86. - (void)application:(UIApplication *)application
  87. didFailToRegisterForRemoteNotificationsWithError:(nonnull NSError *)error {
  88. self.tokenErrorReceived = error;
  89. }
  90. - (void)application:(UIApplication *)application
  91. didReceiveRemoteNotification:(NSDictionary *)userInfo
  92. fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  93. self.notificationReceived = userInfo;
  94. completionHandler(UIBackgroundFetchResultNewData);
  95. }
  96. - (BOOL)application:(UIApplication *)app
  97. openURL:(NSURL *)url
  98. options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
  99. self.urlOpened = url;
  100. return NO;
  101. }
  102. @end
  103. /** @class FIRAuthOtherLegacyAppDelegate
  104. @brief A @c UIApplicationDelegate implementation that implements `application:handleOpenURL:`.
  105. */
  106. @interface FIRAuthOtherLegacyAppDelegate : NSObject <UIApplicationDelegate>
  107. /** @var urlOpened
  108. @brief The last URL opened, if any.
  109. */
  110. @property(nonatomic, copy, nullable) NSURL *urlOpened;
  111. @end
  112. @implementation FIRAuthOtherLegacyAppDelegate
  113. - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
  114. self.urlOpened = url;
  115. return NO;
  116. }
  117. @end
  118. /** @class FIRAuthAppDelegateProxyTests
  119. @brief Unit tests for @c FIRAuthAppDelegateProxy .
  120. */
  121. @interface FIRAuthAppDelegateProxyTests : XCTestCase
  122. @end
  123. @implementation FIRAuthAppDelegateProxyTests {
  124. /** @var _mockApplication
  125. @brief The mock UIApplication used for testing.
  126. */
  127. id _mockApplication;
  128. /** @var _deviceToken
  129. @brief The fake APNs device token for testing.
  130. */
  131. NSData *_deviceToken;
  132. /** @var _error
  133. @brief The fake error for testing.
  134. */
  135. NSError *_error;
  136. /** @var _notification
  137. @brief The fake notification for testing.
  138. */
  139. NSDictionary* _notification;
  140. /** @var _url
  141. @brief The fake URL for testing.
  142. */
  143. NSURL *_url;
  144. /** @var _isIOS9orLater
  145. @brief Whether the OS version is iOS 9 or later.
  146. */
  147. BOOL _isIOS9orLater;
  148. }
  149. - (void)setUp {
  150. [super setUp];
  151. _mockApplication = OCMClassMock([UIApplication class]);
  152. _deviceToken = [@"asdf" dataUsingEncoding:NSUTF8StringEncoding];
  153. _error = [NSError errorWithDomain:@"FakeError" code:12345 userInfo:nil];
  154. _notification = @{ @"zxcv" : @1234 };
  155. _url = [NSURL URLWithString:@"https://abc.def/ghi"];
  156. _isIOS9orLater = [[[UIDevice currentDevice] systemVersion] doubleValue] >= 9.0;
  157. }
  158. - (void)tearDown {
  159. OCMVerifyAll(_mockApplication);
  160. [super tearDown];
  161. }
  162. /** @fn testSharedInstance
  163. @brief Tests that the shared instance is the same one.
  164. */
  165. - (void)testSharedInstance {
  166. FIRAuthAppDelegateProxy *proxy1 = [FIRAuthAppDelegateProxy sharedInstance];
  167. FIRAuthAppDelegateProxy *proxy2 = [FIRAuthAppDelegateProxy sharedInstance];
  168. XCTAssertEqual(proxy1, proxy2);
  169. }
  170. /** @fn testNilApplication
  171. @brief Tests that initialization fails if the application is nil.
  172. */
  173. - (void)testNilApplication {
  174. XCTAssertNil([[FIRAuthAppDelegateProxy alloc] initWithApplication:nil]);
  175. }
  176. /** @fn testNilDelegate
  177. @brief Tests that initialization fails if the application's delegate is nil.
  178. */
  179. - (void)testNilDelegate {
  180. OCMExpect([_mockApplication delegate]).andReturn(nil);
  181. XCTAssertNil([[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication]);
  182. }
  183. /** @fn testNonconformingDelegate
  184. @brief Tests that initialization fails if the application's delegate does not conform to
  185. @c UIApplicationDelegate protocol.
  186. */
  187. - (void)testNonconformingDelegate {
  188. OCMExpect([_mockApplication delegate]).andReturn(@"abc");
  189. XCTAssertNil([[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication]);
  190. }
  191. /** @fn testDisabledByBundleEntry
  192. @brief Tests that initialization fails if the proxy is disabled by a bundle entry.
  193. */
  194. - (void)testDisabledByBundleEntry {
  195. // Swizzle NSBundle's objectForInfoDictionaryKey to return @NO for the specific key.
  196. Method method = class_getInstanceMethod([NSBundle class], @selector(objectForInfoDictionaryKey:));
  197. __block IMP originalImplementation;
  198. IMP newImplmentation = imp_implementationWithBlock(^id(id object, NSString *key) {
  199. if ([key isEqualToString:@"FirebaseAppDelegateProxyEnabled"]) {
  200. return @NO;
  201. }
  202. typedef id (*Implementation)(id object, SEL cmd, NSString *key);
  203. return ((Implementation)originalImplementation)(object, @selector(objectForInfoDictionaryKey:),
  204. key);
  205. });
  206. originalImplementation = method_setImplementation(method, newImplmentation);
  207. // Verify that initialization fails.
  208. FIRAuthEmptyAppDelegate *delegate = [[FIRAuthEmptyAppDelegate alloc] init];
  209. OCMStub([_mockApplication delegate]).andReturn(delegate);
  210. XCTAssertNil([[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication]);
  211. // Unswizzle.
  212. imp_removeBlock(method_setImplementation(method, originalImplementation));
  213. }
  214. // Deprecated methods are call intentionally in tests to verify behaviors on older iOS systems.
  215. #pragma clang diagnostic push
  216. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  217. /** @fn testEmptyDelegateOneHandler
  218. @brief Tests that the proxy works against an empty @c UIApplicationDelegate for one handler.
  219. */
  220. - (void)testEmptyDelegateOneHandler {
  221. FIRAuthEmptyAppDelegate *delegate = [[FIRAuthEmptyAppDelegate alloc] init];
  222. OCMExpect([_mockApplication delegate]).andReturn(delegate);
  223. __weak id weakProxy;
  224. @autoreleasepool {
  225. FIRAuthAppDelegateProxy *proxy =
  226. [[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication];
  227. XCTAssertNotNil(proxy);
  228. // Verify certain methods are swizzled while others are not.
  229. XCTAssertTrue([delegate respondsToSelector:
  230. @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:)]);
  231. XCTAssertTrue([delegate respondsToSelector:
  232. @selector(application:didFailToRegisterForRemoteNotificationsWithError:)]);
  233. XCTAssertTrue([delegate respondsToSelector:
  234. @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)]);
  235. XCTAssertFalse([delegate respondsToSelector:
  236. @selector(application:didReceiveRemoteNotification:)]);
  237. if (_isIOS9orLater) {
  238. XCTAssertTrue([delegate respondsToSelector:@selector(application:openURL:options:)]);
  239. XCTAssertFalse([delegate respondsToSelector:
  240. @selector(application:openURL:sourceApplication:annotation:)]);
  241. } else {
  242. XCTAssertFalse([delegate respondsToSelector:@selector(application:openURL:options:)]);
  243. XCTAssertTrue([delegate respondsToSelector:
  244. @selector(application:openURL:sourceApplication:annotation:)]);
  245. }
  246. XCTAssertFalse([delegate respondsToSelector:@selector(application:handleOpenURL:)]);
  247. // Verify the handler is called after being added.
  248. __weak id weakHandler;
  249. @autoreleasepool {
  250. id mockHandler = OCMProtocolMock(@protocol(FIRAuthAppDelegateHandler));
  251. [proxy addHandler:mockHandler];
  252. // Verify `application:didRegisterForRemoteNotificationsWithDeviceToken:` is handled.
  253. OCMExpect([mockHandler setAPNSToken:_deviceToken]);
  254. [delegate application:_mockApplication
  255. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  256. OCMVerifyAll(mockHandler);
  257. // Verify `application:didFailToRegisterForRemoteNotificationsWithError:` is handled.
  258. OCMExpect([mockHandler handleAPNSTokenError:_error]);
  259. [delegate application:_mockApplication
  260. didFailToRegisterForRemoteNotificationsWithError:_error];
  261. OCMVerifyAll(mockHandler);
  262. // Verify `application:didReceiveRemoteNotification:fetchCompletionHandler:` is handled.
  263. OCMExpect([mockHandler canHandleNotification:_notification]).andReturn(YES);
  264. __block BOOL fetchCompletionHandlerCalled = NO;
  265. [delegate application:_mockApplication
  266. didReceiveRemoteNotification:_notification
  267. fetchCompletionHandler:^(UIBackgroundFetchResult result) {
  268. XCTAssertEqual(result, UIBackgroundFetchResultNoData);
  269. fetchCompletionHandlerCalled = YES;
  270. }];
  271. OCMVerifyAll(mockHandler);
  272. XCTAssertTrue(fetchCompletionHandlerCalled);
  273. // Verify one of the `application:openURL:...` methods is handled.
  274. OCMExpect([mockHandler canHandleURL:_url]).andReturn(YES);
  275. if (_isIOS9orLater) {
  276. // Verify `application:openURL:options:` is handled.
  277. XCTAssertTrue([delegate application:_mockApplication openURL:_url options:@{}]);
  278. } else {
  279. // Verify `application:openURL:sourceApplication:annotation:` is handled.
  280. XCTAssertTrue([delegate application:_mockApplication
  281. openURL:_url
  282. sourceApplication:@"sourceApplication"
  283. annotation:@"annotaton"]);
  284. }
  285. OCMVerifyAll(mockHandler);
  286. weakHandler = mockHandler;
  287. XCTAssertNotNil(weakHandler);
  288. }
  289. // Verify the handler is not retained by the proxy.
  290. XCTAssertNil(weakHandler);
  291. // Verify nothing bad happens after the handler is released.
  292. [delegate application:_mockApplication
  293. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  294. [delegate application:_mockApplication
  295. didFailToRegisterForRemoteNotificationsWithError:_error];
  296. [delegate application:_mockApplication
  297. didReceiveRemoteNotification:_notification
  298. fetchCompletionHandler:^(UIBackgroundFetchResult result) {
  299. XCTFail(@"Should not call completion handler.");
  300. }];
  301. if (_isIOS9orLater) {
  302. XCTAssertFalse([delegate application:_mockApplication openURL:_url options:@{}]);
  303. } else {
  304. XCTAssertFalse([delegate application:_mockApplication
  305. openURL:_url
  306. sourceApplication:@"sourceApplication"
  307. annotation:@"annotaton"]);
  308. }
  309. weakProxy = proxy;
  310. XCTAssertNotNil(weakProxy);
  311. }
  312. // Verify the proxy does not retain itself.
  313. XCTAssertNil(weakProxy);
  314. // Verify nothing bad happens after the proxy is released.
  315. [delegate application:_mockApplication
  316. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  317. [delegate application:_mockApplication
  318. didReceiveRemoteNotification:_notification
  319. fetchCompletionHandler:^(UIBackgroundFetchResult result) {
  320. XCTFail(@"Should not call completion handler.");
  321. }];
  322. if (_isIOS9orLater) {
  323. XCTAssertFalse([delegate application:_mockApplication openURL:_url options:@{}]);
  324. } else {
  325. XCTAssertFalse([delegate application:_mockApplication
  326. openURL:_url
  327. sourceApplication:@"sourceApplication"
  328. annotation:@"annotaton"]);
  329. }
  330. }
  331. /** @fn testLegacyDelegateTwoHandlers
  332. @brief Tests that the proxy works against a legacy @c UIApplicationDelegate for two handlers.
  333. */
  334. - (void)testLegacyDelegateTwoHandlers {
  335. FIRAuthLegacyAppDelegate *delegate = [[FIRAuthLegacyAppDelegate alloc] init];
  336. OCMExpect([_mockApplication delegate]).andReturn(delegate);
  337. __weak id weakProxy;
  338. @autoreleasepool {
  339. FIRAuthAppDelegateProxy *proxy =
  340. [[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication];
  341. XCTAssertNotNil(proxy);
  342. // Verify certain methods are swizzled while others are not.
  343. XCTAssertTrue([delegate respondsToSelector:
  344. @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:)]);
  345. XCTAssertTrue([delegate respondsToSelector:
  346. @selector(application:didFailToRegisterForRemoteNotificationsWithError:)]);
  347. XCTAssertFalse([delegate respondsToSelector:
  348. @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)]);
  349. XCTAssertTrue([delegate respondsToSelector:
  350. @selector(application:didReceiveRemoteNotification:)]);
  351. XCTAssertFalse([delegate respondsToSelector:@selector(application:openURL:options:)]);
  352. XCTAssertTrue([delegate respondsToSelector:
  353. @selector(application:openURL:sourceApplication:annotation:)]);
  354. XCTAssertFalse([delegate respondsToSelector:@selector(application:handleOpenURL:)]);
  355. // Verify the handler is called after being added.
  356. __weak id weakHandler1;
  357. @autoreleasepool {
  358. id mockHandler1 = OCMProtocolMock(@protocol(FIRAuthAppDelegateHandler));
  359. [proxy addHandler:mockHandler1];
  360. __weak id weakHandler2;
  361. @autoreleasepool {
  362. id mockHandler2 = OCMProtocolMock(@protocol(FIRAuthAppDelegateHandler));
  363. [proxy addHandler:mockHandler2];
  364. // Verify `application:didRegisterForRemoteNotificationsWithDeviceToken:` is handled.
  365. OCMExpect([mockHandler1 setAPNSToken:_deviceToken]);
  366. OCMExpect([mockHandler2 setAPNSToken:_deviceToken]);
  367. [delegate application:_mockApplication
  368. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  369. OCMVerifyAll(mockHandler1);
  370. OCMVerifyAll(mockHandler2);
  371. // Verify `application:didFailToRegisterForRemoteNotificationsWithError:` is handled.
  372. OCMExpect([mockHandler1 handleAPNSTokenError:_error]);
  373. OCMExpect([mockHandler2 handleAPNSTokenError:_error]);
  374. [delegate application:_mockApplication
  375. didFailToRegisterForRemoteNotificationsWithError:_error];
  376. OCMVerifyAll(mockHandler1);
  377. OCMVerifyAll(mockHandler2);
  378. // Verify `application:didReceiveRemoteNotification:fetchCompletionHandler:` is handled.
  379. OCMExpect([mockHandler1 canHandleNotification:_notification]).andReturn(YES);
  380. // handler2 shouldn't been invoked because it is already handled by handler1.
  381. [delegate application:_mockApplication didReceiveRemoteNotification:_notification];
  382. OCMVerifyAll(mockHandler1);
  383. OCMVerifyAll(mockHandler2);
  384. XCTAssertNil(delegate.notificationReceived);
  385. // Verify `application:openURL:sourceApplication:annotation:` is handled.
  386. OCMExpect([mockHandler1 canHandleURL:_url]).andReturn(YES);
  387. XCTAssertTrue([delegate application:_mockApplication
  388. openURL:_url
  389. sourceApplication:@"sourceApplication"
  390. annotation:@"annotaton"]);
  391. OCMVerifyAll(mockHandler1);
  392. OCMVerifyAll(mockHandler2);
  393. XCTAssertNil(delegate.urlOpened);
  394. weakHandler2 = mockHandler2;
  395. XCTAssertNotNil(weakHandler2);
  396. }
  397. // Verify the handler2 is not retained by the proxy.
  398. XCTAssertNil(weakHandler2);
  399. // Verify `application:didRegisterForRemoteNotificationsWithDeviceToken:` is handled.
  400. OCMExpect([mockHandler1 setAPNSToken:_deviceToken]);
  401. [delegate application:_mockApplication
  402. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  403. OCMVerifyAll(mockHandler1);
  404. // Verify `application:didFailToRegisterForRemoteNotificationsWithError:` is handled.
  405. OCMExpect([mockHandler1 handleAPNSTokenError:_error]);
  406. [delegate application:_mockApplication
  407. didFailToRegisterForRemoteNotificationsWithError:_error];
  408. OCMVerifyAll(mockHandler1);
  409. // Verify `application:didReceiveRemoteNotification:fetchCompletionHandler:` is NOT handled.
  410. OCMExpect([mockHandler1 canHandleNotification:_notification]).andReturn(NO);
  411. [delegate application:_mockApplication didReceiveRemoteNotification:_notification];
  412. OCMVerifyAll(mockHandler1);
  413. XCTAssertEqualObjects(delegate.notificationReceived, _notification);
  414. delegate.notificationReceived = nil;
  415. // Verify `application:openURL:sourceApplication:annotation:` is NOT handled.
  416. OCMExpect([mockHandler1 canHandleURL:_url]).andReturn(NO);
  417. XCTAssertFalse([delegate application:_mockApplication
  418. openURL:_url
  419. sourceApplication:@"sourceApplication"
  420. annotation:@"annotation"]);
  421. OCMVerifyAll(mockHandler1);
  422. XCTAssertEqualObjects(delegate.urlOpened, _url);
  423. delegate.urlOpened = nil;
  424. weakHandler1 = mockHandler1;
  425. XCTAssertNotNil(weakHandler1);
  426. }
  427. // Verify the handler1 is not retained by the proxy.
  428. XCTAssertNil(weakHandler1);
  429. // Verify the delegate still works after all handlers are released.
  430. [delegate application:_mockApplication
  431. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  432. [delegate application:_mockApplication
  433. didFailToRegisterForRemoteNotificationsWithError:_error];
  434. [delegate application:_mockApplication didReceiveRemoteNotification:_notification];
  435. XCTAssertEqualObjects(delegate.notificationReceived, _notification);
  436. delegate.notificationReceived = nil;
  437. XCTAssertFalse([delegate application:_mockApplication
  438. openURL:_url
  439. sourceApplication:@"sourceApplication"
  440. annotation:@"annotation"]);
  441. XCTAssertEqualObjects(delegate.urlOpened, _url);
  442. delegate.urlOpened = nil;
  443. weakProxy = proxy;
  444. XCTAssertNotNil(weakProxy);
  445. }
  446. // Verify the proxy does not retain itself.
  447. XCTAssertNil(weakProxy);
  448. // Verify the delegate still works after the proxy is released.
  449. [delegate application:_mockApplication
  450. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  451. [delegate application:_mockApplication didReceiveRemoteNotification:_notification];
  452. XCTAssertEqualObjects(delegate.notificationReceived, _notification);
  453. delegate.notificationReceived = nil;
  454. XCTAssertFalse([delegate application:_mockApplication
  455. openURL:_url
  456. sourceApplication:@"sourceApplication"
  457. annotation:@"annotation"]);
  458. XCTAssertEqualObjects(delegate.urlOpened, _url);
  459. delegate.urlOpened = nil;
  460. }
  461. /** @fn testModernDelegateWithOtherInstance
  462. @brief Tests that the proxy works against a modern @c UIApplicationDelegate along with
  463. another unaffected instance.
  464. */
  465. - (void)testModernDelegateWithUnaffectedInstance {
  466. FIRAuthModernAppDelegate *delegate = [[FIRAuthModernAppDelegate alloc] init];
  467. OCMExpect([_mockApplication delegate]).andReturn(delegate);
  468. FIRAuthModernAppDelegate *unaffectedDelegate = [[FIRAuthModernAppDelegate alloc] init];
  469. __weak id weakProxy;
  470. @autoreleasepool {
  471. FIRAuthAppDelegateProxy *proxy =
  472. [[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication];
  473. XCTAssertNotNil(proxy);
  474. // Verify certain methods are swizzled while others are not.
  475. XCTAssertTrue([delegate respondsToSelector:
  476. @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:)]);
  477. XCTAssertTrue([delegate respondsToSelector:
  478. @selector(application:didFailToRegisterForRemoteNotificationsWithError:)]);
  479. XCTAssertTrue([delegate respondsToSelector:
  480. @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)]);
  481. XCTAssertFalse([delegate respondsToSelector:
  482. @selector(application:didReceiveRemoteNotification:)]);
  483. XCTAssertTrue([delegate respondsToSelector:@selector(application:openURL:options:)]);
  484. if (_isIOS9orLater) {
  485. XCTAssertFalse([delegate respondsToSelector:
  486. @selector(application:openURL:sourceApplication:annotation:)]);
  487. } else {
  488. XCTAssertTrue([delegate respondsToSelector:
  489. @selector(application:openURL:sourceApplication:annotation:)]);
  490. }
  491. XCTAssertFalse([delegate respondsToSelector:@selector(application:handleOpenURL:)]);
  492. // Verify the handler is called after being added.
  493. __weak id weakHandler;
  494. @autoreleasepool {
  495. id mockHandler = OCMProtocolMock(@protocol(FIRAuthAppDelegateHandler));
  496. [proxy addHandler:mockHandler];
  497. // Verify `application:didRegisterForRemoteNotificationsWithDeviceToken:` is handled.
  498. OCMExpect([mockHandler setAPNSToken:_deviceToken]);
  499. [delegate application:_mockApplication
  500. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  501. OCMVerifyAll(mockHandler);
  502. XCTAssertEqualObjects(delegate.deviceTokenReceived, _deviceToken);
  503. delegate.deviceTokenReceived = nil;
  504. // Verify `application:didFailToRegisterForRemoteNotificationsWithError:` is handled.
  505. OCMExpect([mockHandler handleAPNSTokenError:_error]);
  506. [delegate application:_mockApplication
  507. didFailToRegisterForRemoteNotificationsWithError:_error];
  508. OCMVerifyAll(mockHandler);
  509. XCTAssertEqualObjects(delegate.tokenErrorReceived, _error);
  510. delegate.tokenErrorReceived = nil;
  511. // Verify `application:didReceiveRemoteNotification:fetchCompletionHandler:` is handled.
  512. OCMExpect([mockHandler canHandleNotification:_notification]).andReturn(YES);
  513. __block BOOL fetchCompletionHandlerCalled = NO;
  514. [delegate application:_mockApplication
  515. didReceiveRemoteNotification:_notification
  516. fetchCompletionHandler:^(UIBackgroundFetchResult result) {
  517. XCTAssertEqual(result, UIBackgroundFetchResultNoData);
  518. fetchCompletionHandlerCalled = YES;
  519. }];
  520. OCMVerifyAll(mockHandler);
  521. XCTAssertTrue(fetchCompletionHandlerCalled);
  522. XCTAssertNil(delegate.notificationReceived);
  523. // Verify one of the `application:openURL:...` methods is handled.
  524. OCMExpect([mockHandler canHandleURL:_url]).andReturn(YES);
  525. if (_isIOS9orLater) {
  526. // Verify `application:openURL:options:` is handled.
  527. XCTAssertTrue([delegate application:_mockApplication openURL:_url options:@{}]);
  528. } else {
  529. // Verify `application:openURL:sourceApplication:annotation:` is handled.
  530. XCTAssertTrue([delegate application:_mockApplication
  531. openURL:_url
  532. sourceApplication:@"sourceApplication"
  533. annotation:@"annotaton"]);
  534. }
  535. OCMVerifyAll(mockHandler);
  536. XCTAssertNil(delegate.urlOpened);
  537. // Verify unaffected delegate instance.
  538. [unaffectedDelegate application:_mockApplication
  539. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  540. XCTAssertEqualObjects(unaffectedDelegate.deviceTokenReceived, _deviceToken);
  541. unaffectedDelegate.deviceTokenReceived = nil;
  542. [unaffectedDelegate application:_mockApplication
  543. didFailToRegisterForRemoteNotificationsWithError:_error];
  544. XCTAssertEqualObjects(unaffectedDelegate.tokenErrorReceived, _error);
  545. unaffectedDelegate.tokenErrorReceived = nil;
  546. fetchCompletionHandlerCalled = NO;
  547. [unaffectedDelegate application:_mockApplication
  548. didReceiveRemoteNotification:_notification
  549. fetchCompletionHandler:^(UIBackgroundFetchResult result) {
  550. XCTAssertEqual(result, UIBackgroundFetchResultNewData);
  551. fetchCompletionHandlerCalled = YES;
  552. }];
  553. XCTAssertTrue(fetchCompletionHandlerCalled);
  554. XCTAssertEqualObjects(unaffectedDelegate.notificationReceived, _notification);
  555. unaffectedDelegate.notificationReceived = nil;
  556. XCTAssertFalse([unaffectedDelegate application:_mockApplication openURL:_url options:@{}]);
  557. XCTAssertEqualObjects(unaffectedDelegate.urlOpened, _url);
  558. unaffectedDelegate.urlOpened = nil;
  559. weakHandler = mockHandler;
  560. XCTAssertNotNil(weakHandler);
  561. }
  562. // Verify the handler is not retained by the proxy.
  563. XCTAssertNil(weakHandler);
  564. // Verify the delegate still works after the handler is released.
  565. [delegate application:_mockApplication
  566. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  567. XCTAssertEqualObjects(delegate.deviceTokenReceived, _deviceToken);
  568. delegate.deviceTokenReceived = nil;
  569. [delegate application:_mockApplication
  570. didFailToRegisterForRemoteNotificationsWithError:_error];
  571. XCTAssertEqualObjects(delegate.tokenErrorReceived, _error);
  572. delegate.tokenErrorReceived = nil;
  573. __block BOOL fetchCompletionHandlerCalled = NO;
  574. [delegate application:_mockApplication
  575. didReceiveRemoteNotification:_notification
  576. fetchCompletionHandler:^(UIBackgroundFetchResult result) {
  577. XCTAssertEqual(result, UIBackgroundFetchResultNewData);
  578. fetchCompletionHandlerCalled = YES;
  579. }];
  580. XCTAssertEqualObjects(delegate.notificationReceived, _notification);
  581. delegate.notificationReceived = nil;
  582. XCTAssertTrue(fetchCompletionHandlerCalled);
  583. XCTAssertFalse([delegate application:_mockApplication openURL:_url options:@{}]);
  584. XCTAssertEqualObjects(delegate.urlOpened, _url);
  585. delegate.urlOpened = nil;
  586. weakProxy = proxy;
  587. XCTAssertNotNil(weakProxy);
  588. }
  589. // Verify the proxy does not retain itself.
  590. XCTAssertNil(weakProxy);
  591. // Verify the delegate still works after the proxy is released.
  592. [delegate application:_mockApplication
  593. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  594. XCTAssertEqualObjects(delegate.deviceTokenReceived, _deviceToken);
  595. delegate.deviceTokenReceived = nil;
  596. [delegate application:_mockApplication
  597. didFailToRegisterForRemoteNotificationsWithError:_error];
  598. XCTAssertEqualObjects(delegate.tokenErrorReceived, _error);
  599. delegate.tokenErrorReceived = nil;
  600. __block BOOL fetchCompletionHandlerCalled = NO;
  601. [delegate application:_mockApplication
  602. didReceiveRemoteNotification:_notification
  603. fetchCompletionHandler:^(UIBackgroundFetchResult result) {
  604. XCTAssertEqual(result, UIBackgroundFetchResultNewData);
  605. fetchCompletionHandlerCalled = YES;
  606. }];
  607. XCTAssertEqualObjects(delegate.notificationReceived, _notification);
  608. delegate.notificationReceived = nil;
  609. XCTAssertTrue(fetchCompletionHandlerCalled);
  610. XCTAssertFalse([delegate application:_mockApplication openURL:_url options:@{}]);
  611. XCTAssertEqualObjects(delegate.urlOpened, _url);
  612. delegate.urlOpened = nil;
  613. }
  614. /** @fn testOtherLegacyDelegateHandleOpenURL
  615. @brief Tests that the proxy works against another legacy @c UIApplicationDelegate for
  616. `application:handleOpenURL:`.
  617. */
  618. - (void)testOtherLegacyDelegateHandleOpenURL {
  619. FIRAuthOtherLegacyAppDelegate *delegate = [[FIRAuthOtherLegacyAppDelegate alloc] init];
  620. OCMExpect([_mockApplication delegate]).andReturn(delegate);
  621. __weak id weakProxy;
  622. @autoreleasepool {
  623. FIRAuthAppDelegateProxy *proxy =
  624. [[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication];
  625. XCTAssertNotNil(proxy);
  626. // Verify certain methods are swizzled while others are not.
  627. XCTAssertFalse([delegate respondsToSelector:@selector(application:openURL:options:)]);
  628. XCTAssertFalse([delegate respondsToSelector:
  629. @selector(application:openURL:sourceApplication:annotation:)]);
  630. XCTAssertTrue([delegate respondsToSelector:@selector(application:handleOpenURL:)]);
  631. // Verify the handler is called after being added.
  632. __weak id weakHandler;
  633. @autoreleasepool {
  634. id mockHandler = OCMProtocolMock(@protocol(FIRAuthAppDelegateHandler));
  635. [proxy addHandler:mockHandler];
  636. // Verify `application:handleOpenURL:` is handled.
  637. OCMExpect([mockHandler canHandleURL:_url]).andReturn(YES);
  638. XCTAssertTrue([delegate application:_mockApplication handleOpenURL:_url]);
  639. OCMVerifyAll(mockHandler);
  640. weakHandler = mockHandler;
  641. XCTAssertNotNil(weakHandler);
  642. }
  643. // Verify the handler is not retained by the proxy.
  644. XCTAssertNil(weakHandler);
  645. // Verify nothing bad happens after the handler is released.
  646. XCTAssertFalse([delegate application:_mockApplication handleOpenURL:_url]);
  647. XCTAssertEqualObjects(delegate.urlOpened, _url);
  648. delegate.urlOpened = nil;
  649. weakProxy = proxy;
  650. XCTAssertNotNil(weakProxy);
  651. }
  652. // Verify the proxy does not retain itself.
  653. XCTAssertNil(weakProxy);
  654. // Verify nothing bad happens after the proxy is released.
  655. XCTAssertFalse([delegate application:_mockApplication handleOpenURL:_url]);
  656. XCTAssertEqualObjects(delegate.urlOpened, _url);
  657. delegate.urlOpened = nil;
  658. }
  659. #pragma clang diagnostic pop // ignored "-Wdeprecated-declarations"
  660. @end
  661. NS_ASSUME_NONNULL_END