FIRAppTest.m 50 KB

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