FIRAuthAppDelegateProxyTests.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  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:`.
  31. */
  32. @interface FIRAuthLegacyAppDelegate : NSObject <UIApplicationDelegate>
  33. /** @var notificationReceived
  34. @brief The last notification received, if any.
  35. */
  36. @property(nonatomic, copy, nullable) NSDictionary *notificationReceived;
  37. @end
  38. @implementation FIRAuthLegacyAppDelegate
  39. - (void)application:(UIApplication *)application
  40. didReceiveRemoteNotification:(NSDictionary *)userInfo {
  41. self.notificationReceived = userInfo;
  42. }
  43. @end
  44. /** @class FIRAuthModernAppDelegate
  45. @brief A @c UIApplicationDelegate implementation that implements both
  46. `application:didRegisterForRemoteNotificationsWithDeviceToken:` and
  47. `application:didReceiveRemoteNotification:fetchCompletionHandler:`.
  48. */
  49. @interface FIRAuthModernAppDelegate : NSObject <UIApplicationDelegate>
  50. /** @var deviceTokenReceived
  51. @brief The last device token received, if any.
  52. */
  53. @property(nonatomic, copy, nullable) NSData *deviceTokenReceived;
  54. /** @var notificationReceived
  55. @brief The last notification received, if any.
  56. */
  57. @property(nonatomic, copy, nullable) NSDictionary *notificationReceived;
  58. @end
  59. @implementation FIRAuthModernAppDelegate
  60. - (void)application:(UIApplication *)application
  61. didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
  62. self.deviceTokenReceived = deviceToken;
  63. }
  64. - (void)application:(UIApplication *)application
  65. didReceiveRemoteNotification:(NSDictionary *)userInfo
  66. fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  67. self.notificationReceived = userInfo;
  68. completionHandler(UIBackgroundFetchResultNewData);
  69. }
  70. @end
  71. /** @class FIRAuthAppDelegateProxyTests
  72. @brief Unit tests for @c FIRAuthAppDelegateProxy .
  73. */
  74. @interface FIRAuthAppDelegateProxyTests : XCTestCase
  75. @end
  76. @implementation FIRAuthAppDelegateProxyTests {
  77. /** @var _mockApplication
  78. @brief The mock UIApplication used for testing.
  79. */
  80. id _mockApplication;
  81. /** @var _deviceToken
  82. @brief The fake APNs device token for testing.
  83. */
  84. NSData *_deviceToken;
  85. /** @var _notification
  86. @brief The fake notification for testing.
  87. */
  88. NSDictionary* _notification;
  89. }
  90. - (void)setUp {
  91. [super setUp];
  92. _mockApplication = OCMClassMock([UIApplication class]);
  93. _deviceToken = [@"asdf" dataUsingEncoding:NSUTF8StringEncoding];
  94. _notification = @{ @"zxcv" : @1234 };
  95. }
  96. - (void)tearDown {
  97. OCMVerifyAll(_mockApplication);
  98. [super tearDown];
  99. }
  100. /** @fn testSharedInstance
  101. @brief Tests that the shared instance is the same one.
  102. */
  103. - (void)testSharedInstance {
  104. FIRAuthAppDelegateProxy *proxy1 = [FIRAuthAppDelegateProxy sharedInstance];
  105. FIRAuthAppDelegateProxy *proxy2 = [FIRAuthAppDelegateProxy sharedInstance];
  106. XCTAssertEqual(proxy1, proxy2);
  107. }
  108. /** @fn testNilApplication
  109. @brief Tests that initialization fails if the application is nil.
  110. */
  111. - (void)testNilApplication {
  112. XCTAssertNil([[FIRAuthAppDelegateProxy alloc] initWithApplication:nil]);
  113. }
  114. /** @fn testNilDelegate
  115. @brief Tests that initialization fails if the application's delegate is nil.
  116. */
  117. - (void)testNilDelegate {
  118. OCMExpect([_mockApplication delegate]).andReturn(nil);
  119. XCTAssertNil([[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication]);
  120. }
  121. /** @fn testNonconformingDelegate
  122. @brief Tests that initialization fails if the application's delegate does not conform to
  123. @c UIApplicationDelegate protocol.
  124. */
  125. - (void)testNonconformingDelegate {
  126. OCMExpect([_mockApplication delegate]).andReturn(@"abc");
  127. XCTAssertNil([[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication]);
  128. }
  129. /** @fn testDisabledByBundleEntry
  130. @brief Tests that initialization fails if the proxy is disabled by a bundle entry.
  131. */
  132. - (void)testDisabledByBundleEntry {
  133. // Swizzle NSBundle's objectForInfoDictionaryKey to return @NO for the specific key.
  134. Method method = class_getInstanceMethod([NSBundle class], @selector(objectForInfoDictionaryKey:));
  135. __block IMP originalImplementation;
  136. IMP newImplmentation = imp_implementationWithBlock(^id(id object, NSString *key) {
  137. if ([key isEqualToString:@"FirebaseAppDelegateProxyEnabled"]) {
  138. return @NO;
  139. }
  140. typedef id (*Implementation)(id object, SEL cmd, NSString *key);
  141. return ((Implementation)originalImplementation)(object, @selector(objectForInfoDictionaryKey:),
  142. key);
  143. });
  144. originalImplementation = method_setImplementation(method, newImplmentation);
  145. // Verify that initialization fails.
  146. FIRAuthEmptyAppDelegate *delegate = [[FIRAuthEmptyAppDelegate alloc] init];
  147. OCMStub([_mockApplication delegate]).andReturn(delegate);
  148. XCTAssertNil([[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication]);
  149. // Unswizzle.
  150. imp_removeBlock(method_setImplementation(method, originalImplementation));
  151. }
  152. /** @fn testEmptyDelegateOneHandler
  153. @brief Tests that the proxy works against an empty @c UIApplicationDelegate for one handler.
  154. */
  155. - (void)testEmptyDelegateOneHandler {
  156. FIRAuthEmptyAppDelegate *delegate = [[FIRAuthEmptyAppDelegate alloc] init];
  157. OCMExpect([_mockApplication delegate]).andReturn(delegate);
  158. __weak id weakProxy;
  159. @autoreleasepool {
  160. FIRAuthAppDelegateProxy *proxy =
  161. [[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication];
  162. XCTAssertNotNil(proxy);
  163. // Verify `application:didReceiveRemoteNotification:` is not swizzled.
  164. XCTAssertFalse([delegate respondsToSelector:
  165. @selector(application:didReceiveRemoteNotification:)]);
  166. // Verify the handler is called after being added.
  167. __weak id weakHandler;
  168. @autoreleasepool {
  169. id mockHandler = OCMProtocolMock(@protocol(FIRAuthAppDelegateHandler));
  170. [proxy addHandler:mockHandler];
  171. // Verify handling of `application:didRegisterForRemoteNotificationsWithDeviceToken:`.
  172. OCMExpect([mockHandler setAPNSToken:_deviceToken]);
  173. [delegate application:_mockApplication
  174. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  175. OCMVerifyAll(mockHandler);
  176. // Verify handling of `application:didReceiveRemoteNotification:fetchCompletionHandler:`.
  177. OCMExpect([mockHandler canHandleNotification:_notification]).andReturn(YES);
  178. __block BOOL fetchCompletionHandlerCalled = NO;
  179. [delegate application:_mockApplication
  180. didReceiveRemoteNotification:_notification
  181. fetchCompletionHandler:^(UIBackgroundFetchResult result) {
  182. XCTAssertEqual(result, UIBackgroundFetchResultNoData);
  183. fetchCompletionHandlerCalled = YES;
  184. }];
  185. OCMVerifyAll(mockHandler);
  186. XCTAssertTrue(fetchCompletionHandlerCalled);
  187. weakHandler = mockHandler;
  188. XCTAssertNotNil(weakHandler);
  189. }
  190. // Verify the handler is not retained by the proxy.
  191. XCTAssertNil(weakHandler);
  192. // Verify nothing bad happens after the handler is released.
  193. [delegate application:_mockApplication
  194. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  195. [delegate application:_mockApplication
  196. didReceiveRemoteNotification:_notification
  197. fetchCompletionHandler:^(UIBackgroundFetchResult result) {
  198. XCTFail(@"Should not call completion handler.");
  199. }];
  200. weakProxy = proxy;
  201. XCTAssertNotNil(weakProxy);
  202. }
  203. // Verify the proxy does not retain itself.
  204. XCTAssertNil(weakProxy);
  205. // Verify nothing bad happens after the proxy is released.
  206. [delegate application:_mockApplication
  207. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  208. [delegate application:_mockApplication
  209. didReceiveRemoteNotification:_notification
  210. fetchCompletionHandler:^(UIBackgroundFetchResult result) {
  211. XCTFail(@"Should not call completion handler.");
  212. }];
  213. }
  214. /** @fn testLegacyDelegateTwoHandlers
  215. @brief Tests that the proxy works against a legacy @c UIApplicationDelegate for two handlers.
  216. */
  217. - (void)testLegacyDelegateTwoHandlers {
  218. FIRAuthLegacyAppDelegate *delegate = [[FIRAuthLegacyAppDelegate alloc] init];
  219. OCMExpect([_mockApplication delegate]).andReturn(delegate);
  220. __weak id weakProxy;
  221. @autoreleasepool {
  222. FIRAuthAppDelegateProxy *proxy =
  223. [[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication];
  224. XCTAssertNotNil(proxy);
  225. // Verify `application:didReceiveRemoteNotification:fetchCompletionHandler` is not swizzled.
  226. XCTAssertFalse([delegate respondsToSelector:
  227. @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)]);
  228. // Verify the handler is called after being added.
  229. __weak id weakHandler1;
  230. @autoreleasepool {
  231. id mockHandler1 = OCMProtocolMock(@protocol(FIRAuthAppDelegateHandler));
  232. [proxy addHandler:mockHandler1];
  233. __weak id weakHandler2;
  234. @autoreleasepool {
  235. id mockHandler2 = OCMProtocolMock(@protocol(FIRAuthAppDelegateHandler));
  236. [proxy addHandler:mockHandler2];
  237. // Verify handling of `application:didRegisterForRemoteNotificationsWithDeviceToken:`.
  238. OCMExpect([mockHandler1 setAPNSToken:_deviceToken]);
  239. OCMExpect([mockHandler2 setAPNSToken:_deviceToken]);
  240. [delegate application:_mockApplication
  241. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  242. OCMVerifyAll(mockHandler1);
  243. OCMVerifyAll(mockHandler2);
  244. // Verify handling of `application:didReceiveRemoteNotification:fetchCompletionHandler:`.
  245. OCMExpect([mockHandler1 canHandleNotification:_notification]).andReturn(YES);
  246. // handler2 shouldn't been invoked because it is already handled by handler1.
  247. [delegate application:_mockApplication didReceiveRemoteNotification:_notification];
  248. OCMVerifyAll(mockHandler1);
  249. OCMVerifyAll(mockHandler2);
  250. XCTAssertNil(delegate.notificationReceived);
  251. weakHandler2 = mockHandler2;
  252. XCTAssertNotNil(weakHandler2);
  253. }
  254. // Verify the handler2 is not retained by the proxy.
  255. XCTAssertNil(weakHandler2);
  256. // Verify handling of `application:didRegisterForRemoteNotificationsWithDeviceToken:`.
  257. OCMExpect([mockHandler1 setAPNSToken:_deviceToken]);
  258. [delegate application:_mockApplication
  259. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  260. OCMVerifyAll(mockHandler1);
  261. // Verify NOT handling of `application:didReceiveRemoteNotification:fetchCompletionHandler:`.
  262. OCMExpect([mockHandler1 canHandleNotification:_notification]).andReturn(NO);
  263. [delegate application:_mockApplication didReceiveRemoteNotification:_notification];
  264. OCMVerifyAll(mockHandler1);
  265. XCTAssertEqualObjects(delegate.notificationReceived, _notification);
  266. delegate.notificationReceived = nil;
  267. weakHandler1 = mockHandler1;
  268. XCTAssertNotNil(weakHandler1);
  269. }
  270. // Verify the handler1 is not retained by the proxy.
  271. XCTAssertNil(weakHandler1);
  272. // Verify the delegate still works after all handlers are released.
  273. [delegate application:_mockApplication
  274. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  275. [delegate application:_mockApplication didReceiveRemoteNotification:_notification];
  276. XCTAssertEqualObjects(delegate.notificationReceived, _notification);
  277. delegate.notificationReceived = nil;
  278. weakProxy = proxy;
  279. XCTAssertNotNil(weakProxy);
  280. }
  281. // Verify the proxy does not retain itself.
  282. XCTAssertNil(weakProxy);
  283. // Verify the delegate still works after the proxy is released.
  284. [delegate application:_mockApplication
  285. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  286. [delegate application:_mockApplication didReceiveRemoteNotification:_notification];
  287. XCTAssertEqualObjects(delegate.notificationReceived, _notification);
  288. delegate.notificationReceived = nil;
  289. }
  290. /** @fn testModernDelegateWithOtherInstance
  291. @brief Tests that the proxy works against a modern @c UIApplicationDelegate along with
  292. another unaffected instance.
  293. */
  294. - (void)testModernDelegateWithUnaffectedInstance {
  295. FIRAuthModernAppDelegate *delegate = [[FIRAuthModernAppDelegate alloc] init];
  296. OCMExpect([_mockApplication delegate]).andReturn(delegate);
  297. FIRAuthModernAppDelegate *unaffectedDelegate = [[FIRAuthModernAppDelegate alloc] init];
  298. __weak id weakProxy;
  299. @autoreleasepool {
  300. FIRAuthAppDelegateProxy *proxy =
  301. [[FIRAuthAppDelegateProxy alloc] initWithApplication:_mockApplication];
  302. XCTAssertNotNil(proxy);
  303. // Verify `application:didReceiveRemoteNotification:` is not swizzled.
  304. XCTAssertFalse([delegate respondsToSelector:
  305. @selector(application:didReceiveRemoteNotification:)]);
  306. // Verify the handler is called after being added.
  307. __weak id weakHandler;
  308. @autoreleasepool {
  309. id mockHandler = OCMProtocolMock(@protocol(FIRAuthAppDelegateHandler));
  310. [proxy addHandler:mockHandler];
  311. // Verify handling of `application:didRegisterForRemoteNotificationsWithDeviceToken:`.
  312. OCMExpect([mockHandler setAPNSToken:_deviceToken]);
  313. [delegate application:_mockApplication
  314. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  315. OCMVerifyAll(mockHandler);
  316. XCTAssertEqualObjects(delegate.deviceTokenReceived, _deviceToken);
  317. delegate.deviceTokenReceived = nil;
  318. // Verify handling of `application:didReceiveRemoteNotification:fetchCompletionHandler:`.
  319. OCMExpect([mockHandler canHandleNotification:_notification]).andReturn(YES);
  320. __block BOOL fetchCompletionHandlerCalled = NO;
  321. [delegate application:_mockApplication
  322. didReceiveRemoteNotification:_notification
  323. fetchCompletionHandler:^(UIBackgroundFetchResult result) {
  324. XCTAssertEqual(result, UIBackgroundFetchResultNoData);
  325. fetchCompletionHandlerCalled = YES;
  326. }];
  327. OCMVerifyAll(mockHandler);
  328. XCTAssertTrue(fetchCompletionHandlerCalled);
  329. XCTAssertNil(delegate.notificationReceived);
  330. // Verify unaffected delegate instance.
  331. [unaffectedDelegate application:_mockApplication
  332. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  333. XCTAssertEqualObjects(unaffectedDelegate.deviceTokenReceived, _deviceToken);
  334. unaffectedDelegate.deviceTokenReceived = nil;
  335. fetchCompletionHandlerCalled = NO;
  336. [unaffectedDelegate application:_mockApplication
  337. didReceiveRemoteNotification:_notification
  338. fetchCompletionHandler:^(UIBackgroundFetchResult result) {
  339. XCTAssertEqual(result, UIBackgroundFetchResultNewData);
  340. fetchCompletionHandlerCalled = YES;
  341. }];
  342. XCTAssertTrue(fetchCompletionHandlerCalled);
  343. XCTAssertEqualObjects(unaffectedDelegate.notificationReceived, _notification);
  344. unaffectedDelegate.notificationReceived = nil;
  345. weakHandler = mockHandler;
  346. XCTAssertNotNil(weakHandler);
  347. }
  348. // Verify the handler is not retained by the proxy.
  349. XCTAssertNil(weakHandler);
  350. // Verify the delegate still works after the handler is released.
  351. [delegate application:_mockApplication
  352. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  353. XCTAssertEqualObjects(delegate.deviceTokenReceived, _deviceToken);
  354. delegate.deviceTokenReceived = nil;
  355. __block BOOL fetchCompletionHandlerCalled = NO;
  356. [delegate application:_mockApplication
  357. didReceiveRemoteNotification:_notification
  358. fetchCompletionHandler:^(UIBackgroundFetchResult result) {
  359. XCTAssertEqual(result, UIBackgroundFetchResultNewData);
  360. fetchCompletionHandlerCalled = YES;
  361. }];
  362. XCTAssertEqualObjects(delegate.notificationReceived, _notification);
  363. delegate.notificationReceived = nil;
  364. XCTAssertTrue(fetchCompletionHandlerCalled);
  365. weakProxy = proxy;
  366. XCTAssertNotNil(weakProxy);
  367. }
  368. // Verify the proxy does not retain itself.
  369. XCTAssertNil(weakProxy);
  370. // Verify the delegate still works after the proxy is released.
  371. [delegate application:_mockApplication
  372. didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken];
  373. XCTAssertEqualObjects(delegate.deviceTokenReceived, _deviceToken);
  374. delegate.deviceTokenReceived = nil;
  375. __block BOOL fetchCompletionHandlerCalled = NO;
  376. [delegate application:_mockApplication
  377. didReceiveRemoteNotification:_notification
  378. fetchCompletionHandler:^(UIBackgroundFetchResult result) {
  379. XCTAssertEqual(result, UIBackgroundFetchResultNewData);
  380. fetchCompletionHandlerCalled = YES;
  381. }];
  382. XCTAssertEqualObjects(delegate.notificationReceived, _notification);
  383. delegate.notificationReceived = nil;
  384. XCTAssertTrue(fetchCompletionHandlerCalled);
  385. }
  386. @end
  387. NS_ASSUME_NONNULL_END