FIROptionsTest.m 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  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/Extension/FIRAppInternal.h"
  16. #import "FirebaseCore/Extension/FIROptionsInternal.h"
  17. #import "FirebaseCore/Sources/FIRBundleUtil.h"
  18. #import "FirebaseCore/Sources/Public/FirebaseCore/FIRVersion.h"
  19. #import "SharedTestUtilities/FIROptionsMock.h"
  20. extern NSString *const kFIRIsMeasurementEnabled;
  21. extern NSString *const kFIRIsAnalyticsCollectionEnabled;
  22. extern NSString *const kFIRIsAnalyticsCollectionDeactivated;
  23. extern NSString *const kFIRLibraryVersionID;
  24. @interface FIROptions (Test)
  25. - (nullable NSDictionary *)analyticsOptionsDictionaryWithInfoDictionary:
  26. (nullable NSDictionary *)infoDictionary;
  27. @end
  28. @interface FIROptionsTest : FIRTestCase
  29. @end
  30. @implementation FIROptionsTest
  31. - (void)setUp {
  32. [super setUp];
  33. [FIROptions resetDefaultOptions];
  34. }
  35. - (void)testInit {
  36. [FIROptionsMock mockFIROptions];
  37. NSDictionary *optionsDictionary = [FIROptions defaultOptionsDictionary];
  38. FIROptions *options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
  39. [self assertOptionsMatchDefaults:options];
  40. XCTAssertNil(options.deepLinkURLScheme);
  41. XCTAssertTrue(options.usingOptionsFromDefaultPlist);
  42. options.deepLinkURLScheme = kDeepLinkURLScheme;
  43. XCTAssertEqualObjects(options.deepLinkURLScheme, kDeepLinkURLScheme);
  44. }
  45. - (void)testDefaultOptionsDictionaryWithNilFilePath {
  46. id mockBundleUtil = OCMClassMock([FIRBundleUtil class]);
  47. OCMStub([mockBundleUtil optionsDictionaryPathWithResourceName:kJsonFileName
  48. andFileType:kJsonFileType
  49. inBundles:[FIRBundleUtil relevantBundles]])
  50. .andReturn(nil);
  51. XCTAssertNil([FIROptions defaultOptionsDictionary]);
  52. }
  53. - (void)testDefaultOptionsDictionaryWithInvalidSourceFile {
  54. id mockBundleUtil = OCMClassMock([FIRBundleUtil class]);
  55. OCMStub([mockBundleUtil optionsDictionaryPathWithResourceName:kJsonFileName
  56. andFileType:kJsonFileType
  57. inBundles:[FIRBundleUtil relevantBundles]])
  58. .andReturn(@"invalid.plist");
  59. XCTAssertNil([FIROptions defaultOptionsDictionary]);
  60. }
  61. - (void)testDefaultOptions {
  62. [FIROptionsMock mockFIROptions];
  63. FIROptions *options = [FIROptions defaultOptions];
  64. [self assertOptionsMatchDefaults:options];
  65. XCTAssertNil(options.deepLinkURLScheme);
  66. XCTAssertTrue(options.usingOptionsFromDefaultPlist);
  67. options.deepLinkURLScheme = kDeepLinkURLScheme;
  68. XCTAssertEqualObjects(options.deepLinkURLScheme, kDeepLinkURLScheme);
  69. }
  70. #ifndef SWIFT_PACKAGE
  71. // Another strategy is needed for these tests to pass with SWIFT_PACKAGE, since the mocking
  72. // prevents the file path traversals.
  73. - (void)testDefaultOptionsAreInitializedOncePlist {
  74. id mockBundleUtil = OCMClassMock([FIRBundleUtil class]);
  75. OCMExpect([mockBundleUtil optionsDictionaryPathWithResourceName:kServiceInfoFileName
  76. andFileType:kServiceInfoFileType
  77. inBundles:[FIRBundleUtil relevantBundles]])
  78. .andReturn([self validGoogleServicesInfoPlistPath]);
  79. XCTAssertNotNil([FIROptions defaultOptions]);
  80. OCMVerifyAll(mockBundleUtil);
  81. OCMReject([mockBundleUtil optionsDictionaryPathWithResourceName:OCMOCK_ANY
  82. andFileType:OCMOCK_ANY
  83. inBundles:OCMOCK_ANY]);
  84. XCTAssertNotNil([FIROptions defaultOptions]);
  85. OCMVerifyAll(mockBundleUtil);
  86. }
  87. - (void)testDefaultOptionsAreInitializedOnceJson {
  88. id mockBundleUtil = OCMClassMock([FIRBundleUtil class]);
  89. OCMExpect([mockBundleUtil optionsDictionaryPathWithResourceName:kJsonFileName
  90. andFileType:kJsonFileType
  91. inBundles:[FIRBundleUtil relevantBundles]])
  92. .andReturn([self validJsonConfigPath]);
  93. XCTAssertNotNil([FIROptions defaultOptions]);
  94. OCMVerifyAll(mockBundleUtil);
  95. OCMReject([mockBundleUtil optionsDictionaryPathWithResourceName:OCMOCK_ANY
  96. andFileType:OCMOCK_ANY
  97. inBundles:OCMOCK_ANY]);
  98. XCTAssertNotNil([FIROptions defaultOptions]);
  99. OCMVerifyAll(mockBundleUtil);
  100. }
  101. - (void)testDefaultOptionsDictionaryIsInitializedOncePlist {
  102. id mockBundleUtil = OCMClassMock([FIRBundleUtil class]);
  103. OCMExpect([mockBundleUtil optionsDictionaryPathWithResourceName:kServiceInfoFileName
  104. andFileType:kServiceInfoFileType
  105. inBundles:[FIRBundleUtil relevantBundles]])
  106. .andReturn([self validGoogleServicesInfoPlistPath]);
  107. XCTAssertNotNil([FIROptions defaultOptionsDictionary]);
  108. OCMVerifyAll(mockBundleUtil);
  109. OCMReject([mockBundleUtil optionsDictionaryPathWithResourceName:OCMOCK_ANY
  110. andFileType:OCMOCK_ANY
  111. inBundles:OCMOCK_ANY]);
  112. XCTAssertNotNil([FIROptions defaultOptionsDictionary]);
  113. OCMVerifyAll(mockBundleUtil);
  114. }
  115. - (void)testDefaultOptionsDictionaryIsInitializedOnceJson {
  116. id mockBundleUtil = OCMClassMock([FIRBundleUtil class]);
  117. OCMExpect([mockBundleUtil optionsDictionaryPathWithResourceName:kJsonFileName
  118. andFileType:kJsonFileType
  119. inBundles:[FIRBundleUtil relevantBundles]])
  120. .andReturn([self validJsonConfigPath]);
  121. XCTAssertNotNil([FIROptions defaultOptionsDictionary]);
  122. OCMVerifyAll(mockBundleUtil);
  123. OCMReject([mockBundleUtil optionsDictionaryPathWithResourceName:OCMOCK_ANY
  124. andFileType:OCMOCK_ANY
  125. inBundles:OCMOCK_ANY]);
  126. XCTAssertNotNil([FIROptions defaultOptionsDictionary]);
  127. OCMVerifyAll(mockBundleUtil);
  128. }
  129. - (void)testInitWithContentsOfPlistFile {
  130. NSString *filePath = [self validGoogleServicesInfoPlistPath];
  131. FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath];
  132. [self assertOptionsMatchDefaults:options];
  133. XCTAssertNil(options.deepLinkURLScheme);
  134. XCTAssertFalse(options.usingOptionsFromDefaultPlist);
  135. #pragma clang diagnostic push
  136. #pragma clang diagnostic ignored "-Wnonnull"
  137. FIROptions *emptyOptions = [[FIROptions alloc] initWithContentsOfFile:nil];
  138. #pragma clang diagnostic pop
  139. XCTAssertNil(emptyOptions);
  140. FIROptions *invalidOptions = [[FIROptions alloc] initWithContentsOfFile:@"invalid.plist"];
  141. XCTAssertNil(invalidOptions);
  142. }
  143. - (void)testInitWithContentsOfJsonFile {
  144. NSString *filePath = [self validJsonConfigPath];
  145. FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath];
  146. [self assertOptionsMatchDefaults:options];
  147. XCTAssertNil(options.deepLinkURLScheme);
  148. XCTAssertFalse(options.usingOptionsFromDefaultPlist);
  149. #pragma clang diagnostic push
  150. #pragma clang diagnostic ignored "-Wnonnull"
  151. FIROptions *emptyOptions = [[FIROptions alloc] initWithContentsOfFile:nil];
  152. #pragma clang diagnostic pop
  153. XCTAssertNil(emptyOptions);
  154. FIROptions *invalidOptions = [[FIROptions alloc] initWithContentsOfFile:@"invalid.json"];
  155. XCTAssertNil(invalidOptions);
  156. }
  157. #endif
  158. - (void)testInitCustomizedOptionsPlist {
  159. FIROptions *options = [self oldAppOptions];
  160. options.APIKey = kAPIKey;
  161. options.bundleID = kBundleID;
  162. options.clientID = kClientID;
  163. options.databaseURL = kDatabaseURL;
  164. options.deepLinkURLScheme = kDeepLinkURLScheme;
  165. options.projectID = kProjectID;
  166. options.storageBucket = kStorageBucket;
  167. [self assertOptionsMatchDefaults:options];
  168. XCTAssertEqualObjects(options.deepLinkURLScheme, kDeepLinkURLScheme);
  169. XCTAssertFalse(options.usingOptionsFromDefaultPlist);
  170. }
  171. - (void)testInitCustomizedOptionsJson {
  172. FIROptions *options = [self appOptions];
  173. options.bundleID = kBundleID;
  174. options.clientID = kClientID;
  175. options.databaseURL = kDatabaseURL;
  176. options.deepLinkURLScheme = kDeepLinkURLScheme;
  177. options.storageBucket = kStorageBucket;
  178. [self assertOptionsMatchDefaults:options];
  179. XCTAssertEqualObjects(options.deepLinkURLScheme, kDeepLinkURLScheme);
  180. XCTAssertFalse(options.usingOptionsFromDefaultPlist);
  181. }
  182. - (void)assertOptionsMatchDefaults:(FIROptions *)options {
  183. XCTAssertEqualObjects(options.googleAppID, kGoogleAppID);
  184. XCTAssertEqualObjects(options.APIKey, kAPIKey);
  185. XCTAssertEqualObjects(options.clientID, kClientID);
  186. XCTAssertEqualObjects(options.GCMSenderID, kProjectNumber);
  187. XCTAssertEqualObjects(options.libraryVersionID, kFIRLibraryVersionID);
  188. XCTAssertEqualObjects(options.databaseURL, kDatabaseURL);
  189. XCTAssertEqualObjects(options.storageBucket, kStorageBucket);
  190. XCTAssertEqualObjects(options.bundleID, kBundleID);
  191. XCTAssertEqualObjects(options.projectID, kProjectID);
  192. }
  193. - (void)testCopyingPropertiesPlist {
  194. NSMutableString *mutableString;
  195. FIROptions *options = [self appOptions];
  196. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  197. options.APIKey = mutableString;
  198. [mutableString appendString:@"2"];
  199. XCTAssertEqualObjects(options.APIKey, @"1");
  200. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  201. options.bundleID = mutableString;
  202. [mutableString appendString:@"2"];
  203. XCTAssertEqualObjects(options.bundleID, @"1");
  204. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  205. options.clientID = mutableString;
  206. [mutableString appendString:@"2"];
  207. XCTAssertEqualObjects(options.clientID, @"1");
  208. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  209. options.GCMSenderID = mutableString;
  210. [mutableString appendString:@"2"];
  211. XCTAssertEqualObjects(options.GCMSenderID, @"1");
  212. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  213. options.projectID = mutableString;
  214. [mutableString appendString:@"2"];
  215. XCTAssertEqualObjects(options.projectID, @"1");
  216. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  217. options.googleAppID = mutableString;
  218. [mutableString appendString:@"2"];
  219. XCTAssertEqualObjects(options.googleAppID, @"1");
  220. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  221. options.databaseURL = mutableString;
  222. [mutableString appendString:@"2"];
  223. XCTAssertEqualObjects(options.databaseURL, @"1");
  224. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  225. options.deepLinkURLScheme = mutableString;
  226. [mutableString appendString:@"2"];
  227. XCTAssertEqualObjects(options.deepLinkURLScheme, @"1");
  228. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  229. options.storageBucket = mutableString;
  230. [mutableString appendString:@"2"];
  231. XCTAssertEqualObjects(options.storageBucket, @"1");
  232. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  233. options.appGroupID = mutableString;
  234. [mutableString appendString:@"2"];
  235. XCTAssertEqualObjects(options.appGroupID, @"1");
  236. }
  237. - (void)testCopyingPropertiesJSON {
  238. NSMutableString *mutableString;
  239. FIROptions *options = [self appOptions];
  240. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  241. options.APIKey = mutableString;
  242. [mutableString appendString:@"2"];
  243. XCTAssertEqualObjects(options.APIKey, @"1");
  244. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  245. options.bundleID = mutableString;
  246. [mutableString appendString:@"2"];
  247. XCTAssertEqualObjects(options.bundleID, @"1");
  248. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  249. options.clientID = mutableString;
  250. [mutableString appendString:@"2"];
  251. XCTAssertEqualObjects(options.clientID, @"1");
  252. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  253. options.GCMSenderID = mutableString;
  254. [mutableString appendString:@"2"];
  255. XCTAssertEqualObjects(options.GCMSenderID, @"1");
  256. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  257. options.projectID = mutableString;
  258. [mutableString appendString:@"2"];
  259. XCTAssertEqualObjects(options.projectID, @"1");
  260. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  261. options.googleAppID = mutableString;
  262. [mutableString appendString:@"2"];
  263. XCTAssertEqualObjects(options.googleAppID, @"1");
  264. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  265. options.databaseURL = mutableString;
  266. [mutableString appendString:@"2"];
  267. XCTAssertEqualObjects(options.databaseURL, @"1");
  268. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  269. options.deepLinkURLScheme = mutableString;
  270. [mutableString appendString:@"2"];
  271. XCTAssertEqualObjects(options.deepLinkURLScheme, @"1");
  272. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  273. options.storageBucket = mutableString;
  274. [mutableString appendString:@"2"];
  275. XCTAssertEqualObjects(options.storageBucket, @"1");
  276. mutableString = [[NSMutableString alloc] initWithString:@"1"];
  277. options.appGroupID = mutableString;
  278. [mutableString appendString:@"2"];
  279. XCTAssertEqualObjects(options.appGroupID, @"1");
  280. }
  281. - (void)testCopyWithZone {
  282. [FIROptionsMock mockFIROptions];
  283. // default options
  284. FIROptions *options = [FIROptions defaultOptions];
  285. options.deepLinkURLScheme = kDeepLinkURLScheme;
  286. XCTAssertEqualObjects(options.deepLinkURLScheme, kDeepLinkURLScheme);
  287. FIROptions *newOptions = [options copy];
  288. XCTAssertEqualObjects(newOptions.deepLinkURLScheme, kDeepLinkURLScheme);
  289. [options setDeepLinkURLScheme:kNewDeepLinkURLScheme];
  290. XCTAssertEqualObjects(options.deepLinkURLScheme, kNewDeepLinkURLScheme);
  291. XCTAssertEqualObjects(newOptions.deepLinkURLScheme, kDeepLinkURLScheme);
  292. // customized options with plist
  293. FIROptions *customizedOptions = [self oldAppOptions];
  294. customizedOptions.deepLinkURLScheme = kDeepLinkURLScheme;
  295. FIROptions *copyCustomizedOptions = [customizedOptions copy];
  296. [copyCustomizedOptions setDeepLinkURLScheme:kNewDeepLinkURLScheme];
  297. XCTAssertEqualObjects(customizedOptions.deepLinkURLScheme, kDeepLinkURLScheme);
  298. XCTAssertEqualObjects(copyCustomizedOptions.deepLinkURLScheme, kNewDeepLinkURLScheme);
  299. // customized options with json
  300. FIROptions *customizedOptionsJSON = [self appOptions];
  301. customizedOptionsJSON.deepLinkURLScheme = kDeepLinkURLScheme;
  302. FIROptions *copyCustomizedOptionsJSON = [customizedOptionsJSON copy];
  303. [copyCustomizedOptionsJSON setDeepLinkURLScheme:kNewDeepLinkURLScheme];
  304. XCTAssertEqualObjects(customizedOptionsJSON.deepLinkURLScheme, kDeepLinkURLScheme);
  305. XCTAssertEqualObjects(copyCustomizedOptionsJSON.deepLinkURLScheme, kNewDeepLinkURLScheme);
  306. }
  307. - (void)testAnalyticsConstants {
  308. // The keys are public values and should never change.
  309. XCTAssertEqualObjects(kFIRIsMeasurementEnabled, @"IS_MEASUREMENT_ENABLED");
  310. XCTAssertEqualObjects(kFIRIsAnalyticsCollectionEnabled, @"FIREBASE_ANALYTICS_COLLECTION_ENABLED");
  311. XCTAssertEqualObjects(kFIRIsAnalyticsCollectionDeactivated,
  312. @"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED");
  313. }
  314. - (void)testAnalyticsOptions {
  315. // No keys anywhere.
  316. NSDictionary *optionsDictionary = nil;
  317. FIROptions *options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
  318. NSDictionary *mainDictionary = nil;
  319. NSDictionary *expectedAnalyticsOptions = @{};
  320. NSDictionary *analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:nil];
  321. XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions);
  322. optionsDictionary = @{};
  323. options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
  324. mainDictionary = @{};
  325. expectedAnalyticsOptions = @{};
  326. analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary];
  327. XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions);
  328. // Main has no keys.
  329. optionsDictionary = @{
  330. kFIRIsAnalyticsCollectionDeactivated : @YES,
  331. kFIRIsAnalyticsCollectionEnabled : @YES,
  332. kFIRIsMeasurementEnabled : @YES
  333. };
  334. options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
  335. mainDictionary = @{};
  336. expectedAnalyticsOptions = optionsDictionary;
  337. analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary];
  338. XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions);
  339. // Main overrides all the keys.
  340. optionsDictionary = @{
  341. kFIRIsAnalyticsCollectionDeactivated : @YES,
  342. kFIRIsAnalyticsCollectionEnabled : @YES,
  343. kFIRIsMeasurementEnabled : @YES
  344. };
  345. options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
  346. mainDictionary = @{
  347. kFIRIsAnalyticsCollectionDeactivated : @NO,
  348. kFIRIsAnalyticsCollectionEnabled : @NO,
  349. kFIRIsMeasurementEnabled : @NO
  350. };
  351. expectedAnalyticsOptions = mainDictionary;
  352. analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary];
  353. XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions);
  354. // Keys exist only in main.
  355. optionsDictionary = @{};
  356. options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
  357. mainDictionary = @{
  358. kFIRIsAnalyticsCollectionDeactivated : @YES,
  359. kFIRIsAnalyticsCollectionEnabled : @YES,
  360. kFIRIsMeasurementEnabled : @YES
  361. };
  362. expectedAnalyticsOptions = mainDictionary;
  363. analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary];
  364. XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions);
  365. // Main overrides single keys.
  366. optionsDictionary = @{
  367. kFIRIsAnalyticsCollectionDeactivated : @YES,
  368. kFIRIsAnalyticsCollectionEnabled : @YES,
  369. kFIRIsMeasurementEnabled : @YES
  370. };
  371. options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
  372. mainDictionary = @{kFIRIsAnalyticsCollectionDeactivated : @NO};
  373. expectedAnalyticsOptions = @{
  374. kFIRIsAnalyticsCollectionDeactivated : @NO, // override
  375. kFIRIsAnalyticsCollectionEnabled : @YES,
  376. kFIRIsMeasurementEnabled : @YES
  377. };
  378. analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary];
  379. XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions);
  380. optionsDictionary = @{
  381. kFIRIsAnalyticsCollectionDeactivated : @YES,
  382. kFIRIsAnalyticsCollectionEnabled : @YES,
  383. kFIRIsMeasurementEnabled : @YES
  384. };
  385. options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
  386. mainDictionary = @{kFIRIsAnalyticsCollectionEnabled : @NO};
  387. expectedAnalyticsOptions = @{
  388. kFIRIsAnalyticsCollectionDeactivated : @YES,
  389. kFIRIsAnalyticsCollectionEnabled : @NO, // override
  390. kFIRIsMeasurementEnabled : @YES
  391. };
  392. analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary];
  393. XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions);
  394. optionsDictionary = @{
  395. kFIRIsAnalyticsCollectionDeactivated : @YES,
  396. kFIRIsAnalyticsCollectionEnabled : @YES,
  397. kFIRIsMeasurementEnabled : @YES
  398. };
  399. options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
  400. mainDictionary = @{kFIRIsMeasurementEnabled : @NO};
  401. expectedAnalyticsOptions = @{
  402. kFIRIsAnalyticsCollectionDeactivated : @YES,
  403. kFIRIsAnalyticsCollectionEnabled : @YES,
  404. kFIRIsMeasurementEnabled : @NO // override
  405. };
  406. analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary];
  407. XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions);
  408. }
  409. - (void)testAnalyticsOptions_combinatorial {
  410. // Complete combinatorial test.
  411. // Possible values for the flags in the plist, where NSNull means the flag is not present.
  412. NSArray *values = @[ [NSNull null], @NO, @YES ];
  413. // Sanity checks for the combination generation.
  414. int combinationCount = 0;
  415. NSMutableArray *uniqueMainCombinations = [[NSMutableArray alloc] init];
  416. NSMutableArray *uniqueOptionsCombinations = [[NSMutableArray alloc] init];
  417. // Generate all optout flag combinations for { main plist X GoogleService-info options plist }.
  418. // Options present in the main plist should override options of the same key in the service plist.
  419. for (id mainDeactivated in values) {
  420. for (id mainAnalyticsEnabled in values) {
  421. for (id mainMeasurementEnabled in values) {
  422. for (id optionsDeactivated in values) {
  423. for (id optionsAnalyticsEnabled in values) {
  424. for (id optionsMeasurementEnabled in values) {
  425. @autoreleasepool {
  426. // Fill the GoogleService-info options plist dictionary.
  427. NSMutableDictionary *optionsDictionary = [[NSMutableDictionary alloc] init];
  428. if (![optionsDeactivated isEqual:[NSNull null]]) {
  429. optionsDictionary[kFIRIsAnalyticsCollectionDeactivated] = optionsDeactivated;
  430. }
  431. if (![optionsAnalyticsEnabled isEqual:[NSNull null]]) {
  432. optionsDictionary[kFIRIsAnalyticsCollectionEnabled] = optionsAnalyticsEnabled;
  433. }
  434. if (![optionsMeasurementEnabled isEqual:[NSNull null]]) {
  435. optionsDictionary[kFIRIsMeasurementEnabled] = optionsMeasurementEnabled;
  436. }
  437. FIROptions *options =
  438. [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
  439. if (![uniqueOptionsCombinations containsObject:optionsDictionary]) {
  440. [uniqueOptionsCombinations addObject:optionsDictionary];
  441. }
  442. // Fill the main plist dictionary.
  443. NSMutableDictionary *mainDictionary = [[NSMutableDictionary alloc] init];
  444. if (![mainDeactivated isEqual:[NSNull null]]) {
  445. mainDictionary[kFIRIsAnalyticsCollectionDeactivated] = mainDeactivated;
  446. }
  447. if (![mainAnalyticsEnabled isEqual:[NSNull null]]) {
  448. mainDictionary[kFIRIsAnalyticsCollectionEnabled] = mainAnalyticsEnabled;
  449. }
  450. if (![mainMeasurementEnabled isEqual:[NSNull null]]) {
  451. mainDictionary[kFIRIsMeasurementEnabled] = mainMeasurementEnabled;
  452. }
  453. // Add mainDictionary to uniqueMainCombinations if it isn't included yet.
  454. if (![uniqueMainCombinations containsObject:mainDictionary]) {
  455. [uniqueMainCombinations addObject:mainDictionary];
  456. }
  457. // Generate the expected options by adding main values on top of the service options
  458. // values. The main values will replace any existing options values with the same
  459. // key. This is a different way of combining the two sets of flags from the actual
  460. // implementation in FIROptions, with equivalent output.
  461. NSMutableDictionary *expectedAnalyticsOptions =
  462. [[NSMutableDictionary alloc] initWithDictionary:optionsDictionary];
  463. [expectedAnalyticsOptions addEntriesFromDictionary:mainDictionary];
  464. NSDictionary *analyticsOptions =
  465. [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary];
  466. XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions);
  467. combinationCount++;
  468. }
  469. }
  470. }
  471. }
  472. }
  473. }
  474. }
  475. // Verify the sanity checks.
  476. XCTAssertEqual(combinationCount, 729); // = 3^6.
  477. XCTAssertEqual(uniqueOptionsCombinations.count, 27);
  478. int optionsSizeCount[4] = {0};
  479. for (NSDictionary *dictionary in uniqueOptionsCombinations) {
  480. optionsSizeCount[dictionary.count]++;
  481. }
  482. XCTAssertEqual(optionsSizeCount[0], 1);
  483. XCTAssertEqual(optionsSizeCount[1], 6);
  484. XCTAssertEqual(optionsSizeCount[2], 12);
  485. XCTAssertEqual(optionsSizeCount[3], 8);
  486. XCTAssertEqual(uniqueMainCombinations.count, 27);
  487. int mainSizeCount[4] = {0};
  488. for (NSDictionary *dictionary in uniqueMainCombinations) {
  489. mainSizeCount[dictionary.count]++;
  490. }
  491. XCTAssertEqual(mainSizeCount[0], 1);
  492. XCTAssertEqual(mainSizeCount[1], 6);
  493. XCTAssertEqual(mainSizeCount[2], 12);
  494. XCTAssertEqual(mainSizeCount[3], 8);
  495. }
  496. - (void)testAnalyticsCollectionGlobalSwitchEnabled {
  497. // Stub the default app, and set the global switch to YES.
  498. id appMock = OCMClassMock([FIRApp class]);
  499. OCMStub([appMock isDefaultAppConfigured]).andReturn(YES);
  500. OCMStub([appMock defaultApp]).andReturn(appMock);
  501. OCMStub([appMock isDataCollectionDefaultEnabled]).andReturn(YES);
  502. // With no other settings, Analytics collection should default to the app's flag.
  503. FIROptions *options = [[FIROptions alloc] initInternalWithOptionsDictionary:@{}];
  504. XCTAssertTrue(options.isAnalyticsCollectionEnabled);
  505. XCTAssertTrue(options.isMeasurementEnabled);
  506. [appMock stopMocking];
  507. }
  508. - (void)testAnalyticsCollectionGlobalSwitchDisabled {
  509. // Stub the default app, and set the global switch to NO.
  510. id appMock = OCMClassMock([FIRApp class]);
  511. OCMStub([appMock isDefaultAppConfigured]).andReturn(YES);
  512. OCMStub([appMock defaultApp]).andReturn(appMock);
  513. OCMStub([appMock isDataCollectionDefaultEnabled]).andReturn(NO);
  514. // With no other settings, Analytics collection should default to the app's flag.
  515. FIROptions *options = [[FIROptions alloc] initInternalWithOptionsDictionary:@{}];
  516. XCTAssertFalse(options.isAnalyticsCollectionEnabled);
  517. XCTAssertFalse(options.isMeasurementEnabled);
  518. [appMock stopMocking];
  519. }
  520. - (void)testAnalyticsCollectionGlobalSwitchOverrideToDisable {
  521. // Stub the default app, and set the global switch to YES.
  522. id appMock = OCMClassMock([FIRApp class]);
  523. OCMStub([appMock isDefaultAppConfigured]).andReturn(YES);
  524. OCMStub([appMock defaultApp]).andReturn(appMock);
  525. OCMStub([appMock isDataCollectionDefaultEnabled]).andReturn(YES);
  526. // Test the three Analytics flags that override to disable Analytics collection.
  527. FIROptions *collectionEnabledOptions = [[FIROptions alloc]
  528. initInternalWithOptionsDictionary:@{kFIRIsAnalyticsCollectionEnabled : @NO}];
  529. XCTAssertFalse(collectionEnabledOptions.isAnalyticsCollectionEnabled);
  530. FIROptions *collectionDeactivatedOptions = [[FIROptions alloc]
  531. initInternalWithOptionsDictionary:@{kFIRIsAnalyticsCollectionDeactivated : @YES}];
  532. XCTAssertFalse(collectionDeactivatedOptions.isAnalyticsCollectionEnabled);
  533. FIROptions *measurementEnabledOptions =
  534. [[FIROptions alloc] initInternalWithOptionsDictionary:@{kFIRIsMeasurementEnabled : @NO}];
  535. XCTAssertFalse(measurementEnabledOptions.isAnalyticsCollectionEnabled);
  536. }
  537. - (void)testAnalyticsCollectionGlobalSwitchOverrideToEnable {
  538. // Stub the default app, and set the global switch to YES.
  539. id appMock = OCMClassMock([FIRApp class]);
  540. OCMStub([appMock isDefaultAppConfigured]).andReturn(YES);
  541. OCMStub([appMock defaultApp]).andReturn(appMock);
  542. OCMStub([appMock isDataCollectionDefaultEnabled]).andReturn(NO);
  543. // Test the two Analytics flags that can override and enable collection.
  544. FIROptions *collectionEnabledOptions = [[FIROptions alloc]
  545. initInternalWithOptionsDictionary:@{kFIRIsAnalyticsCollectionEnabled : @YES}];
  546. XCTAssertTrue(collectionEnabledOptions.isAnalyticsCollectionEnabled);
  547. FIROptions *measurementEnabledOptions =
  548. [[FIROptions alloc] initInternalWithOptionsDictionary:@{kFIRIsMeasurementEnabled : @YES}];
  549. XCTAssertTrue(measurementEnabledOptions.isAnalyticsCollectionEnabled);
  550. }
  551. - (void)testAnalyticsCollectionExplicitlySet {
  552. NSDictionary *optionsDictionary = @{};
  553. FIROptions *options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
  554. NSDictionary *analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:@{}];
  555. XCTAssertFalse([options isAnalyticsCollectionExplicitlySet]);
  556. // Test deactivation flag.
  557. optionsDictionary = @{kFIRIsAnalyticsCollectionDeactivated : @YES};
  558. options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
  559. analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:@{}];
  560. XCTAssertTrue([options isAnalyticsCollectionExplicitlySet]);
  561. // If "deactivated" == NO, that doesn't mean it's explicitly set / enabled so it should be treated
  562. // as if it's not set.
  563. optionsDictionary = @{kFIRIsAnalyticsCollectionDeactivated : @NO};
  564. options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
  565. analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:@{}];
  566. XCTAssertFalse([options isAnalyticsCollectionExplicitlySet]);
  567. // Test the collection enabled flag.
  568. optionsDictionary = @{kFIRIsAnalyticsCollectionEnabled : @YES};
  569. options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
  570. analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:@{}];
  571. XCTAssertTrue([options isAnalyticsCollectionExplicitlySet]);
  572. optionsDictionary = @{kFIRIsAnalyticsCollectionEnabled : @NO};
  573. options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
  574. analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:@{}];
  575. XCTAssertTrue([options isAnalyticsCollectionExplicitlySet]);
  576. // Test the old measurement flag.
  577. options = [[FIROptions alloc] initInternalWithOptionsDictionary:@{}];
  578. analyticsOptions =
  579. [options analyticsOptionsDictionaryWithInfoDictionary:@{kFIRIsMeasurementEnabled : @YES}];
  580. XCTAssertTrue([options isAnalyticsCollectionExplicitlySet]);
  581. options = [[FIROptions alloc] initInternalWithOptionsDictionary:@{}];
  582. analyticsOptions =
  583. [options analyticsOptionsDictionaryWithInfoDictionary:@{kFIRIsMeasurementEnabled : @NO}];
  584. XCTAssertTrue([options isAnalyticsCollectionExplicitlySet]);
  585. // For good measure, a combination of all 3 (even if they conflict).
  586. optionsDictionary =
  587. @{kFIRIsAnalyticsCollectionDeactivated : @YES, kFIRIsAnalyticsCollectionEnabled : @YES};
  588. options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
  589. analyticsOptions =
  590. [options analyticsOptionsDictionaryWithInfoDictionary:@{kFIRIsMeasurementEnabled : @NO}];
  591. XCTAssertTrue([options isAnalyticsCollectionExplicitlySet]);
  592. }
  593. - (void)testModifyingOptionsThrows {
  594. FIROptions *options = [self appOptions];
  595. options.editingLocked = YES;
  596. // Modification to every property should result in an exception.
  597. XCTAssertThrows(options.APIKey = @"should_throw");
  598. XCTAssertThrows(options.bundleID = @"should_throw");
  599. XCTAssertThrows(options.clientID = @"should_throw");
  600. XCTAssertThrows(options.databaseURL = @"should_throw");
  601. XCTAssertThrows(options.deepLinkURLScheme = @"should_throw");
  602. XCTAssertThrows(options.GCMSenderID = @"should_throw");
  603. XCTAssertThrows(options.googleAppID = @"should_throw");
  604. XCTAssertThrows(options.projectID = @"should_throw");
  605. XCTAssertThrows(options.storageBucket = @"should_throw");
  606. }
  607. - (void)testVersionFormat {
  608. // `kFIRLibraryVersionID` is `nil` until `libraryVersion` is called on `FIROptions`.
  609. FIROptions *options = [self appOptions];
  610. __unused NSString *libraryVersion = options.libraryVersionID;
  611. options = nil;
  612. NSRegularExpression *sLibraryVersionRegex =
  613. [NSRegularExpression regularExpressionWithPattern:@"^[0-9]{8,}$" options:0 error:NULL];
  614. NSUInteger numberOfMatches =
  615. [sLibraryVersionRegex numberOfMatchesInString:kFIRLibraryVersionID
  616. options:0
  617. range:NSMakeRange(0, kFIRLibraryVersionID.length)];
  618. XCTAssertEqual(numberOfMatches, 1, @"Incorrect library version format.");
  619. }
  620. - (void)testVersionConsistency {
  621. // `kFIRLibraryVersionID` is `nil` until `libraryVersion` is called on `FIROptions`.
  622. FIROptions *options = [self appOptions];
  623. __unused NSString *libraryVersion = options.libraryVersionID;
  624. options = nil;
  625. // Now `kFIRLibraryVersionID` is assigned, test that it is formatted correctly.
  626. const char *versionString = [kFIRLibraryVersionID UTF8String];
  627. int major = (versionString[0] - '0') * 10 + versionString[1] - '0';
  628. int minor = (versionString[2] - '0') * 10 + versionString[3] - '0';
  629. int patch = (versionString[4] - '0') * 10 + versionString[5] - '0';
  630. NSString *str = [NSString stringWithFormat:@"%d.%d.%d", major, minor, patch];
  631. XCTAssertTrue([FIRFirebaseVersion() hasPrefix:str]);
  632. }
  633. // Repeat test with more Objective-C.
  634. - (void)testVersionConsistency2 {
  635. // `kFIRLibraryVersionID` is `nil` until `libraryVersion` is called on `FIROptions`.
  636. FIROptions *options = [self appOptions];
  637. __unused NSString *libraryVersion = options.libraryVersionID;
  638. options = nil;
  639. NSRange major = NSMakeRange(0, 2);
  640. NSRange minor = NSMakeRange(2, 2);
  641. NSRange patch = NSMakeRange(4, 2);
  642. NSString *str = [NSString
  643. stringWithFormat:@"%d.%d.%d", [[kFIRLibraryVersionID substringWithRange:major] intValue],
  644. [[kFIRLibraryVersionID substringWithRange:minor] intValue],
  645. [[kFIRLibraryVersionID substringWithRange:patch] intValue]];
  646. XCTAssertTrue([FIRFirebaseVersion() hasPrefix:str]);
  647. }
  648. #pragma mark - Helpers
  649. - (NSString *)validGoogleServicesInfoPlistPath {
  650. NSString *filePath = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info"
  651. ofType:@"plist"];
  652. if (filePath == nil) {
  653. // Use bundleForClass to allow GoogleService-Info.plist to be in the test target's bundle.
  654. NSBundle *bundle = [NSBundle bundleForClass:[self class]];
  655. filePath = [bundle pathForResource:@"GoogleService-Info" ofType:@"plist"];
  656. }
  657. XCTAssertNotNil(filePath);
  658. return filePath;
  659. }
  660. - (NSString *)validJsonConfigPath {
  661. NSString *filePath = [[NSBundle mainBundle] pathForResource:@"firebase-sdk-config-apple"
  662. ofType:@"json"];
  663. if (filePath == nil) {
  664. // Use bundleForClass to allow GoogleService-Info.plist to be in the test target's bundle.
  665. NSBundle *bundle = [NSBundle bundleForClass:[self class]];
  666. filePath = [bundle pathForResource:@"firebase-sdk-config-apple" ofType:@"json"];
  667. }
  668. XCTAssertNotNil(filePath);
  669. return filePath;
  670. }
  671. - (FIROptions *)appOptions {
  672. return [[FIROptions alloc] initWithAppID:kGoogleAppID
  673. projectNumber:kProjectNumber
  674. projectID:kProjectID
  675. apiKey:kAPIKey];
  676. }
  677. - (FIROptions *)oldAppOptions {
  678. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  679. return [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kProjectNumber];
  680. #pragma clang diagnostic pop
  681. }
  682. @end