FIRAuthAppDelegateProxyTests.m 27 KB

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