FIRAppTest.m 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997
  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 "FirebaseCore/Tests/Unit/FIRTestCase.h"
  15. #import "FirebaseCore/Tests/Unit/FIRTestComponents.h"
  16. #import <GoogleUtilities/GULAppEnvironmentUtil.h>
  17. #import "FirebaseCore/Sources/FIRAnalyticsConfiguration.h"
  18. #import "FirebaseCore/Sources/Private/FIRAppInternal.h"
  19. #import "FirebaseCore/Sources/Private/FIRComponentType.h"
  20. #import "FirebaseCore/Sources/Private/FIRCoreDiagnosticsConnector.h"
  21. #import "FirebaseCore/Sources/Private/FIROptionsInternal.h"
  22. #import "SharedTestUtilities/FIROptionsMock.h"
  23. NSString *const kFIRTestAppName1 = @"test_app_name_1";
  24. NSString *const kFIRTestAppName2 = @"test-app-name-2";
  25. @interface FIRApp (TestInternal)
  26. + (void)resetApps;
  27. - (instancetype)initInstanceWithName:(NSString *)name options:(FIROptions *)options;
  28. - (BOOL)configureCore;
  29. + (NSError *)errorForInvalidAppID;
  30. - (BOOL)isAppIDValid;
  31. + (NSString *)actualBundleID;
  32. + (NSNumber *)mapFromServiceStringToTypeEnum:(NSString *)serviceString;
  33. + (NSString *)deviceModel;
  34. + (NSString *)installString;
  35. + (NSURL *)filePathURLWithName:(NSString *)fileName;
  36. + (NSString *)stringAtURL:(NSURL *)filePathURL;
  37. + (BOOL)writeString:(NSString *)string toURL:(NSURL *)filePathURL;
  38. + (void)logAppInfo:(NSNotification *)notification;
  39. + (BOOL)validateAppID:(NSString *)appID;
  40. + (BOOL)validateAppIDFormat:(NSString *)appID withVersion:(NSString *)version;
  41. + (BOOL)validateAppIDFingerprint:(NSString *)appID withVersion:(NSString *)version;
  42. + (nullable NSNumber *)readDataCollectionSwitchFromPlist;
  43. + (nullable NSNumber *)readDataCollectionSwitchFromUserDefaultsForApp:(FIRApp *)app;
  44. @end
  45. @interface FIRAppTest : FIRTestCase
  46. @property(nonatomic) id appClassMock;
  47. @property(nonatomic) id mockCoreDiagnosticsConnector;
  48. @property(nonatomic) NSNotificationCenter *notificationCenter;
  49. /// If `YES` then throws when `logCoreTelemetryWithOptions:` method is called.
  50. @property(nonatomic) BOOL assertNoLogCoreTelemetry;
  51. @end
  52. @implementation FIRAppTest
  53. - (void)setUp {
  54. [super setUp];
  55. [FIROptions resetDefaultOptions];
  56. [FIRApp resetApps];
  57. // TODO: Don't mock the class we are testing.
  58. _appClassMock = OCMClassMock([FIRApp class]);
  59. _mockCoreDiagnosticsConnector = OCMClassMock([FIRCoreDiagnosticsConnector class]);
  60. [FIROptionsMock mockFIROptions];
  61. self.assertNoLogCoreTelemetry = NO;
  62. OCMStub(ClassMethod([self.mockCoreDiagnosticsConnector logCoreTelemetryWithOptions:[OCMArg any]]))
  63. .andDo(^(NSInvocation *invocation) {
  64. if (self.assertNoLogCoreTelemetry) {
  65. XCTFail(@"Method `-[mockCoreDiagnosticsConnector logCoreTelemetryWithOptions:]` must not "
  66. @"be called");
  67. }
  68. });
  69. // TODO: Remove all usages of defaultCenter in Core, then we can instantiate an instance here to
  70. // inject instead of using defaultCenter.
  71. _notificationCenter = [NSNotificationCenter defaultCenter];
  72. }
  73. - (void)tearDown {
  74. // Wait for background operations to complete.
  75. NSDate *waitUntilDate = [NSDate dateWithTimeIntervalSinceNow:0.5];
  76. while ([[NSDate date] compare:waitUntilDate] == NSOrderedAscending) {
  77. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
  78. }
  79. [_appClassMock stopMocking];
  80. _appClassMock = nil;
  81. _notificationCenter = nil;
  82. _mockCoreDiagnosticsConnector = nil;
  83. [super tearDown];
  84. }
  85. - (void)testConfigure {
  86. [self registerLibrariesWithClasses:@[
  87. [FIRTestClassCached class], [FIRTestClassEagerCached class]
  88. ]];
  89. NSDictionary *expectedUserInfo = [self expectedUserInfoWithAppName:kFIRDefaultAppName
  90. isDefaultApp:YES];
  91. XCTestExpectation *notificationExpectation =
  92. [self expectNotificationNamed:kFIRAppReadyToConfigureSDKNotification
  93. object:[FIRApp class]
  94. userInfo:expectedUserInfo];
  95. XCTAssertNoThrow([FIRApp configure]);
  96. [self waitForExpectations:@[ notificationExpectation ] timeout:0.1];
  97. FIRApp *app = [FIRApp defaultApp];
  98. XCTAssertNotNil(app);
  99. XCTAssertEqualObjects(app.name, kFIRDefaultAppName);
  100. XCTAssertEqualObjects(app.options.clientID, kClientID);
  101. XCTAssertTrue([FIRApp allApps].count == 1);
  102. // Check the registered libraries instances available.
  103. XCTAssertNotNil(FIR_COMPONENT(FIRTestProtocolCached, app.container));
  104. XCTAssertNotNil(FIR_COMPONENT(FIRTestProtocolEagerCached, app.container));
  105. XCTAssertNil(FIR_COMPONENT(FIRTestProtocol, app.container));
  106. }
  107. - (void)testConfigureWithNoDefaultOptions {
  108. id optionsClassMock = OCMClassMock([FIROptions class]);
  109. OCMStub([optionsClassMock defaultOptions]).andReturn(nil);
  110. XCTAssertThrows([FIRApp configure]);
  111. }
  112. - (void)testConfigureWithOptions {
  113. #pragma clang diagnostic push
  114. #pragma clang diagnostic ignored "-Wnonnull"
  115. // Test `nil` options.
  116. XCTAssertThrows([FIRApp configureWithOptions:nil]);
  117. #pragma clang diagnostic pop
  118. XCTAssertTrue([FIRApp allApps].count == 0);
  119. NSDictionary *expectedUserInfo = [self expectedUserInfoWithAppName:kFIRDefaultAppName
  120. isDefaultApp:YES];
  121. XCTestExpectation *notificationExpectation =
  122. [self expectNotificationNamed:kFIRAppReadyToConfigureSDKNotification
  123. object:[FIRApp class]
  124. userInfo:expectedUserInfo];
  125. // Use a valid instance of options.
  126. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  127. GCMSenderID:kGCMSenderID];
  128. options.clientID = kClientID;
  129. XCTAssertNoThrow([FIRApp configureWithOptions:options]);
  130. [self waitForExpectations:@[ notificationExpectation ] timeout:0.1];
  131. // Verify the default app instance is created.
  132. FIRApp *app = [FIRApp defaultApp];
  133. XCTAssertNotNil(app);
  134. XCTAssertEqualObjects(app.name, kFIRDefaultAppName);
  135. XCTAssertEqualObjects(app.options.googleAppID, kGoogleAppID);
  136. XCTAssertEqualObjects(app.options.GCMSenderID, kGCMSenderID);
  137. XCTAssertEqualObjects(app.options.clientID, kClientID);
  138. XCTAssertTrue([FIRApp allApps].count == 1);
  139. }
  140. - (void)testConfigureWithNameAndOptions {
  141. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  142. GCMSenderID:kGCMSenderID];
  143. options.clientID = kClientID;
  144. #pragma clang diagnostic push
  145. #pragma clang diagnostic ignored "-Wnonnull"
  146. XCTAssertThrows([FIRApp configureWithName:nil options:options]);
  147. XCTAssertThrows([FIRApp configureWithName:kFIRTestAppName1 options:nil]);
  148. #pragma clang diagnostic pop
  149. XCTAssertThrows([FIRApp configureWithName:@"" options:options]);
  150. XCTAssertTrue([FIRApp allApps].count == 0);
  151. NSDictionary *expectedUserInfo = [self expectedUserInfoWithAppName:kFIRTestAppName1
  152. isDefaultApp:NO];
  153. XCTestExpectation *notificationExpectation =
  154. [self expectNotificationNamed:kFIRAppReadyToConfigureSDKNotification
  155. object:[FIRApp class]
  156. userInfo:expectedUserInfo];
  157. XCTAssertNoThrow([FIRApp configureWithName:kFIRTestAppName1 options:options]);
  158. [self waitForExpectations:@[ notificationExpectation ] timeout:0.1];
  159. XCTAssertTrue([FIRApp allApps].count == 1);
  160. FIRApp *app = [FIRApp appNamed:kFIRTestAppName1];
  161. XCTAssertNotNil(app);
  162. XCTAssertEqualObjects(app.name, kFIRTestAppName1);
  163. XCTAssertEqualObjects(app.options.clientID, kClientID);
  164. // Configure the same app again should throw an exception.
  165. XCTAssertThrows([FIRApp configureWithName:kFIRTestAppName1 options:options]);
  166. }
  167. - (void)testConfigureWithMultipleApps {
  168. FIROptions *options1 = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  169. GCMSenderID:kGCMSenderID];
  170. options1.deepLinkURLScheme = kDeepLinkURLScheme;
  171. NSDictionary *expectedUserInfo1 = [self expectedUserInfoWithAppName:kFIRTestAppName1
  172. isDefaultApp:NO];
  173. XCTestExpectation *configExpectation1 =
  174. [self expectNotificationNamed:kFIRAppReadyToConfigureSDKNotification
  175. object:[FIRApp class]
  176. userInfo:expectedUserInfo1];
  177. XCTAssertNoThrow([FIRApp configureWithName:kFIRTestAppName1 options:options1]);
  178. XCTAssertTrue([FIRApp allApps].count == 1);
  179. // Configure a different app with valid customized options.
  180. FIROptions *options2 = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  181. GCMSenderID:kGCMSenderID];
  182. options2.bundleID = kBundleID;
  183. options2.APIKey = kCustomizedAPIKey;
  184. NSDictionary *expectedUserInfo2 = [self expectedUserInfoWithAppName:kFIRTestAppName2
  185. isDefaultApp:NO];
  186. XCTestExpectation *configExpectation2 =
  187. [self expectNotificationNamed:kFIRAppReadyToConfigureSDKNotification
  188. object:[FIRApp class]
  189. userInfo:expectedUserInfo2];
  190. XCTAssertNoThrow([FIRApp configureWithName:kFIRTestAppName2 options:options2]);
  191. [self waitForExpectations:@[ configExpectation1, configExpectation2 ]
  192. timeout:0.1
  193. enforceOrder:YES];
  194. XCTAssertTrue([FIRApp allApps].count == 2);
  195. FIRApp *app = [FIRApp appNamed:kFIRTestAppName2];
  196. XCTAssertNotNil(app);
  197. XCTAssertEqualObjects(app.name, kFIRTestAppName2);
  198. XCTAssertEqualObjects(app.options.googleAppID, kGoogleAppID);
  199. XCTAssertEqualObjects(app.options.APIKey, kCustomizedAPIKey);
  200. }
  201. - (void)testConfigureThrowsAfterConfigured {
  202. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  203. GCMSenderID:kGCMSenderID];
  204. [FIRApp configureWithOptions:options];
  205. XCTAssertNotNil([FIRApp defaultApp]);
  206. // A second configure call should throw, since Firebase is already configured.
  207. XCTAssertThrows([FIRApp configureWithOptions:options]);
  208. // Test the same with a custom named app.
  209. [FIRApp configureWithName:kFIRTestAppName1 options:options];
  210. XCTAssertNotNil([FIRApp appNamed:kFIRTestAppName1]);
  211. // A second configure call should throw, since Firebase is already configured.
  212. XCTAssertThrows([FIRApp configureWithName:kFIRTestAppName1 options:options]);
  213. }
  214. - (void)testConfigureDefaultAppInExtension {
  215. id environmentMock = OCMClassMock([GULAppEnvironmentUtil class]);
  216. OCMStub([environmentMock isAppExtension]).andReturn(YES);
  217. // Set up the default app like a standard app.
  218. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  219. GCMSenderID:kGCMSenderID];
  220. [FIRApp configureWithOptions:options];
  221. XCTAssertNotNil([FIRApp defaultApp]);
  222. XCTAssertEqual([FIRApp allApps].count, 1);
  223. // Configuring with the same set of options shouldn't throw.
  224. XCTAssertNoThrow([FIRApp configureWithOptions:options]);
  225. // Only 1 app should have been configured still, the default app.
  226. XCTAssertNotNil([FIRApp defaultApp]);
  227. XCTAssertEqual([FIRApp allApps].count, 1);
  228. // Use a set of a different options to call configure again, which should throw.
  229. FIROptions *differentOptions = [[FIROptions alloc] initWithGoogleAppID:@"1:789:ios:789XYZ"
  230. GCMSenderID:kGCMSenderID];
  231. XCTAssertThrows([FIRApp configureWithOptions:differentOptions]);
  232. XCTAssertEqual([FIRApp allApps].count, 1);
  233. // Explicily stop the environmentMock.
  234. [environmentMock stopMocking];
  235. environmentMock = nil;
  236. }
  237. - (void)testConfigureCustomAppInExtension {
  238. id environmentMock = OCMClassMock([GULAppEnvironmentUtil class]);
  239. OCMStub([environmentMock isAppExtension]).andReturn(YES);
  240. // Set up a custom named app like a standard app.
  241. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  242. GCMSenderID:kGCMSenderID];
  243. [FIRApp configureWithName:kFIRTestAppName1 options:options];
  244. XCTAssertNotNil([FIRApp appNamed:kFIRTestAppName1]);
  245. XCTAssertEqual([FIRApp allApps].count, 1);
  246. // Configuring with the same set of options shouldn't throw.
  247. XCTAssertNoThrow([FIRApp configureWithName:kFIRTestAppName1 options:options]);
  248. // Only 1 app should have been configured still.
  249. XCTAssertNotNil([FIRApp appNamed:kFIRTestAppName1]);
  250. XCTAssertEqual([FIRApp allApps].count, 1);
  251. // Use a set of a different options to call configure again, which should throw.
  252. FIROptions *differentOptions = [[FIROptions alloc] initWithGoogleAppID:@"1:789:ios:789XYZ"
  253. GCMSenderID:kGCMSenderID];
  254. XCTAssertThrows([FIRApp configureWithName:kFIRTestAppName1 options:differentOptions]);
  255. XCTAssertEqual([FIRApp allApps].count, 1);
  256. // Explicily stop the environmentMock.
  257. [environmentMock stopMocking];
  258. environmentMock = nil;
  259. }
  260. - (void)testValidName {
  261. XCTAssertNoThrow([FIRApp configureWithName:@"aA1_" options:[FIROptions defaultOptions]]);
  262. XCTAssertNoThrow([FIRApp configureWithName:@"aA1-" options:[FIROptions defaultOptions]]);
  263. XCTAssertNoThrow([FIRApp configureWithName:@"aAē1_" options:[FIROptions defaultOptions]]);
  264. XCTAssertThrows([FIRApp configureWithName:@"aA1%" options:[FIROptions defaultOptions]]);
  265. XCTAssertThrows([FIRApp configureWithName:@"aA1?" options:[FIROptions defaultOptions]]);
  266. XCTAssertThrows([FIRApp configureWithName:@"aA1!" options:[FIROptions defaultOptions]]);
  267. }
  268. - (void)testDefaultApp {
  269. FIRApp *app = [FIRApp defaultApp];
  270. XCTAssertNil(app);
  271. [FIRApp configure];
  272. app = [FIRApp defaultApp];
  273. XCTAssertEqualObjects(app.name, kFIRDefaultAppName);
  274. XCTAssertEqualObjects(app.options.clientID, kClientID);
  275. }
  276. - (void)testAppNamed {
  277. FIRApp *app = [FIRApp appNamed:kFIRTestAppName1];
  278. XCTAssertNil(app);
  279. [FIRApp configureWithName:kFIRTestAppName1 options:[FIROptions defaultOptions]];
  280. app = [FIRApp appNamed:kFIRTestAppName1];
  281. XCTAssertEqualObjects(app.name, kFIRTestAppName1);
  282. XCTAssertEqualObjects(app.options.clientID, kClientID);
  283. }
  284. - (void)testDeleteApp {
  285. [self registerLibrariesWithClasses:@[
  286. [FIRTestClassCached class], [FIRTestClassEagerCached class]
  287. ]];
  288. NSString *name = NSStringFromSelector(_cmd);
  289. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  290. GCMSenderID:kGCMSenderID];
  291. [FIRApp configureWithName:name options:options];
  292. FIRApp *app = [FIRApp appNamed:name];
  293. XCTAssertNotNil(app);
  294. XCTAssertTrue([FIRApp allApps].count == 1);
  295. // Check the registered libraries instances available.
  296. XCTAssertNotNil(FIR_COMPONENT(FIRTestProtocolCached, app.container));
  297. XCTAssertNotNil(FIR_COMPONENT(FIRTestProtocolEagerCached, app.container));
  298. XCTAssertNil(FIR_COMPONENT(FIRTestProtocol, app.container));
  299. XCTestExpectation *notificationExpectation =
  300. [self expectationForNotification:kFIRAppDeleteNotification
  301. object:[FIRApp class]
  302. notificationCenter:self.notificationCenter
  303. handler:nil];
  304. XCTestExpectation *deleteExpectation =
  305. [self expectationWithDescription:@"Deleting the app should succeed."];
  306. [app deleteApp:^(BOOL success) {
  307. XCTAssertTrue(success);
  308. [deleteExpectation fulfill];
  309. }];
  310. [self waitForExpectations:@[ notificationExpectation, deleteExpectation ] timeout:1];
  311. XCTAssertTrue([FIRApp allApps].count == 0);
  312. // Check no new library instances created after the app delete.
  313. XCTAssertNil(FIR_COMPONENT(FIRTestProtocolCached, app.container));
  314. XCTAssertNil(FIR_COMPONENT(FIRTestProtocolEagerCached, app.container));
  315. }
  316. - (void)testOptionsLocking {
  317. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  318. GCMSenderID:kGCMSenderID];
  319. options.projectID = kProjectID;
  320. options.databaseURL = kDatabaseURL;
  321. // Options should not be locked before they are used to configure a `FIRApp`.
  322. XCTAssertFalse(options.isEditingLocked);
  323. // The options returned should be locked after configuring `FIRApp`.
  324. NSString *name = NSStringFromSelector(_cmd);
  325. [FIRApp configureWithName:name options:options];
  326. FIROptions *optionsCopy = [[FIRApp appNamed:name] options];
  327. XCTAssertTrue(optionsCopy.isEditingLocked);
  328. }
  329. #pragma mark - App ID v1
  330. - (void)testAppIDV1 {
  331. // Missing separator between platform:fingerprint.
  332. XCTAssertFalse([FIRApp validateAppID:@"1:1337:iosdeadbeef"]);
  333. // Wrong platform "android".
  334. XCTAssertFalse([FIRApp validateAppID:@"1:1337:android:deadbeef"]);
  335. // The fingerprint, aka 4th field, should only contain hex characters.
  336. XCTAssertFalse([FIRApp validateAppID:@"1:1337:ios:123abcxyz"]);
  337. // The fingerprint, aka 4th field, is not tested in V1, so a bad value shouldn't cause a failure.
  338. XCTAssertTrue([FIRApp validateAppID:@"1:1337:ios:deadbeef"]);
  339. }
  340. #pragma mark - App ID v2
  341. - (void)testAppIDV2 {
  342. // Missing separator between platform:fingerprint.
  343. XCTAssertTrue([FIRApp validateAppID:@"2:1337:ios5e18052ab54fbfec"]);
  344. // Unknown versions may contain anything.
  345. XCTAssertTrue([FIRApp validateAppID:@"2:1337:ios:123abcxyz"]);
  346. XCTAssertTrue([FIRApp validateAppID:@"2:thisdoesn'teven_m:a:t:t:e:r_"]);
  347. // Known good fingerprint.
  348. XCTAssertTrue([FIRApp validateAppID:@"2:1337:ios:5e18052ab54fbfec"]);
  349. // Unknown fingerprint, not tested so shouldn't cause a failure.
  350. XCTAssertTrue([FIRApp validateAppID:@"2:1337:ios:deadbeef"]);
  351. }
  352. #pragma mark - App ID other
  353. - (void)testAppIDV3 {
  354. // Currently there is no specification for v3, so we would not expect it to fail.
  355. XCTAssertTrue([FIRApp validateAppID:@"3:1337:ios:deadbeef"]);
  356. }
  357. - (void)testAppIDEmpty {
  358. XCTAssertFalse([FIRApp validateAppID:@""]);
  359. }
  360. - (void)testAppIDValidationTrue {
  361. // Ensure that isAppIDValid matches validateAppID.
  362. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"" GCMSenderID:@""];
  363. FIRApp *app = [[FIRApp alloc] initInstanceWithName:NSStringFromSelector(_cmd) options:options];
  364. OCMStub([self.appClassMock validateAppID:[OCMArg any]]).andReturn(YES);
  365. XCTAssertTrue([app isAppIDValid]);
  366. }
  367. - (void)testAppIDValidationFalse {
  368. // Ensure that isAppIDValid matches validateAppID.
  369. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"" GCMSenderID:@""];
  370. FIRApp *app = [[FIRApp alloc] initInstanceWithName:NSStringFromSelector(_cmd) options:options];
  371. OCMStub([self.appClassMock validateAppID:[OCMArg any]]).andReturn(NO);
  372. XCTAssertFalse([app isAppIDValid]);
  373. }
  374. - (void)testAppIDPrefix {
  375. // Unknown numeric-character prefixes should pass.
  376. XCTAssertTrue([FIRApp validateAppID:@"0:"]);
  377. XCTAssertTrue([FIRApp validateAppID:@"01:"]);
  378. XCTAssertTrue([FIRApp validateAppID:@"10:"]);
  379. XCTAssertTrue([FIRApp validateAppID:@"010:"]);
  380. XCTAssertTrue([FIRApp validateAppID:@"3:"]);
  381. XCTAssertTrue([FIRApp validateAppID:@"123:"]);
  382. XCTAssertTrue([FIRApp validateAppID:@"999999999:"]);
  383. // Non-numeric prefixes should not pass.
  384. XCTAssertFalse([FIRApp validateAppID:@"a:"]);
  385. XCTAssertFalse([FIRApp validateAppID:@"abcsdf0:"]);
  386. XCTAssertFalse([FIRApp validateAppID:@"0aaaa:"]);
  387. XCTAssertFalse([FIRApp validateAppID:@"0aaaa0450:"]);
  388. XCTAssertFalse([FIRApp validateAppID:@"-1:"]);
  389. XCTAssertFalse([FIRApp validateAppID:@"abcsdf:"]);
  390. XCTAssertFalse([FIRApp validateAppID:@"ABDCF:"]);
  391. XCTAssertFalse([FIRApp validateAppID:@" :"]);
  392. XCTAssertFalse([FIRApp validateAppID:@"1 :"]);
  393. XCTAssertFalse([FIRApp validateAppID:@" 1:"]);
  394. XCTAssertFalse([FIRApp validateAppID:@" 123 :"]);
  395. XCTAssertFalse([FIRApp validateAppID:@"1 23:"]);
  396. XCTAssertFalse([FIRApp validateAppID:@"&($*&%(*$&:"]);
  397. XCTAssertFalse([FIRApp validateAppID:@"abCDSF$%%df:"]);
  398. // Known version prefixes should never pass without the rest of the app ID string present.
  399. XCTAssertFalse([FIRApp validateAppID:@"1:"]);
  400. // Version must include ":".
  401. XCTAssertFalse([FIRApp validateAppID:@"0"]);
  402. XCTAssertFalse([FIRApp validateAppID:@"01"]);
  403. XCTAssertFalse([FIRApp validateAppID:@"10"]);
  404. XCTAssertFalse([FIRApp validateAppID:@"010"]);
  405. XCTAssertFalse([FIRApp validateAppID:@"3"]);
  406. XCTAssertFalse([FIRApp validateAppID:@"123"]);
  407. XCTAssertFalse([FIRApp validateAppID:@"999999999"]);
  408. XCTAssertFalse([FIRApp validateAppID:@"com.google.bundleID"]);
  409. }
  410. - (void)testAppIDFormatInvalid {
  411. OCMStub([self.appClassMock actualBundleID]).andReturn(@"com.google.bundleID");
  412. // Some direct tests of the validateAppIDFormat:withVersion: method.
  413. // Sanity checks first.
  414. NSString *const kGoodAppIDV1 = @"1:1337:ios:deadbeef";
  415. NSString *const kGoodVersionV1 = @"1";
  416. XCTAssertTrue([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:kGoodVersionV1]);
  417. NSString *const kGoodAppIDV2 = @"2:1337:ios:5e18052ab54fbfec";
  418. NSString *const kGoodVersionV2 = @"2";
  419. XCTAssertTrue([FIRApp validateAppIDFormat:kGoodAppIDV2 withVersion:kGoodVersionV2]);
  420. // Version mismatch.
  421. XCTAssertFalse([FIRApp validateAppIDFormat:kGoodAppIDV2 withVersion:kGoodVersionV1]);
  422. XCTAssertFalse([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:kGoodVersionV2]);
  423. XCTAssertFalse([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:@"999:"]);
  424. // Nil or empty strings.
  425. XCTAssertFalse([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:nil]);
  426. XCTAssertFalse([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:@""]);
  427. XCTAssertFalse([FIRApp validateAppIDFormat:nil withVersion:kGoodVersionV1]);
  428. XCTAssertFalse([FIRApp validateAppIDFormat:@"" withVersion:kGoodVersionV1]);
  429. XCTAssertFalse([FIRApp validateAppIDFormat:nil withVersion:nil]);
  430. XCTAssertFalse([FIRApp validateAppIDFormat:@"" withVersion:@""]);
  431. // App ID contains only the version prefix.
  432. XCTAssertFalse([FIRApp validateAppIDFormat:kGoodVersionV1 withVersion:kGoodVersionV1]);
  433. // The version is the entire app ID.
  434. XCTAssertFalse([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:kGoodAppIDV1]);
  435. // Versions digits that may make a partial match.
  436. XCTAssertFalse([FIRApp validateAppIDFormat:@"01:1337:ios:deadbeef" withVersion:kGoodVersionV1]);
  437. XCTAssertFalse([FIRApp validateAppIDFormat:@"10:1337:ios:deadbeef" withVersion:kGoodVersionV1]);
  438. XCTAssertFalse([FIRApp validateAppIDFormat:@"11:1337:ios:deadbeef" withVersion:kGoodVersionV1]);
  439. XCTAssertFalse([FIRApp validateAppIDFormat:@"21:1337:ios:5e18052ab54fbfec"
  440. withVersion:kGoodVersionV2]);
  441. XCTAssertFalse([FIRApp validateAppIDFormat:@"22:1337:ios:5e18052ab54fbfec"
  442. withVersion:kGoodVersionV2]);
  443. XCTAssertFalse([FIRApp validateAppIDFormat:@"02:1337:ios:5e18052ab54fbfec"
  444. withVersion:kGoodVersionV2]);
  445. XCTAssertFalse([FIRApp validateAppIDFormat:@"20:1337:ios:5e18052ab54fbfec"
  446. withVersion:kGoodVersionV2]);
  447. // Extra fields.
  448. XCTAssertFalse([FIRApp validateAppIDFormat:@"ab:1:1337:ios:deadbeef" withVersion:kGoodVersionV1]);
  449. XCTAssertFalse([FIRApp validateAppIDFormat:@"1:ab:1337:ios:deadbeef" withVersion:kGoodVersionV1]);
  450. XCTAssertFalse([FIRApp validateAppIDFormat:@"1:1337:ab:ios:deadbeef" withVersion:kGoodVersionV1]);
  451. XCTAssertFalse([FIRApp validateAppIDFormat:@"1:1337:ios:ab:deadbeef" withVersion:kGoodVersionV1]);
  452. XCTAssertFalse([FIRApp validateAppIDFormat:@"1:1337:ios:deadbeef:ab" withVersion:kGoodVersionV1]);
  453. }
  454. - (void)testAppIDFingerprintInvalid {
  455. OCMStub([self.appClassMock actualBundleID]).andReturn(@"com.google.bundleID");
  456. // Some direct tests of the validateAppIDFingerprint:withVersion: method.
  457. // Sanity checks first.
  458. NSString *const kGoodAppIDV1 = @"1:1337:ios:deadbeef";
  459. NSString *const kGoodVersionV1 = @"1";
  460. XCTAssertTrue([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:kGoodVersionV1]);
  461. NSString *const kGoodAppIDV2 = @"2:1337:ios:5e18052ab54fbfec";
  462. NSString *const kGoodVersionV2 = @"2";
  463. XCTAssertTrue([FIRApp validateAppIDFormat:kGoodAppIDV2 withVersion:kGoodVersionV2]);
  464. // Nil or empty strings.
  465. XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:nil]);
  466. XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:@""]);
  467. XCTAssertFalse([FIRApp validateAppIDFingerprint:nil withVersion:kGoodVersionV1]);
  468. XCTAssertFalse([FIRApp validateAppIDFingerprint:@"" withVersion:kGoodVersionV1]);
  469. XCTAssertFalse([FIRApp validateAppIDFingerprint:nil withVersion:nil]);
  470. XCTAssertFalse([FIRApp validateAppIDFingerprint:@"" withVersion:@""]);
  471. // App ID contains only the version prefix.
  472. XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodVersionV1 withVersion:kGoodVersionV1]);
  473. // The version is the entire app ID.
  474. XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:kGoodAppIDV1]);
  475. }
  476. // Uncomment if you need to measure performance of [FIRApp validateAppID:].
  477. // It is commented because measures are heavily dependent on a build agent configuration,
  478. // so it cannot produce reliable resault on CI
  479. //- (void)testAppIDFingerprintPerfomance {
  480. // [self measureBlock:^{
  481. // for (NSInteger i = 0; i < 100; ++i) {
  482. // [self testAppIDPrefix];
  483. // }
  484. // }];
  485. //}
  486. #pragma mark - Automatic Data Collection Tests
  487. - (void)testGlobalDataCollectionNoFlags {
  488. // Test: No flags set.
  489. NSString *name = NSStringFromSelector(_cmd);
  490. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  491. GCMSenderID:kGCMSenderID];
  492. FIRApp *app = [[FIRApp alloc] initInstanceWithName:name options:options];
  493. OCMStub([self.appClassMock readDataCollectionSwitchFromPlist]).andReturn(nil);
  494. OCMStub([self.appClassMock readDataCollectionSwitchFromUserDefaultsForApp:OCMOCK_ANY])
  495. .andReturn(nil);
  496. XCTAssertTrue(app.isDataCollectionDefaultEnabled);
  497. }
  498. - (void)testGlobalDataCollectionPlistSetEnabled {
  499. // Test: Plist set to enabled, no override.
  500. NSString *name = NSStringFromSelector(_cmd);
  501. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  502. GCMSenderID:kGCMSenderID];
  503. FIRApp *app = [[FIRApp alloc] initInstanceWithName:name options:options];
  504. OCMStub([self.appClassMock readDataCollectionSwitchFromPlist]).andReturn(@YES);
  505. OCMStub([self.appClassMock readDataCollectionSwitchFromUserDefaultsForApp:OCMOCK_ANY])
  506. .andReturn(nil);
  507. XCTAssertTrue(app.isDataCollectionDefaultEnabled);
  508. }
  509. - (void)testGlobalDataCollectionPlistSetDisabled {
  510. // Test: Plist set to disabled, no override.
  511. NSString *name = NSStringFromSelector(_cmd);
  512. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  513. GCMSenderID:kGCMSenderID];
  514. FIRApp *app = [[FIRApp alloc] initInstanceWithName:name options:options];
  515. OCMStub([self.appClassMock readDataCollectionSwitchFromPlist]).andReturn(@NO);
  516. OCMStub([self.appClassMock readDataCollectionSwitchFromUserDefaultsForApp:OCMOCK_ANY])
  517. .andReturn(nil);
  518. XCTAssertFalse(app.isDataCollectionDefaultEnabled);
  519. }
  520. - (void)testGlobalDataCollectionUserSpecifiedEnabled {
  521. // Test: User specified as enabled, no plist value.
  522. NSString *name = NSStringFromSelector(_cmd);
  523. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  524. GCMSenderID:kGCMSenderID];
  525. FIRApp *app = [[FIRApp alloc] initInstanceWithName:name options:options];
  526. OCMStub([self.appClassMock readDataCollectionSwitchFromPlist]).andReturn(nil);
  527. OCMStub([self.appClassMock readDataCollectionSwitchFromUserDefaultsForApp:OCMOCK_ANY])
  528. .andReturn(@YES);
  529. XCTAssertTrue(app.isDataCollectionDefaultEnabled);
  530. }
  531. - (void)testGlobalDataCollectionUserSpecifiedDisabled {
  532. // Test: User specified as disabled, no plist value.
  533. NSString *name = NSStringFromSelector(_cmd);
  534. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  535. GCMSenderID:kGCMSenderID];
  536. FIRApp *app = [[FIRApp alloc] initInstanceWithName:name options:options];
  537. OCMStub([self.appClassMock readDataCollectionSwitchFromPlist]).andReturn(nil);
  538. OCMStub([self.appClassMock readDataCollectionSwitchFromUserDefaultsForApp:OCMOCK_ANY])
  539. .andReturn(@NO);
  540. XCTAssertFalse(app.isDataCollectionDefaultEnabled);
  541. }
  542. - (void)testGlobalDataCollectionUserOverriddenEnabled {
  543. // Test: User specified as enabled, with plist set as disabled.
  544. NSString *name = NSStringFromSelector(_cmd);
  545. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  546. GCMSenderID:kGCMSenderID];
  547. FIRApp *app = [[FIRApp alloc] initInstanceWithName:name options:options];
  548. OCMStub([self.appClassMock readDataCollectionSwitchFromPlist]).andReturn(@NO);
  549. OCMStub([self.appClassMock readDataCollectionSwitchFromUserDefaultsForApp:OCMOCK_ANY])
  550. .andReturn(@YES);
  551. XCTAssertTrue(app.isDataCollectionDefaultEnabled);
  552. }
  553. - (void)testGlobalDataCollectionUserOverriddenDisabled {
  554. // Test: User specified as disabled, with plist set as enabled.
  555. NSString *name = NSStringFromSelector(_cmd);
  556. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  557. GCMSenderID:kGCMSenderID];
  558. FIRApp *app = [[FIRApp alloc] initInstanceWithName:name options:options];
  559. OCMStub([self.appClassMock readDataCollectionSwitchFromPlist]).andReturn(@YES);
  560. OCMStub([self.appClassMock readDataCollectionSwitchFromUserDefaultsForApp:OCMOCK_ANY])
  561. .andReturn(@NO);
  562. XCTAssertFalse(app.isDataCollectionDefaultEnabled);
  563. }
  564. - (void)testGlobalDataCollectionWriteToDefaults {
  565. id defaultsMock = OCMPartialMock([NSUserDefaults standardUserDefaults]);
  566. NSString *name = NSStringFromSelector(_cmd);
  567. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  568. GCMSenderID:kGCMSenderID];
  569. [FIRApp configureWithName:name options:options];
  570. FIRApp *app = [FIRApp appNamed:name];
  571. app.dataCollectionDefaultEnabled = YES;
  572. NSString *key =
  573. [NSString stringWithFormat:kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat, app.name];
  574. OCMVerify([defaultsMock setObject:@YES forKey:key]);
  575. app.dataCollectionDefaultEnabled = NO;
  576. OCMVerify([defaultsMock setObject:@NO forKey:key]);
  577. [defaultsMock stopMocking];
  578. }
  579. - (void)testGlobalDataCollectionClearedAfterDelete {
  580. // Configure and disable data collection for the default FIRApp.
  581. NSString *name = NSStringFromSelector(_cmd);
  582. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  583. GCMSenderID:kGCMSenderID];
  584. [FIRApp configureWithName:name options:options];
  585. FIRApp *app = [FIRApp appNamed:name];
  586. app.dataCollectionDefaultEnabled = NO;
  587. XCTAssertFalse(app.isDataCollectionDefaultEnabled);
  588. // Delete the app, and verify that the switch was reset.
  589. XCTestExpectation *deleteFinished =
  590. [self expectationWithDescription:@"The app should successfully delete."];
  591. [app deleteApp:^(BOOL success) {
  592. XCTAssertTrue(success);
  593. [deleteFinished fulfill];
  594. }];
  595. // Wait for the delete to complete.
  596. [self waitForExpectations:@[ deleteFinished ] timeout:1];
  597. // Set up an app with the same name again, and check the data collection flag.
  598. [FIRApp configureWithName:name options:options];
  599. XCTAssertTrue([FIRApp appNamed:name].isDataCollectionDefaultEnabled);
  600. }
  601. - (void)testGlobalDataCollectionNoDiagnosticsSent {
  602. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID
  603. GCMSenderID:kGCMSenderID];
  604. FIRApp *app = [[FIRApp alloc] initInstanceWithName:NSStringFromSelector(_cmd) options:options];
  605. app.dataCollectionDefaultEnabled = NO;
  606. // Stub out reading from user defaults since stubbing out the BOOL has issues. If the data
  607. // collection switch is disabled, the `sendLogs` call should return immediately and not fire a
  608. // notification.
  609. OCMStub([self.appClassMock readDataCollectionSwitchFromUserDefaultsForApp:OCMOCK_ANY])
  610. .andReturn(@NO);
  611. // Don't expect the diagnostics data to be sent.
  612. self.assertNoLogCoreTelemetry = YES;
  613. // The diagnostics data is expected to be sent on `UIApplicationDidBecomeActiveNotification` when
  614. // data collection is enabled.
  615. [FIRApp configure];
  616. [self.notificationCenter postNotificationName:[self appDidBecomeActiveNotificationName]
  617. object:nil];
  618. // Wait for some time because diagnostics is logged asynchronously.
  619. OCMVerifyAllWithDelay(self.mockCoreDiagnosticsConnector, 1);
  620. }
  621. #pragma mark - Analytics Flag Tests
  622. - (void)testAnalyticsSetByGlobalDataCollectionSwitch {
  623. // Test that the global data collection switch triggers setting Analytics when no explicit flag is
  624. // set.
  625. id optionsMock = OCMClassMock([FIROptions class]);
  626. OCMStub([optionsMock isAnalyticsCollectionExplicitlySet]).andReturn(NO);
  627. // We need to use the default app name since Analytics only associates with the default app.
  628. FIRApp *defaultApp = [[FIRApp alloc] initInstanceWithName:kFIRDefaultAppName options:optionsMock];
  629. id configurationMock = OCMClassMock([FIRAnalyticsConfiguration class]);
  630. OCMStub([configurationMock sharedInstance]).andReturn(configurationMock);
  631. // Ensure Analytics is set after the global flag is set. It needs to
  632. [defaultApp setDataCollectionDefaultEnabled:YES];
  633. OCMVerify([configurationMock setAnalyticsCollectionEnabled:YES persistSetting:NO]);
  634. [defaultApp setDataCollectionDefaultEnabled:NO];
  635. OCMVerify([configurationMock setAnalyticsCollectionEnabled:NO persistSetting:NO]);
  636. }
  637. - (void)testAnalyticsNotSetByGlobalDataCollectionSwitch {
  638. // Test that the global data collection switch doesn't override an explicitly set Analytics flag.
  639. id optionsMock = OCMClassMock([FIROptions class]);
  640. OCMStub([optionsMock isAnalyticsCollectionExplicitlySet]).andReturn(YES);
  641. FIRApp *app = [[FIRApp alloc] initInstanceWithName:@"testAnalyticsNotSet" options:optionsMock];
  642. id configurationMock = OCMClassMock([FIRAnalyticsConfiguration class]);
  643. OCMStub([configurationMock sharedInstance]).andReturn(configurationMock);
  644. // Reject any changes to Analytics when the data collection changes.
  645. OCMReject([configurationMock setAnalyticsCollectionEnabled:YES persistSetting:YES]);
  646. OCMReject([configurationMock setAnalyticsCollectionEnabled:YES persistSetting:NO]);
  647. [app setDataCollectionDefaultEnabled:YES];
  648. OCMReject([configurationMock setAnalyticsCollectionEnabled:NO persistSetting:YES]);
  649. OCMReject([configurationMock setAnalyticsCollectionEnabled:NO persistSetting:NO]);
  650. [app setDataCollectionDefaultEnabled:NO];
  651. }
  652. #pragma mark - Internal Methods
  653. - (void)testIsDefaultAppConfigured {
  654. // Ensure it's false before anything is configured.
  655. XCTAssertFalse([FIRApp isDefaultAppConfigured]);
  656. // Configure it and ensure it's configured.
  657. [FIRApp configure];
  658. XCTAssertTrue([FIRApp isDefaultAppConfigured]);
  659. // Reset the apps and ensure it's not configured anymore.
  660. [FIRApp resetApps];
  661. XCTAssertFalse([FIRApp isDefaultAppConfigured]);
  662. }
  663. - (void)testRegisterLibrary_InvalidLibraryName {
  664. NSString *originalFirebaseUserAgent = [FIRApp firebaseUserAgent];
  665. [FIRApp registerLibrary:@"Oops>" withVersion:@"1.0.0"];
  666. XCTAssertTrue([[FIRApp firebaseUserAgent] isEqualToString:originalFirebaseUserAgent]);
  667. }
  668. - (void)testRegisterLibrary_InvalidLibraryVersion {
  669. NSString *originalFirebaseUserAgent = [FIRApp firebaseUserAgent];
  670. [FIRApp registerLibrary:@"ValidName" withVersion:@"1.0.0+"];
  671. XCTAssertTrue([[FIRApp firebaseUserAgent] isEqualToString:originalFirebaseUserAgent]);
  672. }
  673. - (void)testRegisterLibrary_SingleLibrary {
  674. [FIRApp registerLibrary:@"ValidName" withVersion:@"1.0.0"];
  675. XCTAssertTrue([[FIRApp firebaseUserAgent] containsString:@"ValidName/1.0.0"]);
  676. }
  677. - (void)testRegisterLibrary_MultipleLibraries {
  678. [FIRApp registerLibrary:@"ValidName" withVersion:@"1.0.0"];
  679. [FIRApp registerLibrary:@"ValidName2" withVersion:@"2.0.0"];
  680. XCTAssertTrue([[FIRApp firebaseUserAgent] containsString:@"ValidName/1.0.0 ValidName2/2.0.0"]);
  681. }
  682. - (void)testRegisterLibrary_RegisteringConformingLibrary {
  683. Class testClass = [FIRTestClass class];
  684. [FIRApp registerInternalLibrary:testClass withName:@"ValidName" withVersion:@"1.0.0"];
  685. XCTAssertTrue([[FIRApp firebaseUserAgent] containsString:@"ValidName/1.0.0"]);
  686. }
  687. - (void)testRegisterLibrary_RegisteringNonConformingLibrary {
  688. XCTAssertThrows([FIRApp registerInternalLibrary:[NSString class]
  689. withName:@"InvalidLibrary"
  690. withVersion:@"1.0.0"]);
  691. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"InvalidLibrary`/1.0.0"]);
  692. }
  693. - (void)testFirebaseUserAgent_ApplePlatformFlag {
  694. // When a Catalyst app is run on macOS then both `TARGET_OS_MACCATALYST` and `TARGET_OS_IOS` are
  695. // `true`.
  696. #if TARGET_OS_MACCATALYST
  697. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/ios"]);
  698. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/tvos"]);
  699. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/macos"]);
  700. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/watchos"]);
  701. XCTAssertTrue([[FIRApp firebaseUserAgent] containsString:@"apple-platform/maccatalyst"]);
  702. #elif TARGET_OS_IOS
  703. XCTAssertTrue([[FIRApp firebaseUserAgent] containsString:@"apple-platform/ios"]);
  704. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/tvos"]);
  705. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/macos"]);
  706. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/watchos"]);
  707. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/maccatalyst"]);
  708. #endif // TARGET_OS_MACCATALYST
  709. #if TARGET_OS_TV
  710. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/ios"]);
  711. XCTAssertTrue([[FIRApp firebaseUserAgent] containsString:@"apple-platform/tvos"]);
  712. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/macos"]);
  713. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/watchos"]);
  714. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/maccatalyst"]);
  715. #endif // TARGET_OS_TV
  716. #if TARGET_OS_OSX
  717. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/ios"]);
  718. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/tvos"]);
  719. XCTAssertTrue([[FIRApp firebaseUserAgent] containsString:@"apple-platform/macos"]);
  720. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/watchos"]);
  721. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/maccatalyst"]);
  722. #endif // TARGET_OS_OSX
  723. #if TARGET_OS_WATCH
  724. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/ios"]);
  725. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/tvos"]);
  726. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/macos"]);
  727. XCTAssertTrue([[FIRApp firebaseUserAgent] containsString:@"apple-platform/watchos"]);
  728. XCTAssertFalse([[FIRApp firebaseUserAgent] containsString:@"apple-platform/maccatalyst"]);
  729. #endif // TARGET_OS_WATCH
  730. }
  731. - (void)testFirebaseUserAgent_DeploymentType {
  732. #if SWIFT_PACKAGE
  733. NSString *deploymentType = @"swiftpm";
  734. #elif FIREBASE_BUILD_CARTHAGE
  735. NSString *deploymentType = @"carthage";
  736. #elif FIREBASE_BUILD_ZIP_FILE
  737. NSString *deploymentType = @"zip";
  738. #else
  739. NSString *deploymentType = @"cocoapods";
  740. #endif
  741. NSString *expectedComponent = [NSString stringWithFormat:@"deploy/%@", deploymentType];
  742. XCTAssertTrue([[FIRApp firebaseUserAgent] containsString:expectedComponent]);
  743. }
  744. - (void)testFirebaseUserAgent_DeviceModel {
  745. NSString *expectedComponent =
  746. [NSString stringWithFormat:@"device/%@", [GULAppEnvironmentUtil deviceModel]];
  747. XCTAssertTrue([[FIRApp firebaseUserAgent] containsString:expectedComponent]);
  748. }
  749. - (void)testFirebaseUserAgent_OSVersion {
  750. NSString *expectedComponent =
  751. [NSString stringWithFormat:@"os-version/%@", [GULAppEnvironmentUtil systemVersion]];
  752. XCTAssertTrue([[FIRApp firebaseUserAgent] containsString:expectedComponent]);
  753. }
  754. - (void)testFirebaseUserAgent_IsFromAppStore {
  755. NSString *appStoreValue = [GULAppEnvironmentUtil isFromAppStore] ? @"true" : @"false";
  756. NSString *expectedComponent = [NSString stringWithFormat:@"appstore/%@", appStoreValue];
  757. XCTAssertTrue([[FIRApp firebaseUserAgent] containsString:expectedComponent]);
  758. }
  759. #pragma mark - Core Diagnostics
  760. - (void)testCoreDiagnosticsLoggedWhenAppDidBecomeActive {
  761. FIRApp *app = [self createConfiguredAppWithName:NSStringFromSelector(_cmd)];
  762. [self expectCoreDiagnosticsDataLogWithOptions:app.options];
  763. [self.notificationCenter postNotificationName:[self appDidBecomeActiveNotificationName]
  764. object:nil];
  765. OCMVerifyAllWithDelay(self.mockCoreDiagnosticsConnector, 0.5);
  766. }
  767. #pragma mark - private
  768. - (XCTestExpectation *)expectNotificationNamed:(NSNotificationName)name
  769. object:(nullable id)object
  770. userInfo:(NSDictionary *)userInfo {
  771. XCTestExpectation *notificationExpectation =
  772. [self expectationForNotification:name
  773. object:object
  774. notificationCenter:self.notificationCenter
  775. handler:^BOOL(NSNotification *_Nonnull notification) {
  776. return [userInfo isEqualToDictionary:notification.userInfo];
  777. }];
  778. return notificationExpectation;
  779. }
  780. - (NSDictionary<NSString *, NSObject *> *)expectedUserInfoWithAppName:(NSString *)name
  781. isDefaultApp:(BOOL)isDefaultApp {
  782. return @{
  783. kFIRAppNameKey : name,
  784. kFIRAppIsDefaultAppKey : [NSNumber numberWithBool:isDefaultApp],
  785. kFIRGoogleAppIDKey : kGoogleAppID
  786. };
  787. }
  788. - (void)expectCoreDiagnosticsDataLogWithOptions:(nullable FIROptions *)expectedOptions {
  789. [self.mockCoreDiagnosticsConnector stopMocking];
  790. self.mockCoreDiagnosticsConnector = nil;
  791. self.mockCoreDiagnosticsConnector = OCMClassMock([FIRCoreDiagnosticsConnector class]);
  792. OCMExpect(ClassMethod([self.mockCoreDiagnosticsConnector
  793. logCoreTelemetryWithOptions:[OCMArg checkWithBlock:^BOOL(FIROptions *options) {
  794. if (!expectedOptions) {
  795. return YES;
  796. }
  797. return [options.googleAppID isEqualToString:expectedOptions.googleAppID] &&
  798. [options.GCMSenderID isEqualToString:expectedOptions.GCMSenderID];
  799. }]]));
  800. }
  801. - (NSNotificationName)appDidBecomeActiveNotificationName {
  802. #if TARGET_OS_IOS || TARGET_OS_TV
  803. return UIApplicationDidBecomeActiveNotification;
  804. #endif
  805. #if TARGET_OS_OSX
  806. return NSApplicationDidBecomeActiveNotification;
  807. #endif
  808. }
  809. - (FIRApp *)createConfiguredAppWithName:(NSString *)name {
  810. FIROptions *options = [self appOptions];
  811. [FIRApp configureWithName:name options:options];
  812. return [FIRApp appNamed:name];
  813. }
  814. - (FIROptions *)appOptions {
  815. return [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID];
  816. }
  817. - (void)registerLibrariesWithClasses:(NSArray<Class> *)classes {
  818. for (Class klass in classes) {
  819. [FIRApp registerInternalLibrary:klass withName:NSStringFromClass(klass) withVersion:@"1.0"];
  820. }
  821. }
  822. @end