FIRAppTest.m 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820
  1. // Copyright 2017 Google
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #import "FIRTestCase.h"
  15. #import <FirebaseCore/FIRAnalyticsConfiguration+Internal.h>
  16. #import <FirebaseCore/FIRAppInternal.h>
  17. #import <FirebaseCore/FIROptionsInternal.h>
  18. NSString *const kFIRTestAppName1 = @"test_app_name_1";
  19. NSString *const kFIRTestAppName2 = @"test-app-name-2";
  20. @interface FIRApp (TestInternal)
  21. + (void)resetApps;
  22. - (instancetype)initInstanceWithName:(NSString *)name options:(FIROptions *)options;
  23. - (BOOL)configureCore;
  24. + (NSError *)errorForInvalidAppID;
  25. - (BOOL)isAppIDValid;
  26. + (NSString *)actualBundleID;
  27. + (NSNumber *)mapFromServiceStringToTypeEnum:(NSString *)serviceString;
  28. + (NSString *)deviceModel;
  29. + (NSString *)installString;
  30. + (NSURL *)filePathURLWithName:(NSString *)fileName;
  31. + (NSString *)stringAtURL:(NSURL *)filePathURL;
  32. + (BOOL)writeString:(NSString *)string toURL:(NSURL *)filePathURL;
  33. + (void)logAppInfo:(NSNotification *)notification;
  34. + (BOOL)validateAppID:(NSString *)appID;
  35. + (BOOL)validateAppIDFormat:(NSString *)appID withVersion:(NSString *)version;
  36. + (BOOL)validateAppIDFingerprint:(NSString *)appID withVersion:(NSString *)version;
  37. + (nullable NSNumber *)readDataCollectionSwitchFromPlist;
  38. + (nullable NSNumber *)readDataCollectionSwitchFromUserDefaultsForApp:(FIRApp *)app;
  39. @end
  40. @interface FIRAppTest : FIRTestCase
  41. @property(nonatomic) id appClassMock;
  42. @property(nonatomic) id observerMock;
  43. @property(nonatomic) NSNotificationCenter *notificationCenter;
  44. @end
  45. @implementation FIRAppTest
  46. - (void)setUp {
  47. [super setUp];
  48. [FIROptions resetDefaultOptions];
  49. [FIRApp resetApps];
  50. _appClassMock = OCMClassMock([FIRApp class]);
  51. _observerMock = OCMObserverMock();
  52. // TODO: Remove all usages of defaultCenter in Core, then we can instantiate an instance here to
  53. // inject instead of using defaultCenter.
  54. _notificationCenter = [NSNotificationCenter defaultCenter];
  55. }
  56. - (void)tearDown {
  57. [_appClassMock stopMocking];
  58. [_notificationCenter removeObserver:_observerMock];
  59. _observerMock = nil;
  60. _notificationCenter = nil;
  61. [super tearDown];
  62. }
  63. - (void)testConfigure {
  64. NSDictionary *expectedUserInfo =
  65. [self expectedUserInfoWithAppName:kFIRDefaultAppName isDefaultApp:YES];
  66. [self expectNotificationForObserver:self.observerMock
  67. notificationName:kFIRAppReadyToConfigureSDKNotification
  68. object:[FIRApp class]
  69. userInfo:expectedUserInfo];
  70. XCTAssertNoThrow([FIRApp configure]);
  71. OCMVerifyAll(self.observerMock);
  72. FIRApp *app = [FIRApp defaultApp];
  73. XCTAssertNotNil(app);
  74. XCTAssertEqualObjects(app.name, kFIRDefaultAppName);
  75. XCTAssertEqualObjects(app.options.clientID, kClientID);
  76. XCTAssertTrue([FIRApp allApps].count == 1);
  77. }
  78. - (void)testConfigureWithNoDefaultOptions {
  79. id optionsClassMock = OCMClassMock([FIROptions class]);
  80. OCMStub([optionsClassMock defaultOptions]).andReturn(nil);
  81. XCTAssertThrows([FIRApp configure]);
  82. }
  83. - (void)testConfigureWithOptions {
  84. #pragma clang diagnostic push
  85. #pragma clang diagnostic ignored "-Wnonnull"
  86. // Test `nil` options.
  87. XCTAssertThrows([FIRApp configureWithOptions:nil]);
  88. #pragma clang diagnostic pop
  89. XCTAssertTrue([FIRApp allApps].count == 0);
  90. NSDictionary *expectedUserInfo =
  91. [self expectedUserInfoWithAppName:kFIRDefaultAppName isDefaultApp:YES];
  92. [self expectNotificationForObserver:self.observerMock
  93. notificationName:kFIRAppReadyToConfigureSDKNotification
  94. object:[FIRApp class]
  95. userInfo:expectedUserInfo];
  96. // Use a valid instance of options.
  97. FIROptions *options =
  98. [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID];
  99. options.clientID = kClientID;
  100. XCTAssertNoThrow([FIRApp configureWithOptions:options]);
  101. OCMVerifyAll(self.observerMock);
  102. // Verify the default app instance is created.
  103. FIRApp *app = [FIRApp defaultApp];
  104. XCTAssertNotNil(app);
  105. XCTAssertEqualObjects(app.name, kFIRDefaultAppName);
  106. XCTAssertEqualObjects(app.options.googleAppID, kGoogleAppID);
  107. XCTAssertEqualObjects(app.options.GCMSenderID, kGCMSenderID);
  108. XCTAssertEqualObjects(app.options.clientID, kClientID);
  109. XCTAssertTrue([FIRApp allApps].count == 1);
  110. }
  111. - (void)testConfigureWithNameAndOptions {
  112. FIROptions *options =
  113. [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID];
  114. options.clientID = kClientID;
  115. #pragma clang diagnostic push
  116. #pragma clang diagnostic ignored "-Wnonnull"
  117. XCTAssertThrows([FIRApp configureWithName:nil options:options]);
  118. XCTAssertThrows([FIRApp configureWithName:kFIRTestAppName1 options:nil]);
  119. #pragma clang diagnostic pop
  120. XCTAssertThrows([FIRApp configureWithName:@"" options:options]);
  121. XCTAssertTrue([FIRApp allApps].count == 0);
  122. NSDictionary *expectedUserInfo =
  123. [self expectedUserInfoWithAppName:kFIRTestAppName1 isDefaultApp:NO];
  124. [self expectNotificationForObserver:self.observerMock
  125. notificationName:kFIRAppReadyToConfigureSDKNotification
  126. object:[FIRApp class]
  127. userInfo:expectedUserInfo];
  128. XCTAssertNoThrow([FIRApp configureWithName:kFIRTestAppName1 options:options]);
  129. OCMVerifyAll(self.observerMock);
  130. XCTAssertTrue([FIRApp allApps].count == 1);
  131. FIRApp *app = [FIRApp appNamed:kFIRTestAppName1];
  132. XCTAssertNotNil(app);
  133. XCTAssertEqualObjects(app.name, kFIRTestAppName1);
  134. XCTAssertEqualObjects(app.options.clientID, kClientID);
  135. // Configure the same app again should throw an exception.
  136. XCTAssertThrows([FIRApp configureWithName:kFIRTestAppName1 options:options]);
  137. }
  138. - (void)testConfigureWithMultipleApps {
  139. FIROptions *options1 =
  140. [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID];
  141. options1.deepLinkURLScheme = kDeepLinkURLScheme;
  142. // Set up notification center observer for verifying notifications.
  143. [self.notificationCenter addMockObserver:self.observerMock
  144. name:kFIRAppReadyToConfigureSDKNotification
  145. object:[FIRApp class]];
  146. NSDictionary *expectedUserInfo1 =
  147. [self expectedUserInfoWithAppName:kFIRTestAppName1 isDefaultApp:NO];
  148. [[self.observerMock expect] notificationWithName:kFIRAppReadyToConfigureSDKNotification
  149. object:[FIRApp class]
  150. userInfo:expectedUserInfo1];
  151. XCTAssertNoThrow([FIRApp configureWithName:kFIRTestAppName1 options:options1]);
  152. XCTAssertTrue([FIRApp allApps].count == 1);
  153. // Configure a different app with valid customized options.
  154. FIROptions *options2 =
  155. [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID];
  156. options2.bundleID = kBundleID;
  157. options2.APIKey = kCustomizedAPIKey;
  158. NSDictionary *expectedUserInfo2 =
  159. [self expectedUserInfoWithAppName:kFIRTestAppName2 isDefaultApp:NO];
  160. [[self.observerMock expect] notificationWithName:kFIRAppReadyToConfigureSDKNotification
  161. object:[FIRApp class]
  162. userInfo:expectedUserInfo2];
  163. [self.observerMock setExpectationOrderMatters:YES];
  164. XCTAssertNoThrow([FIRApp configureWithName:kFIRTestAppName2 options:options2]);
  165. OCMVerifyAll(self.observerMock);
  166. XCTAssertTrue([FIRApp allApps].count == 2);
  167. FIRApp *app = [FIRApp appNamed:kFIRTestAppName2];
  168. XCTAssertNotNil(app);
  169. XCTAssertEqualObjects(app.name, kFIRTestAppName2);
  170. XCTAssertEqualObjects(app.options.googleAppID, kGoogleAppID);
  171. XCTAssertEqualObjects(app.options.APIKey, kCustomizedAPIKey);
  172. }
  173. - (void)testValidName {
  174. XCTAssertNoThrow([FIRApp configureWithName:@"aA1_" options:[FIROptions defaultOptions]]);
  175. XCTAssertThrows([FIRApp configureWithName:@"aA1%" options:[FIROptions defaultOptions]]);
  176. XCTAssertThrows([FIRApp configureWithName:@"aA1?" options:[FIROptions defaultOptions]]);
  177. XCTAssertThrows([FIRApp configureWithName:@"aA1!" options:[FIROptions defaultOptions]]);
  178. }
  179. - (void)testDefaultApp {
  180. FIRApp *app = [FIRApp defaultApp];
  181. XCTAssertNil(app);
  182. [FIRApp configure];
  183. app = [FIRApp defaultApp];
  184. XCTAssertEqualObjects(app.name, kFIRDefaultAppName);
  185. XCTAssertEqualObjects(app.options.clientID, kClientID);
  186. }
  187. - (void)testAppNamed {
  188. FIRApp *app = [FIRApp appNamed:kFIRTestAppName1];
  189. XCTAssertNil(app);
  190. [FIRApp configureWithName:kFIRTestAppName1 options:[FIROptions defaultOptions]];
  191. app = [FIRApp appNamed:kFIRTestAppName1];
  192. XCTAssertEqualObjects(app.name, kFIRTestAppName1);
  193. XCTAssertEqualObjects(app.options.clientID, kClientID);
  194. }
  195. - (void)testDeleteApp {
  196. NSString *name = NSStringFromSelector(_cmd);
  197. FIROptions *options =
  198. [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID];
  199. [FIRApp configureWithName:name options:options];
  200. FIRApp *app = [FIRApp appNamed:name];
  201. XCTAssertNotNil(app);
  202. XCTAssertTrue([FIRApp allApps].count == 1);
  203. [self expectNotificationForObserver:self.observerMock
  204. notificationName:kFIRAppDeleteNotification
  205. object:[FIRApp class]
  206. userInfo:[OCMArg any]];
  207. XCTestExpectation *expectation =
  208. [self expectationWithDescription:@"Deleting the app should succeed."];
  209. [app deleteApp:^(BOOL success) {
  210. XCTAssertTrue(success);
  211. [expectation fulfill];
  212. }];
  213. [self waitForExpectations:@[ expectation ] timeout:1];
  214. OCMVerifyAll(self.observerMock);
  215. XCTAssertTrue([FIRApp allApps].count == 0);
  216. }
  217. - (void)testErrorForSubspecConfigurationFailure {
  218. NSError *error = [FIRApp errorForSubspecConfigurationFailureWithDomain:kFirebaseAdMobErrorDomain
  219. errorCode:FIRErrorCodeAdMobFailed
  220. service:kFIRServiceAdMob
  221. reason:@"some reason"];
  222. XCTAssertNotNil(error);
  223. XCTAssert([error.domain isEqualToString:kFirebaseAdMobErrorDomain]);
  224. XCTAssert(error.code == FIRErrorCodeAdMobFailed);
  225. XCTAssert([error.description containsString:@"Configuration failed for"]);
  226. }
  227. - (void)testGetTokenWithCallback {
  228. [FIRApp configure];
  229. FIRApp *app = [FIRApp defaultApp];
  230. __block BOOL getTokenImplementationWasCalled = NO;
  231. __block BOOL getTokenCallbackWasCalled = NO;
  232. __block BOOL passedRefreshValue = NO;
  233. [app getTokenForcingRefresh:YES
  234. withCallback:^(NSString *_Nullable token, NSError *_Nullable error) {
  235. getTokenCallbackWasCalled = YES;
  236. }];
  237. XCTAssert(getTokenCallbackWasCalled,
  238. @"The callback should be invoked by the base implementation when no block for "
  239. "'getTokenImplementation' has been specified.");
  240. getTokenCallbackWasCalled = NO;
  241. app.getTokenImplementation = ^(BOOL refresh, FIRTokenCallback callback) {
  242. getTokenImplementationWasCalled = YES;
  243. passedRefreshValue = refresh;
  244. callback(nil, nil);
  245. };
  246. [app getTokenForcingRefresh:YES
  247. withCallback:^(NSString *_Nullable token, NSError *_Nullable error) {
  248. getTokenCallbackWasCalled = YES;
  249. }];
  250. XCTAssert(getTokenImplementationWasCalled,
  251. @"The 'getTokenImplementation' block was never called.");
  252. XCTAssert(passedRefreshValue,
  253. @"The value for the 'refresh' parameter wasn't passed to the 'getTokenImplementation' "
  254. "block correctly.");
  255. XCTAssert(getTokenCallbackWasCalled,
  256. @"The 'getTokenImplementation' should have invoked the callback. This could be an "
  257. "error in this test, or the callback parameter may not have been passed to the "
  258. "implementation correctly.");
  259. getTokenImplementationWasCalled = NO;
  260. getTokenCallbackWasCalled = NO;
  261. passedRefreshValue = NO;
  262. [app getTokenForcingRefresh:NO
  263. withCallback:^(NSString *_Nullable token, NSError *_Nullable error) {
  264. getTokenCallbackWasCalled = YES;
  265. }];
  266. XCTAssertFalse(passedRefreshValue,
  267. @"The value for the 'refresh' parameter wasn't passed to the "
  268. "'getTokenImplementation' block correctly.");
  269. }
  270. - (void)testOptionsLocking {
  271. FIROptions *options =
  272. [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID];
  273. options.projectID = kProjectID;
  274. options.databaseURL = kDatabaseURL;
  275. // Options should not be locked before they are used to configure a `FIRApp`.
  276. XCTAssertFalse(options.isEditingLocked);
  277. // The options returned should be locked after configuring `FIRApp`.
  278. NSString *name = NSStringFromSelector(_cmd);
  279. [FIRApp configureWithName:name options:options];
  280. FIROptions *optionsCopy = [[FIRApp appNamed:name] options];
  281. XCTAssertTrue(optionsCopy.isEditingLocked);
  282. }
  283. #pragma mark - App ID v1
  284. - (void)testAppIDV1 {
  285. // Missing separator between platform:fingerprint.
  286. XCTAssertFalse([FIRApp validateAppID:@"1:1337:iosdeadbeef"]);
  287. // Wrong platform "android".
  288. XCTAssertFalse([FIRApp validateAppID:@"1:1337:android:deadbeef"]);
  289. // The fingerprint, aka 4th field, should only contain hex characters.
  290. XCTAssertFalse([FIRApp validateAppID:@"1:1337:ios:123abcxyz"]);
  291. // The fingerprint, aka 4th field, is not tested in V1, so a bad value shouldn't cause a failure.
  292. XCTAssertTrue([FIRApp validateAppID:@"1:1337:ios:deadbeef"]);
  293. }
  294. #pragma mark - App ID v2
  295. - (void)testAppIDV2 {
  296. // Missing separator between platform:fingerprint.
  297. XCTAssertTrue([FIRApp validateAppID:@"2:1337:ios5e18052ab54fbfec"]);
  298. // Unknown versions may contain anything.
  299. XCTAssertTrue([FIRApp validateAppID:@"2:1337:ios:123abcxyz"]);
  300. XCTAssertTrue([FIRApp validateAppID:@"2:thisdoesn'teven_m:a:t:t:e:r_"]);
  301. // Known good fingerprint.
  302. XCTAssertTrue([FIRApp validateAppID:@"2:1337:ios:5e18052ab54fbfec"]);
  303. // Unknown fingerprint, not tested so shouldn't cause a failure.
  304. XCTAssertTrue([FIRApp validateAppID:@"2:1337:ios:deadbeef"]);
  305. }
  306. #pragma mark - App ID other
  307. - (void)testAppIDV3 {
  308. // Currently there is no specification for v3, so we would not expect it to fail.
  309. XCTAssertTrue([FIRApp validateAppID:@"3:1337:ios:deadbeef"]);
  310. }
  311. - (void)testAppIDEmpty {
  312. XCTAssertFalse([FIRApp validateAppID:@""]);
  313. }
  314. - (void)testAppIDValidationTrue {
  315. // Ensure that isAppIDValid matches validateAppID.
  316. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"" GCMSenderID:@""];
  317. FIRApp *app = [[FIRApp alloc] initInstanceWithName:NSStringFromSelector(_cmd) options:options];
  318. OCMStub([self.appClassMock validateAppID:[OCMArg any]]).andReturn(YES);
  319. XCTAssertTrue([app isAppIDValid]);
  320. }
  321. - (void)testAppIDValidationFalse {
  322. // Ensure that isAppIDValid matches validateAppID.
  323. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"" GCMSenderID:@""];
  324. FIRApp *app = [[FIRApp alloc] initInstanceWithName:NSStringFromSelector(_cmd) options:options];
  325. OCMStub([self.appClassMock validateAppID:[OCMArg any]]).andReturn(NO);
  326. XCTAssertFalse([app isAppIDValid]);
  327. }
  328. - (void)testAppIDPrefix {
  329. // Unknown numeric-character prefixes should pass.
  330. XCTAssertTrue([FIRApp validateAppID:@"0:"]);
  331. XCTAssertTrue([FIRApp validateAppID:@"01:"]);
  332. XCTAssertTrue([FIRApp validateAppID:@"10:"]);
  333. XCTAssertTrue([FIRApp validateAppID:@"010:"]);
  334. XCTAssertTrue([FIRApp validateAppID:@"3:"]);
  335. XCTAssertTrue([FIRApp validateAppID:@"123:"]);
  336. XCTAssertTrue([FIRApp validateAppID:@"999999999:"]);
  337. // Non-numeric prefixes should not pass.
  338. XCTAssertFalse([FIRApp validateAppID:@"a:"]);
  339. XCTAssertFalse([FIRApp validateAppID:@"abcsdf0:"]);
  340. XCTAssertFalse([FIRApp validateAppID:@"0aaaa:"]);
  341. XCTAssertFalse([FIRApp validateAppID:@"0aaaa0450:"]);
  342. XCTAssertFalse([FIRApp validateAppID:@"-1:"]);
  343. XCTAssertFalse([FIRApp validateAppID:@"abcsdf:"]);
  344. XCTAssertFalse([FIRApp validateAppID:@"ABDCF:"]);
  345. XCTAssertFalse([FIRApp validateAppID:@" :"]);
  346. XCTAssertFalse([FIRApp validateAppID:@"1 :"]);
  347. XCTAssertFalse([FIRApp validateAppID:@" 1:"]);
  348. XCTAssertFalse([FIRApp validateAppID:@" 123 :"]);
  349. XCTAssertFalse([FIRApp validateAppID:@"1 23:"]);
  350. XCTAssertFalse([FIRApp validateAppID:@"&($*&%(*$&:"]);
  351. XCTAssertFalse([FIRApp validateAppID:@"abCDSF$%%df:"]);
  352. // Known version prefixes should never pass without the rest of the app ID string present.
  353. XCTAssertFalse([FIRApp validateAppID:@"1:"]);
  354. // Version must include ":".
  355. XCTAssertFalse([FIRApp validateAppID:@"0"]);
  356. XCTAssertFalse([FIRApp validateAppID:@"01"]);
  357. XCTAssertFalse([FIRApp validateAppID:@"10"]);
  358. XCTAssertFalse([FIRApp validateAppID:@"010"]);
  359. XCTAssertFalse([FIRApp validateAppID:@"3"]);
  360. XCTAssertFalse([FIRApp validateAppID:@"123"]);
  361. XCTAssertFalse([FIRApp validateAppID:@"999999999"]);
  362. XCTAssertFalse([FIRApp validateAppID:@"com.google.bundleID"]);
  363. }
  364. - (void)testAppIDFormatInvalid {
  365. OCMStub([self.appClassMock actualBundleID]).andReturn(@"com.google.bundleID");
  366. // Some direct tests of the validateAppIDFormat:withVersion: method.
  367. // Sanity checks first.
  368. NSString *const kGoodAppIDV1 = @"1:1337:ios:deadbeef";
  369. NSString *const kGoodVersionV1 = @"1:";
  370. XCTAssertTrue([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:kGoodVersionV1]);
  371. NSString *const kGoodAppIDV2 = @"2:1337:ios:5e18052ab54fbfec";
  372. NSString *const kGoodVersionV2 = @"2:";
  373. XCTAssertTrue([FIRApp validateAppIDFormat:kGoodAppIDV2 withVersion:kGoodVersionV2]);
  374. // Version mismatch.
  375. XCTAssertFalse([FIRApp validateAppIDFormat:kGoodAppIDV2 withVersion:kGoodVersionV1]);
  376. XCTAssertFalse([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:kGoodVersionV2]);
  377. XCTAssertFalse([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:@"999:"]);
  378. // Nil or empty strings.
  379. XCTAssertFalse([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:nil]);
  380. XCTAssertFalse([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:@""]);
  381. XCTAssertFalse([FIRApp validateAppIDFormat:nil withVersion:kGoodVersionV1]);
  382. XCTAssertFalse([FIRApp validateAppIDFormat:@"" withVersion:kGoodVersionV1]);
  383. XCTAssertFalse([FIRApp validateAppIDFormat:nil withVersion:nil]);
  384. XCTAssertFalse([FIRApp validateAppIDFormat:@"" withVersion:@""]);
  385. // App ID contains only the version prefix.
  386. XCTAssertFalse([FIRApp validateAppIDFormat:kGoodVersionV1 withVersion:kGoodVersionV1]);
  387. // The version is the entire app ID.
  388. XCTAssertFalse([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:kGoodAppIDV1]);
  389. // Versions digits that may make a partial match.
  390. XCTAssertFalse([FIRApp validateAppIDFormat:@"01:1337:ios:deadbeef" withVersion:kGoodVersionV1]);
  391. XCTAssertFalse([FIRApp validateAppIDFormat:@"10:1337:ios:deadbeef" withVersion:kGoodVersionV1]);
  392. XCTAssertFalse([FIRApp validateAppIDFormat:@"11:1337:ios:deadbeef" withVersion:kGoodVersionV1]);
  393. XCTAssertFalse(
  394. [FIRApp validateAppIDFormat:@"21:1337:ios:5e18052ab54fbfec" withVersion:kGoodVersionV2]);
  395. XCTAssertFalse(
  396. [FIRApp validateAppIDFormat:@"22:1337:ios:5e18052ab54fbfec" withVersion:kGoodVersionV2]);
  397. XCTAssertFalse(
  398. [FIRApp validateAppIDFormat:@"02:1337:ios:5e18052ab54fbfec" withVersion:kGoodVersionV2]);
  399. XCTAssertFalse(
  400. [FIRApp validateAppIDFormat:@"20:1337:ios:5e18052ab54fbfec" withVersion:kGoodVersionV2]);
  401. // Extra fields.
  402. XCTAssertFalse([FIRApp validateAppIDFormat:@"ab:1:1337:ios:deadbeef" withVersion:kGoodVersionV1]);
  403. XCTAssertFalse([FIRApp validateAppIDFormat:@"1:ab:1337:ios:deadbeef" withVersion:kGoodVersionV1]);
  404. XCTAssertFalse([FIRApp validateAppIDFormat:@"1:1337:ab:ios:deadbeef" withVersion:kGoodVersionV1]);
  405. XCTAssertFalse([FIRApp validateAppIDFormat:@"1:1337:ios:ab:deadbeef" withVersion:kGoodVersionV1]);
  406. XCTAssertFalse([FIRApp validateAppIDFormat:@"1:1337:ios:deadbeef:ab" withVersion:kGoodVersionV1]);
  407. }
  408. - (void)testAppIDFingerprintInvalid {
  409. OCMStub([self.appClassMock actualBundleID]).andReturn(@"com.google.bundleID");
  410. // Some direct tests of the validateAppIDFingerprint:withVersion: method.
  411. // Sanity checks first.
  412. NSString *const kGoodAppIDV1 = @"1:1337:ios:deadbeef";
  413. NSString *const kGoodVersionV1 = @"1:";
  414. XCTAssertTrue([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:kGoodVersionV1]);
  415. NSString *const kGoodAppIDV2 = @"2:1337:ios:5e18052ab54fbfec";
  416. NSString *const kGoodVersionV2 = @"2:";
  417. XCTAssertTrue([FIRApp validateAppIDFormat:kGoodAppIDV2 withVersion:kGoodVersionV2]);
  418. // Version mismatch.
  419. XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV2 withVersion:kGoodVersionV1]);
  420. XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:kGoodVersionV2]);
  421. XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:@"999:"]);
  422. // Nil or empty strings.
  423. XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:nil]);
  424. XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:@""]);
  425. XCTAssertFalse([FIRApp validateAppIDFingerprint:nil withVersion:kGoodVersionV1]);
  426. XCTAssertFalse([FIRApp validateAppIDFingerprint:@"" withVersion:kGoodVersionV1]);
  427. XCTAssertFalse([FIRApp validateAppIDFingerprint:nil withVersion:nil]);
  428. XCTAssertFalse([FIRApp validateAppIDFingerprint:@"" withVersion:@""]);
  429. // App ID contains only the version prefix.
  430. XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodVersionV1 withVersion:kGoodVersionV1]);
  431. // The version is the entire app ID.
  432. XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:kGoodAppIDV1]);
  433. // Versions digits that may make a partial match.
  434. XCTAssertFalse(
  435. [FIRApp validateAppIDFingerprint:@"01:1337:ios:deadbeef" withVersion:kGoodVersionV1]);
  436. XCTAssertFalse(
  437. [FIRApp validateAppIDFingerprint:@"10:1337:ios:deadbeef" withVersion:kGoodVersionV1]);
  438. XCTAssertFalse(
  439. [FIRApp validateAppIDFingerprint:@"11:1337:ios:deadbeef" withVersion:kGoodVersionV1]);
  440. XCTAssertFalse(
  441. [FIRApp validateAppIDFingerprint:@"21:1337:ios:5e18052ab54fbfec" withVersion:kGoodVersionV2]);
  442. XCTAssertFalse(
  443. [FIRApp validateAppIDFingerprint:@"22:1337:ios:5e18052ab54fbfec" withVersion:kGoodVersionV2]);
  444. XCTAssertFalse(
  445. [FIRApp validateAppIDFingerprint:@"02:1337:ios:5e18052ab54fbfec" withVersion:kGoodVersionV2]);
  446. XCTAssertFalse(
  447. [FIRApp validateAppIDFingerprint:@"20:1337:ios:5e18052ab54fbfec" withVersion:kGoodVersionV2]);
  448. // Extra fields.
  449. XCTAssertFalse(
  450. [FIRApp validateAppIDFingerprint:@"ab:1:1337:ios:deadbeef" withVersion:kGoodVersionV1]);
  451. XCTAssertFalse(
  452. [FIRApp validateAppIDFingerprint:@"1:ab:1337:ios:deadbeef" withVersion:kGoodVersionV1]);
  453. XCTAssertFalse(
  454. [FIRApp validateAppIDFingerprint:@"1:1337:ab:ios:deadbeef" withVersion:kGoodVersionV1]);
  455. XCTAssertFalse(
  456. [FIRApp validateAppIDFingerprint:@"1:1337:ios:ab:deadbeef" withVersion:kGoodVersionV1]);
  457. XCTAssertFalse(
  458. [FIRApp validateAppIDFingerprint:@"1:1337:ios:deadbeef:ab" withVersion:kGoodVersionV1]);
  459. }
  460. #pragma mark - Automatic Data Collection Tests
  461. - (void)testGlobalDataCollectionNoFlags {
  462. // Test: No flags set.
  463. NSString *name = NSStringFromSelector(_cmd);
  464. FIROptions *options =
  465. [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID];
  466. FIRApp *app = [[FIRApp alloc] initInstanceWithName:name options:options];
  467. OCMStub([self.appClassMock readDataCollectionSwitchFromPlist]).andReturn(nil);
  468. OCMStub([self.appClassMock readDataCollectionSwitchFromUserDefaultsForApp:OCMOCK_ANY])
  469. .andReturn(nil);
  470. XCTAssertTrue(app.isDataCollectionDefaultEnabled);
  471. }
  472. - (void)testGlobalDataCollectionPlistSetEnabled {
  473. // Test: Plist set to enabled, no override.
  474. NSString *name = NSStringFromSelector(_cmd);
  475. FIROptions *options =
  476. [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID];
  477. FIRApp *app = [[FIRApp alloc] initInstanceWithName:name options:options];
  478. OCMStub([self.appClassMock readDataCollectionSwitchFromPlist]).andReturn(@YES);
  479. OCMStub([self.appClassMock readDataCollectionSwitchFromUserDefaultsForApp:OCMOCK_ANY])
  480. .andReturn(nil);
  481. XCTAssertTrue(app.isDataCollectionDefaultEnabled);
  482. }
  483. - (void)testGlobalDataCollectionPlistSetDisabled {
  484. // Test: Plist set to disabled, no override.
  485. NSString *name = NSStringFromSelector(_cmd);
  486. FIROptions *options =
  487. [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID];
  488. FIRApp *app = [[FIRApp alloc] initInstanceWithName:name options:options];
  489. OCMStub([self.appClassMock readDataCollectionSwitchFromPlist]).andReturn(@NO);
  490. OCMStub([self.appClassMock readDataCollectionSwitchFromUserDefaultsForApp:OCMOCK_ANY])
  491. .andReturn(nil);
  492. XCTAssertFalse(app.isDataCollectionDefaultEnabled);
  493. }
  494. - (void)testGlobalDataCollectionUserSpecifiedEnabled {
  495. // Test: User specified as enabled, no plist value.
  496. NSString *name = NSStringFromSelector(_cmd);
  497. FIROptions *options =
  498. [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID];
  499. FIRApp *app = [[FIRApp alloc] initInstanceWithName:name options:options];
  500. OCMStub([self.appClassMock readDataCollectionSwitchFromPlist]).andReturn(nil);
  501. OCMStub([self.appClassMock readDataCollectionSwitchFromUserDefaultsForApp:OCMOCK_ANY])
  502. .andReturn(@YES);
  503. XCTAssertTrue(app.isDataCollectionDefaultEnabled);
  504. }
  505. - (void)testGlobalDataCollectionUserSpecifiedDisabled {
  506. // Test: User specified as disabled, no plist value.
  507. NSString *name = NSStringFromSelector(_cmd);
  508. FIROptions *options =
  509. [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID];
  510. FIRApp *app = [[FIRApp alloc] initInstanceWithName:name options:options];
  511. OCMStub([self.appClassMock readDataCollectionSwitchFromPlist]).andReturn(nil);
  512. OCMStub([self.appClassMock readDataCollectionSwitchFromUserDefaultsForApp:OCMOCK_ANY])
  513. .andReturn(@NO);
  514. XCTAssertFalse(app.isDataCollectionDefaultEnabled);
  515. }
  516. - (void)testGlobalDataCollectionUserOverriddenEnabled {
  517. // Test: User specified as enabled, with plist set as disabled.
  518. NSString *name = NSStringFromSelector(_cmd);
  519. FIROptions *options =
  520. [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID];
  521. FIRApp *app = [[FIRApp alloc] initInstanceWithName:name options:options];
  522. OCMStub([self.appClassMock readDataCollectionSwitchFromPlist]).andReturn(@NO);
  523. OCMStub([self.appClassMock readDataCollectionSwitchFromUserDefaultsForApp:OCMOCK_ANY])
  524. .andReturn(@YES);
  525. XCTAssertTrue(app.isDataCollectionDefaultEnabled);
  526. }
  527. - (void)testGlobalDataCollectionUserOverriddenDisabled {
  528. // Test: User specified as disabled, with plist set as enabled.
  529. NSString *name = NSStringFromSelector(_cmd);
  530. FIROptions *options =
  531. [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID];
  532. FIRApp *app = [[FIRApp alloc] initInstanceWithName:name options:options];
  533. OCMStub([self.appClassMock readDataCollectionSwitchFromPlist]).andReturn(@YES);
  534. OCMStub([self.appClassMock readDataCollectionSwitchFromUserDefaultsForApp:OCMOCK_ANY])
  535. .andReturn(@NO);
  536. XCTAssertFalse(app.isDataCollectionDefaultEnabled);
  537. }
  538. - (void)testGlobalDataCollectionWriteToDefaults {
  539. id defaultsMock = OCMPartialMock([NSUserDefaults standardUserDefaults]);
  540. NSString *name = NSStringFromSelector(_cmd);
  541. FIROptions *options =
  542. [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID];
  543. [FIRApp configureWithName:name options:options];
  544. FIRApp *app = [FIRApp appNamed:name];
  545. app.dataCollectionDefaultEnabled = YES;
  546. NSString *key =
  547. [NSString stringWithFormat:kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat, app.name];
  548. OCMVerify([defaultsMock setObject:@YES forKey:key]);
  549. app.dataCollectionDefaultEnabled = NO;
  550. OCMVerify([defaultsMock setObject:@NO forKey:key]);
  551. [defaultsMock stopMocking];
  552. }
  553. - (void)testGlobalDataCollectionClearedAfterDelete {
  554. // Configure and disable data collection for the default FIRApp.
  555. NSString *name = NSStringFromSelector(_cmd);
  556. FIROptions *options =
  557. [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID];
  558. [FIRApp configureWithName:name options:options];
  559. FIRApp *app = [FIRApp appNamed:name];
  560. app.dataCollectionDefaultEnabled = NO;
  561. XCTAssertFalse(app.isDataCollectionDefaultEnabled);
  562. // Delete the app, and verify that the switch was reset.
  563. XCTestExpectation *deleteFinished =
  564. [self expectationWithDescription:@"The app should successfully delete."];
  565. [app deleteApp:^(BOOL success) {
  566. XCTAssertTrue(success);
  567. [deleteFinished fulfill];
  568. }];
  569. // Wait for the delete to complete.
  570. [self waitForExpectations:@[ deleteFinished ] timeout:1];
  571. // Set up an app with the same name again, and check the data collection flag.
  572. [FIRApp configureWithName:name options:options];
  573. XCTAssertTrue([FIRApp appNamed:name].isDataCollectionDefaultEnabled);
  574. }
  575. - (void)testGlobalDataCollectionNoDiagnosticsSent {
  576. FIROptions *options =
  577. [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID];
  578. FIRApp *app = [[FIRApp alloc] initInstanceWithName:NSStringFromSelector(_cmd) options:options];
  579. app.dataCollectionDefaultEnabled = NO;
  580. // Add an observer for the diagnostics notification. Currently no object is sent, but in the
  581. // future that could change so leave it as OCMOCK_ANY.
  582. [self.notificationCenter addMockObserver:self.observerMock
  583. name:kFIRAppDiagnosticsNotification
  584. object:OCMOCK_ANY];
  585. // Stub out reading from user defaults since stubbing out the BOOL has issues. If the data
  586. // collection switch is disabled, the `sendLogs` call should return immediately and not fire a
  587. // notification.
  588. OCMStub([self.appClassMock readDataCollectionSwitchFromUserDefaultsForApp:OCMOCK_ANY])
  589. .andReturn(@NO);
  590. // Ensure configure doesn't fire a notification.
  591. [FIRApp configure];
  592. NSError *error = [NSError errorWithDomain:@"com.firebase" code:42 userInfo:nil];
  593. [app sendLogsWithServiceName:@"Service" version:@"Version" error:error];
  594. // The observer mock is strict and will raise an exception when an unexpected notification is
  595. // received.
  596. OCMVerifyAll(self.observerMock);
  597. }
  598. #pragma mark - Analytics Flag Tests
  599. - (void)testAnalyticsSetByGlobalDataCollectionSwitch {
  600. // Test that the global data collection switch triggers setting Analytics when no explicit flag is
  601. // set.
  602. id optionsMock = OCMClassMock([FIROptions class]);
  603. OCMStub([optionsMock isAnalyticsCollectionExpicitlySet]).andReturn(NO);
  604. // We need to use the default app name since Analytics only associates with the default app.
  605. FIRApp *defaultApp = [[FIRApp alloc] initInstanceWithName:kFIRDefaultAppName options:optionsMock];
  606. id configurationMock = OCMClassMock([FIRAnalyticsConfiguration class]);
  607. OCMStub([configurationMock sharedInstance]).andReturn(configurationMock);
  608. OCMStub([configurationMock setAnalyticsCollectionEnabled:OCMOCK_ANY persistSetting:OCMOCK_ANY]);
  609. // Ensure Analytics is set after the global flag is set. It needs to
  610. [defaultApp setDataCollectionDefaultEnabled:YES];
  611. OCMVerify([configurationMock setAnalyticsCollectionEnabled:YES persistSetting:NO]);
  612. [defaultApp setDataCollectionDefaultEnabled:NO];
  613. OCMVerify([configurationMock setAnalyticsCollectionEnabled:NO persistSetting:NO]);
  614. }
  615. - (void)testAnalyticsNotSetByGlobalDataCollectionSwitch {
  616. // Test that the global data collection switch doesn't override an explicitly set Analytics flag.
  617. id optionsMock = OCMClassMock([FIROptions class]);
  618. OCMStub([optionsMock isAnalyticsCollectionExpicitlySet]).andReturn(YES);
  619. FIRApp *app = [[FIRApp alloc] initInstanceWithName:@"testAnalyticsNotSet" options:optionsMock];
  620. id configurationMock = OCMClassMock([FIRAnalyticsConfiguration class]);
  621. OCMStub([configurationMock sharedInstance]).andReturn(configurationMock);
  622. OCMStub([configurationMock setAnalyticsCollectionEnabled:OCMOCK_ANY persistSetting:OCMOCK_ANY]);
  623. // Reject any changes to Analytics when the data collection changes.
  624. [app setDataCollectionDefaultEnabled:YES];
  625. OCMReject([configurationMock setAnalyticsCollectionEnabled:OCMOCK_ANY persistSetting:OCMOCK_ANY]);
  626. [app setDataCollectionDefaultEnabled:NO];
  627. OCMReject([configurationMock setAnalyticsCollectionEnabled:OCMOCK_ANY persistSetting:OCMOCK_ANY]);
  628. }
  629. #pragma mark - Internal Methods
  630. // TODO: Remove this test once the `getUIDImplementation` block doesn't need to be set in Core.
  631. - (void)testAuthGetUID {
  632. [FIRApp configure];
  633. [FIRApp defaultApp].getUIDImplementation = ^NSString * {
  634. return @"highlander";
  635. };
  636. XCTAssertEqual([[FIRApp defaultApp] getUID], @"highlander");
  637. }
  638. - (void)testIsDefaultAppConfigured {
  639. // Ensure it's false before anything is configured.
  640. XCTAssertFalse([FIRApp isDefaultAppConfigured]);
  641. // Configure it and ensure it's configured.
  642. [FIRApp configure];
  643. XCTAssertTrue([FIRApp isDefaultAppConfigured]);
  644. // Reset the apps and ensure it's not configured anymore.
  645. [FIRApp resetApps];
  646. XCTAssertFalse([FIRApp isDefaultAppConfigured]);
  647. }
  648. - (void)testIllegalLibraryName {
  649. [FIRApp registerLibrary:@"Oops>" withVersion:@"1.0.0"];
  650. XCTAssertTrue([[FIRApp firebaseUserAgent] isEqualToString:@""]);
  651. }
  652. - (void)testIllegalLibraryVersion {
  653. [FIRApp registerLibrary:@"LegalName" withVersion:@"1.0.0+"];
  654. XCTAssertTrue([[FIRApp firebaseUserAgent] isEqualToString:@""]);
  655. }
  656. - (void)testSingleLibrary {
  657. [FIRApp registerLibrary:@"LegalName" withVersion:@"1.0.0"];
  658. XCTAssertTrue([[FIRApp firebaseUserAgent] containsString:@"LegalName/1.0.0"]);
  659. }
  660. - (void)testMultipleLibraries {
  661. [FIRApp registerLibrary:@"LegalName" withVersion:@"1.0.0"];
  662. [FIRApp registerLibrary:@"LegalName2" withVersion:@"2.0.0"];
  663. XCTAssertTrue([[FIRApp firebaseUserAgent] containsString:@"LegalName/1.0.0 LegalName2/2.0.0"]);
  664. }
  665. #pragma mark - private
  666. - (void)expectNotificationForObserver:(id)observer
  667. notificationName:(NSNotificationName)name
  668. object:(nullable id)object
  669. userInfo:(nullable NSDictionary *)userInfo {
  670. [self.notificationCenter addMockObserver:observer name:name object:object];
  671. [[observer expect] notificationWithName:name object:object userInfo:userInfo];
  672. }
  673. - (NSDictionary<NSString *, NSObject *> *)expectedUserInfoWithAppName:(NSString *)name
  674. isDefaultApp:(BOOL)isDefaultApp {
  675. return @{
  676. kFIRAppNameKey : name,
  677. kFIRAppIsDefaultAppKey : [NSNumber numberWithBool:isDefaultApp],
  678. kFIRGoogleAppIDKey : kGoogleAppID
  679. };
  680. }
  681. @end