FIRMessagingRemoteNotificationsProxyTest.m 16 KB

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