FIRMessagingRemoteNotificationsProxyTest.m 16 KB

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