FIRMessagingRemoteNotificationsProxyTest.m 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  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. #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 || \
  17. __TV_OS_VERSION_MAX_ALLOWED >= __TV_10_0 || __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_14
  18. #import <UserNotifications/UserNotifications.h>
  19. #endif
  20. #import <OCMock/OCMock.h>
  21. #import <XCTest/XCTest.h>
  22. #import <FirebaseMessaging/FIRMessaging.h>
  23. #import "GoogleUtilities/AppDelegateSwizzler/Private/GULAppDelegateSwizzler.h"
  24. #import "FirebaseMessaging/Sources/FIRMessagingRemoteNotificationsProxy.h"
  25. #pragma mark - Invalid App Delegate or UNNotificationCenter
  26. @interface RandomObject : NSObject
  27. @property(nonatomic, weak) id delegate;
  28. @end
  29. @implementation RandomObject
  30. - (void)application:(GULApplication *)application
  31. didReceiveRemoteNotification:(NSDictionary *)userInfo {
  32. }
  33. - (void)userNotificationCenter:(UNUserNotificationCenter *)center
  34. willPresentNotification:(UNNotification *)notification
  35. withCompletionHandler:
  36. (void (^)(UNNotificationPresentationOptions options))completionHandler
  37. API_AVAILABLE(ios(10.0), macos(10.14), tvos(10.0)) {
  38. }
  39. #if TARGET_OS_IOS || TARGET_OS_OSX
  40. - (void)userNotificationCenter:(UNUserNotificationCenter *)center
  41. didReceiveNotificationResponse:(UNNotificationResponse *)response
  42. withCompletionHandler:(void (^)(void))completionHandler
  43. API_AVAILABLE(macos(10.14), ios(10.0)) {
  44. }
  45. #endif
  46. @end
  47. #pragma mark - Incomplete App Delegate
  48. @interface IncompleteAppDelegate : NSObject <GULApplicationDelegate>
  49. @end
  50. @implementation IncompleteAppDelegate
  51. @end
  52. #pragma mark - Fake AppDelegate
  53. @interface FakeAppDelegate : NSObject <GULApplicationDelegate>
  54. @property(nonatomic) BOOL remoteNotificationMethodWasCalled;
  55. @property(nonatomic) BOOL remoteNotificationWithFetchHandlerWasCalled;
  56. @property(nonatomic, strong) NSData *deviceToken;
  57. @property(nonatomic, strong) NSError *registerForRemoteNotificationsError;
  58. @end
  59. @implementation FakeAppDelegate
  60. #pragma clang diagnostic push
  61. #pragma clang diagnostic ignored "-Wdeprecated-implementations"
  62. - (void)application:(GULApplication *)application
  63. didReceiveRemoteNotification:(NSDictionary *)userInfo {
  64. self.remoteNotificationMethodWasCalled = YES;
  65. }
  66. #pragma clang diagnostic pop
  67. #if TARGET_OS_IOS || TARGET_OS_TV
  68. - (void)application:(UIApplication *)application
  69. didReceiveRemoteNotification:(NSDictionary *)userInfo
  70. fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  71. self.remoteNotificationWithFetchHandlerWasCalled = YES;
  72. }
  73. #endif
  74. - (void)application:(GULApplication *)application
  75. didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
  76. self.deviceToken = deviceToken;
  77. }
  78. - (void)application:(GULApplication *)application
  79. didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
  80. self.registerForRemoteNotificationsError = error;
  81. }
  82. @end
  83. #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 || \
  84. __TV_OS_VERSION_MAX_ALLOWED >= __TV_10_0 || __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_14
  85. #pragma mark - Incompete UNUserNotificationCenterDelegate
  86. @interface IncompleteUserNotificationCenterDelegate : NSObject <UNUserNotificationCenterDelegate>
  87. @end
  88. @implementation IncompleteUserNotificationCenterDelegate
  89. @end
  90. #pragma mark - Fake UNUserNotificationCenterDelegate
  91. @interface FakeUserNotificationCenterDelegate : NSObject <UNUserNotificationCenterDelegate>
  92. @property(nonatomic) BOOL willPresentWasCalled;
  93. @property(nonatomic) BOOL didReceiveResponseWasCalled;
  94. @end
  95. @implementation FakeUserNotificationCenterDelegate
  96. - (void)userNotificationCenter:(UNUserNotificationCenter *)center
  97. willPresentNotification:(UNNotification *)notification
  98. withCompletionHandler:
  99. (void (^)(UNNotificationPresentationOptions options))completionHandler
  100. API_AVAILABLE(ios(10.0), macos(10.14), tvos(10.0)) {
  101. self.willPresentWasCalled = YES;
  102. }
  103. #if TARGET_OS_IOS || TARGET_OS_OSX
  104. - (void)userNotificationCenter:(UNUserNotificationCenter *)center
  105. didReceiveNotificationResponse:(UNNotificationResponse *)response
  106. withCompletionHandler:(void (^)(void))completionHandler
  107. API_AVAILABLE(ios(10.0), macos(10.14)) {
  108. self.didReceiveResponseWasCalled = YES;
  109. }
  110. #endif
  111. @end
  112. #endif
  113. @interface GULAppDelegateSwizzler (FIRMessagingRemoteNotificationsProxyTest)
  114. + (void)resetProxyOriginalDelegateOnceToken;
  115. @end
  116. #pragma mark - Local, Per-Test Properties
  117. @interface FIRMessagingRemoteNotificationsProxyTest : XCTestCase
  118. @property(nonatomic, strong) FIRMessagingRemoteNotificationsProxy *proxy;
  119. @property(nonatomic, strong) id mockProxyClass;
  120. @property(nonatomic, strong) id mockMessaging;
  121. @property(nonatomic, strong) id mockUserNotificationCenter;
  122. @end
  123. @implementation FIRMessagingRemoteNotificationsProxyTest
  124. - (void)setUp {
  125. [super setUp];
  126. [GULAppDelegateSwizzler resetProxyOriginalDelegateOnceToken];
  127. _mockMessaging = OCMClassMock([FIRMessaging class]);
  128. OCMStub([_mockMessaging messaging]).andReturn(_mockMessaging);
  129. _proxy = [[FIRMessagingRemoteNotificationsProxy alloc] init];
  130. _mockProxyClass = OCMClassMock([FIRMessagingRemoteNotificationsProxy class]);
  131. // Update +sharedProxy to always return our test instance
  132. OCMStub([_mockProxyClass sharedProxy]).andReturn(self.proxy);
  133. if (@available(macOS 10.14, iOS 10.0, *)) {
  134. _mockUserNotificationCenter =
  135. OCMPartialMock([UNUserNotificationCenter currentNotificationCenter]);
  136. }
  137. }
  138. - (void)tearDown {
  139. [_mockProxyClass stopMocking];
  140. _mockProxyClass = nil;
  141. [_mockMessaging stopMocking];
  142. _mockMessaging = nil;
  143. if (@available(macOS 10.14, iOS 10.0, *)) {
  144. [_mockUserNotificationCenter stopMocking];
  145. _mockUserNotificationCenter = nil;
  146. }
  147. _proxy = nil;
  148. [super tearDown];
  149. }
  150. #pragma mark - Method Swizzling Tests
  151. - (void)testSwizzlingNonAppDelegate {
  152. RandomObject *invalidAppDelegate = [[RandomObject alloc] init];
  153. [[GULAppDelegateSwizzler sharedApplication]
  154. setDelegate:(id<GULApplicationDelegate>)invalidAppDelegate];
  155. [self.proxy swizzleMethodsIfPossible];
  156. OCMReject([self.mockMessaging appDidReceiveMessage:[OCMArg any]]);
  157. [invalidAppDelegate application:[GULAppDelegateSwizzler sharedApplication]
  158. didReceiveRemoteNotification:@{}];
  159. }
  160. - (void)testSwizzledIncompleteAppDelegateRemoteNotificationMethod {
  161. IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc] init];
  162. [[GULAppDelegateSwizzler sharedApplication] setDelegate:incompleteAppDelegate];
  163. [self.proxy swizzleMethodsIfPossible];
  164. NSDictionary *notification = @{@"test" : @""};
  165. OCMExpect([self.mockMessaging appDidReceiveMessage:notification]);
  166. #pragma clang diagnostic push
  167. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  168. [incompleteAppDelegate application:[GULAppDelegateSwizzler sharedApplication]
  169. didReceiveRemoteNotification:notification];
  170. #pragma clang diagnostic pop
  171. [self.mockMessaging verify];
  172. }
  173. - (void)testIncompleteAppDelegateRemoteNotificationWithFetchHandlerMethod {
  174. IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc] init];
  175. [[GULAppDelegateSwizzler sharedApplication] setDelegate:incompleteAppDelegate];
  176. [self.proxy swizzleMethodsIfPossible];
  177. #if TARGET_OS_IOS || TARGET_OS_TV
  178. SEL remoteNotificationWithFetchHandler = @selector(application:
  179. didReceiveRemoteNotification:fetchCompletionHandler:);
  180. XCTAssertFalse([incompleteAppDelegate respondsToSelector:remoteNotificationWithFetchHandler]);
  181. #endif
  182. SEL remoteNotification = @selector(application:didReceiveRemoteNotification:);
  183. XCTAssertTrue([incompleteAppDelegate respondsToSelector:remoteNotification]);
  184. }
  185. - (void)testSwizzledAppDelegateRemoteNotificationMethods {
  186. FakeAppDelegate *appDelegate = [[FakeAppDelegate alloc] init];
  187. [[GULAppDelegateSwizzler sharedApplication] setDelegate:appDelegate];
  188. [self.proxy swizzleMethodsIfPossible];
  189. NSDictionary *notification = @{@"test" : @""};
  190. // Test application:didReceiveRemoteNotification:
  191. // Verify our swizzled method was called
  192. OCMExpect([self.mockMessaging appDidReceiveMessage:notification]);
  193. #pragma clang diagnostic push
  194. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  195. [appDelegate application:[GULAppDelegateSwizzler sharedApplication]
  196. didReceiveRemoteNotification:notification];
  197. #pragma clang diagnostic pop
  198. // Verify our original method was called
  199. XCTAssertTrue(appDelegate.remoteNotificationMethodWasCalled);
  200. [self.mockMessaging verify];
  201. // Test application:didReceiveRemoteNotification:fetchCompletionHandler:
  202. #if TARGET_OS_IOS || TARGET_OS_TV
  203. // Verify our swizzled method was called
  204. OCMExpect([self.mockMessaging appDidReceiveMessage:notification]);
  205. [appDelegate application:[GULAppDelegateSwizzler sharedApplication]
  206. didReceiveRemoteNotification:notification
  207. fetchCompletionHandler:^(UIBackgroundFetchResult result){
  208. }];
  209. // Verify our original method was called
  210. XCTAssertTrue(appDelegate.remoteNotificationWithFetchHandlerWasCalled);
  211. [self.mockMessaging verify];
  212. #endif
  213. // Verify application:didRegisterForRemoteNotificationsWithDeviceToken:
  214. NSData *deviceToken = [NSData data];
  215. OCMExpect([self.mockMessaging setAPNSToken:deviceToken]);
  216. [appDelegate application:[GULAppDelegateSwizzler sharedApplication]
  217. didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
  218. XCTAssertEqual(appDelegate.deviceToken, deviceToken);
  219. [self.mockMessaging verify];
  220. // Verify application:didFailToRegisterForRemoteNotificationsWithError:
  221. NSError *error = [NSError errorWithDomain:@"tests" code:-1 userInfo:nil];
  222. [appDelegate application:[GULAppDelegateSwizzler sharedApplication]
  223. didFailToRegisterForRemoteNotificationsWithError:error];
  224. XCTAssertEqual(appDelegate.registerForRemoteNotificationsError, error);
  225. }
  226. - (void)testListeningForDelegateChangesOnInvalidUserNotificationCenter {
  227. if (@available(macOS 10.14, iOS 10.0, *)) {
  228. RandomObject *invalidNotificationCenter = [[RandomObject alloc] init];
  229. OCMStub([self.mockUserNotificationCenter currentNotificationCenter])
  230. .andReturn(invalidNotificationCenter);
  231. [self.proxy swizzleMethodsIfPossible];
  232. OCMReject([self.mockMessaging appDidReceiveMessage:[OCMArg any]]);
  233. [(id<UNUserNotificationCenterDelegate>)invalidNotificationCenter.delegate
  234. userNotificationCenter:self.mockUserNotificationCenter
  235. willPresentNotification:OCMClassMock([UNNotification class])
  236. withCompletionHandler:^(UNNotificationPresentationOptions options){
  237. }];
  238. }
  239. }
  240. - (void)testSwizzlingInvalidUserNotificationCenterDelegate {
  241. if (@available(macOS 10.14, iOS 10.0, *)) {
  242. RandomObject *invalidDelegate = [[RandomObject alloc] init];
  243. OCMStub([self.mockUserNotificationCenter delegate]).andReturn(invalidDelegate);
  244. [self.proxy swizzleMethodsIfPossible];
  245. OCMReject([self.mockMessaging appDidReceiveMessage:[OCMArg any]]);
  246. [invalidDelegate userNotificationCenter:self.mockUserNotificationCenter
  247. willPresentNotification:OCMClassMock([UNNotification class])
  248. withCompletionHandler:^(UNNotificationPresentationOptions options){
  249. }];
  250. }
  251. }
  252. - (void)testSwizzlingUserNotificationsCenterDelegate {
  253. #if TARGET_OS_IOS || TARGET_OS_OSX
  254. if (@available(macOS 10.14, iOS 10.0, *)) {
  255. FakeUserNotificationCenterDelegate *delegate =
  256. [[FakeUserNotificationCenterDelegate alloc] init];
  257. OCMStub([self.mockUserNotificationCenter delegate]).andReturn(delegate);
  258. [self.proxy swizzleMethodsIfPossible];
  259. NSDictionary *message = @{@"message" : @""};
  260. id notification = [self userNotificationWithMessage:message];
  261. OCMExpect([self.mockMessaging appDidReceiveMessage:message]);
  262. [delegate userNotificationCenter:self.mockUserNotificationCenter
  263. willPresentNotification:notification
  264. withCompletionHandler:^(UNNotificationPresentationOptions options){
  265. }];
  266. [self.mockMessaging verify];
  267. }
  268. #endif
  269. }
  270. // Use a fake delegate that doesn't actually implement the needed delegate method.
  271. // Our swizzled method should not be called.
  272. - (void)testIncompleteUserNotificationCenterDelegateMethod {
  273. if (@available(macOS 10.14, iOS 10.0, *)) {
  274. IncompleteUserNotificationCenterDelegate *delegate =
  275. [[IncompleteUserNotificationCenterDelegate alloc] init];
  276. OCMStub([self.mockUserNotificationCenter delegate]).andReturn(delegate);
  277. [self.proxy swizzleMethodsIfPossible];
  278. // Because the incomplete delete does not implement either of the optional delegate methods, we
  279. // should swizzle nothing. If we had swizzled them, then respondsToSelector: would return YES
  280. // even though the delegate does not implement the methods.
  281. SEL willPresentSelector = @selector(userNotificationCenter:
  282. willPresentNotification:withCompletionHandler:);
  283. XCTAssertFalse([delegate respondsToSelector:willPresentSelector]);
  284. SEL didReceiveResponseSelector = @selector(userNotificationCenter:
  285. didReceiveNotificationResponse:withCompletionHandler:);
  286. XCTAssertFalse([delegate respondsToSelector:didReceiveResponseSelector]);
  287. }
  288. }
  289. // Use an object that does actually implement the optional methods. Both should be called.
  290. - (void)testSwizzledUserNotificationsCenterDelegate {
  291. #if TARGET_OS_IOS || TARGET_OS_OSX
  292. FakeUserNotificationCenterDelegate *delegate = [[FakeUserNotificationCenterDelegate alloc] init];
  293. OCMStub([self.mockUserNotificationCenter delegate]).andReturn(delegate);
  294. [self.proxy swizzleMethodsIfPossible];
  295. NSDictionary *message = @{@"message" : @""};
  296. // Verify userNotificationCenter:willPresentNotification:withCompletionHandler:
  297. OCMExpect([self.mockMessaging appDidReceiveMessage:message]);
  298. if (@available(macOS 10.14, iOS 10.0, tvOS 10.0, *)) {
  299. // Invoking delegate method should also invoke our swizzled method
  300. // The swizzled method uses the +sharedProxy, which should be
  301. // returning our proxy.
  302. // Use non-nil, proper classes, otherwise our SDK bails out.
  303. [delegate userNotificationCenter:self.mockUserNotificationCenter
  304. willPresentNotification:[self userNotificationWithMessage:message]
  305. withCompletionHandler:^(NSUInteger options){
  306. }];
  307. // Verify our original method was called
  308. XCTAssertTrue(delegate.willPresentWasCalled);
  309. // Verify our swizzled method was called
  310. [self.mockMessaging verify];
  311. }
  312. if (@available(macOS 10.14, iOS 10.0, *)) {
  313. // Verify userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:
  314. OCMExpect([self.mockMessaging appDidReceiveMessage:message]);
  315. [delegate userNotificationCenter:self.mockUserNotificationCenter
  316. didReceiveNotificationResponse:[self userNotificationResponseWithMessage:message]
  317. withCompletionHandler:^{
  318. }];
  319. // Verify our original method was called
  320. XCTAssertTrue(delegate.didReceiveResponseWasCalled);
  321. // Verify our swizzled method was called
  322. [self.mockMessaging verify];
  323. }
  324. #endif
  325. }
  326. - (id)userNotificationResponseWithMessage:(NSDictionary *)message {
  327. #if TARGET_OS_IOS || TARGET_OS_OSX
  328. if (@available(macOS 10.14, iOS 10.0, *)) {
  329. // Stub out: response.[mock notification above]
  330. id mockNotificationResponse = OCMClassMock([UNNotificationResponse class]);
  331. id mockNotification = [self userNotificationWithMessage:message];
  332. OCMStub([mockNotificationResponse notification]).andReturn(mockNotification);
  333. return mockNotificationResponse;
  334. }
  335. #endif
  336. return nil;
  337. }
  338. - (UNNotification *)userNotificationWithMessage:(NSDictionary *)message
  339. API_AVAILABLE(macos(10.14), ios(10.0)) {
  340. UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
  341. #if TARGET_OS_IOS || TARGET_OS_OSX
  342. content.userInfo = message;
  343. #endif
  344. id notificationRequest = OCMClassMock([UNNotificationRequest class]);
  345. OCMStub([notificationRequest content]).andReturn(content);
  346. id notification = OCMClassMock([UNNotification class]);
  347. OCMStub([notification request]).andReturn(notificationRequest);
  348. return notification;
  349. }
  350. @end