FIRMessagingTest.m 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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 <OCMock/OCMock.h>
  18. #import <FirebaseInstanceID/FirebaseInstanceID.h>
  19. #import "FIRMessaging.h"
  20. #import "FIRMessaging_Private.h"
  21. extern NSString *const kFIRMessagingFCMTokenFetchAPNSOption;
  22. @interface FIRMessaging ()
  23. @property(nonatomic, readwrite, strong) NSString *defaultFcmToken;
  24. @property(nonatomic, readwrite, strong) NSData *apnsTokenData;
  25. @property(nonatomic, readwrite, strong) FIRInstanceID *instanceID;
  26. @property(nonatomic, readwrite, strong) NSUserDefaults *messagingUserDefaults;
  27. - (instancetype)initWithInstanceID:(FIRInstanceID *)instanceID
  28. userDefaults:(NSUserDefaults *)defaults;
  29. // Direct Channel Methods
  30. - (void)updateAutomaticClientConnection;
  31. - (BOOL)shouldBeConnectedAutomatically;
  32. @end
  33. @interface FIRMessagingTest : XCTestCase
  34. @property(nonatomic, readonly, strong) FIRMessaging *messaging;
  35. @property(nonatomic, readwrite, strong) id mockMessaging;
  36. @property(nonatomic, readwrite, strong) id mockInstanceID;
  37. @end
  38. @implementation FIRMessagingTest
  39. - (void)setUp {
  40. [super setUp];
  41. _messaging = [[FIRMessaging alloc] initWithInstanceID:[FIRInstanceID instanceID]
  42. userDefaults:[NSUserDefaults standardUserDefaults]];
  43. _mockMessaging = OCMPartialMock(self.messaging);
  44. _mockInstanceID = OCMPartialMock(self.messaging.instanceID);
  45. self.messaging.instanceID = _mockInstanceID;
  46. [[NSUserDefaults standardUserDefaults]
  47. removePersistentDomainForName:[NSBundle mainBundle].bundleIdentifier];
  48. }
  49. - (void)tearDown {
  50. _messaging = nil;
  51. [_mockMessaging stopMocking];
  52. _mockMessaging = nil;
  53. [_mockInstanceID stopMocking];
  54. _mockInstanceID = nil;
  55. [super tearDown];
  56. }
  57. - (void)testAutoInitEnableFlag {
  58. // Should read from Info.plist
  59. XCTAssertFalse(_messaging.isAutoInitEnabled);
  60. // Now set the flag should overwrite Info.plist value.
  61. _messaging.autoInitEnabled = YES;
  62. XCTAssertTrue(_messaging.isAutoInitEnabled);
  63. }
  64. #pragma mark - Direct Channel Establishment Testing
  65. // Should connect with valid token and application in foreground
  66. - (void)testDoesAutomaticallyConnectIfTokenAvailableAndForegrounded {
  67. // Disable actually attempting a connection
  68. [[[_mockMessaging stub] andDo:^(NSInvocation *invocation) {
  69. // Doing nothing on purpose, when -updateAutomaticClientConnection is called
  70. }] updateAutomaticClientConnection];
  71. // Set direct channel to be established after disabling connection attempt
  72. self.messaging.shouldEstablishDirectChannel = YES;
  73. // Set a "valid" token (i.e. not nil or empty)
  74. self.messaging.defaultFcmToken = @"1234567";
  75. // Swizzle application state to return UIApplicationStateActive
  76. UIApplication *app = [UIApplication sharedApplication];
  77. id mockApp = OCMPartialMock(app);
  78. [[[mockApp stub] andReturnValue:@(UIApplicationStateActive)] applicationState];
  79. BOOL shouldBeConnected = [_mockMessaging shouldBeConnectedAutomatically];
  80. XCTAssertTrue(shouldBeConnected);
  81. }
  82. // Should not connect if application is active, but token is empty
  83. - (void)testDoesNotAutomaticallyConnectIfTokenIsEmpty {
  84. // Disable actually attempting a connection
  85. [[[_mockMessaging stub] andDo:^(NSInvocation *invocation) {
  86. // Doing nothing on purpose, when -updateAutomaticClientConnection is called
  87. }] updateAutomaticClientConnection];
  88. // Set direct channel to be established after disabling connection attempt
  89. self.messaging.shouldEstablishDirectChannel = YES;
  90. // By default, there should be no fcmToken
  91. // Swizzle application state to return UIApplicationStateActive
  92. UIApplication *app = [UIApplication sharedApplication];
  93. id mockApp = OCMPartialMock(app);
  94. [[[mockApp stub] andReturnValue:@(UIApplicationStateActive)] applicationState];
  95. BOOL shouldBeConnected = [_mockMessaging shouldBeConnectedAutomatically];
  96. XCTAssertFalse(shouldBeConnected);
  97. }
  98. // Should not connect if token valid but application isn't active
  99. - (void)testDoesNotAutomaticallyConnectIfApplicationNotActive {
  100. // Disable actually attempting a connection
  101. [[[_mockMessaging stub] andDo:^(NSInvocation *invocation) {
  102. // Doing nothing on purpose, when -updateAutomaticClientConnection is called
  103. }] updateAutomaticClientConnection];
  104. // Set direct channel to be established after disabling connection attempt
  105. self.messaging.shouldEstablishDirectChannel = YES;
  106. // Set a "valid" token (i.e. not nil or empty)
  107. self.messaging.defaultFcmToken = @"abcd1234";
  108. // Swizzle application state to return UIApplicationStateActive
  109. UIApplication *app = [UIApplication sharedApplication];
  110. id mockApp = OCMPartialMock(app);
  111. [[[mockApp stub] andReturnValue:@(UIApplicationStateBackground)] applicationState];
  112. BOOL shouldBeConnected = [_mockMessaging shouldBeConnectedAutomatically];
  113. XCTAssertFalse(shouldBeConnected);
  114. }
  115. #pragma mark - FCM Token Fetching and Deleting
  116. #ifdef NEED_WORKAROUND_FOR_PRIVATE_OCMOCK_getArgumentAtIndexAsObject
  117. - (void)testAPNSTokenIncludedInOptionsIfAvailableDuringTokenFetch {
  118. self.messaging.apnsTokenData =
  119. [@"PRETENDING_TO_BE_A_DEVICE_TOKEN" dataUsingEncoding:NSUTF8StringEncoding];
  120. XCTestExpectation *expectation =
  121. [self expectationWithDescription:@"Included APNS Token data in options dict."];
  122. // Inspect the 'options' dictionary to tell whether our expectation was fulfilled
  123. [[[self.mockInstanceID stub] andDo:^(NSInvocation *invocation) {
  124. // Calling getArgument:atIndex: directly leads to an EXC_BAD_ACCESS; use OCMock's wrapper.
  125. NSDictionary *options = [invocation getArgumentAtIndexAsObject:4];
  126. if (options[@"apns_token"] != nil) {
  127. [expectation fulfill];
  128. }
  129. }] tokenWithAuthorizedEntity:OCMOCK_ANY scope:OCMOCK_ANY options:OCMOCK_ANY handler:OCMOCK_ANY];
  130. [self.messaging retrieveFCMTokenForSenderID:@"123456"
  131. completion:^(NSString * _Nullable FCMToken,
  132. NSError * _Nullable error) {}];
  133. [self waitForExpectationsWithTimeout:0.1 handler:nil];
  134. }
  135. - (void)testAPNSTokenNotIncludedIfUnavailableDuringTokenFetch {
  136. XCTestExpectation *expectation =
  137. [self expectationWithDescription:@"Included APNS Token data not included in options dict."];
  138. // Inspect the 'options' dictionary to tell whether our expectation was fulfilled
  139. [[[self.mockInstanceID stub] andDo:^(NSInvocation *invocation) {
  140. // Calling getArgument:atIndex: directly leads to an EXC_BAD_ACCESS; use OCMock's wrapper.
  141. NSDictionary *options = [invocation getArgumentAtIndexAsObject:4];
  142. if (options[@"apns_token"] == nil) {
  143. [expectation fulfill];
  144. }
  145. }] tokenWithAuthorizedEntity:OCMOCK_ANY scope:OCMOCK_ANY options:OCMOCK_ANY handler:OCMOCK_ANY];
  146. [self.messaging retrieveFCMTokenForSenderID:@"123456"
  147. completion:^(NSString * _Nullable FCMToken,
  148. NSError * _Nullable error) {}];
  149. [self waitForExpectationsWithTimeout:0.1 handler:nil];
  150. }
  151. #endif
  152. - (void)testReturnsErrorWhenFetchingTokenWithoutSenderID {
  153. XCTestExpectation *expectation =
  154. [self expectationWithDescription:@"Returned an error fetching token without Sender ID"];
  155. #pragma clang diagnostic push
  156. #pragma clang diagnostic ignored "-Wnonnull"
  157. [self.messaging retrieveFCMTokenForSenderID:nil
  158. completion:
  159. ^(NSString * _Nullable FCMToken, NSError * _Nullable error) {
  160. if (error != nil) {
  161. [expectation fulfill];
  162. }
  163. }];
  164. #pragma clang diagnostic pop
  165. [self waitForExpectationsWithTimeout:0.1 handler:nil];
  166. }
  167. - (void)testReturnsErrorWhenFetchingTokenWithEmptySenderID {
  168. XCTestExpectation *expectation =
  169. [self expectationWithDescription:@"Returned an error fetching token with empty Sender ID"];
  170. [self.messaging retrieveFCMTokenForSenderID:@""
  171. completion:
  172. ^(NSString * _Nullable FCMToken, NSError * _Nullable error) {
  173. if (error != nil) {
  174. [expectation fulfill];
  175. }
  176. }];
  177. [self waitForExpectationsWithTimeout:0.1 handler:nil];
  178. }
  179. - (void)testReturnsErrorWhenDeletingTokenWithoutSenderID {
  180. XCTestExpectation *expectation =
  181. [self expectationWithDescription:@"Returned an error deleting token without Sender ID"];
  182. #pragma clang diagnostic push
  183. #pragma clang diagnostic ignored "-Wnonnull"
  184. [self.messaging deleteFCMTokenForSenderID:nil completion:^(NSError * _Nullable error) {
  185. if (error != nil) {
  186. [expectation fulfill];
  187. }
  188. }];
  189. #pragma clang diagnostic pop
  190. [self waitForExpectationsWithTimeout:0.1 handler:nil];
  191. }
  192. - (void)testReturnsErrorWhenDeletingTokenWithEmptySenderID {
  193. XCTestExpectation *expectation =
  194. [self expectationWithDescription:@"Returned an error deleting token with empty Sender ID"];
  195. [self.messaging deleteFCMTokenForSenderID:@"" completion:^(NSError * _Nullable error) {
  196. if (error != nil) {
  197. [expectation fulfill];
  198. }
  199. }];
  200. [self waitForExpectationsWithTimeout:0.1 handler:nil];
  201. }
  202. @end