RCNRemoteConfigTest.m 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188
  1. /*
  2. * Copyright 2019 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <XCTest/XCTest.h>
  17. #import <FirebaseRemoteConfig/FIRRemoteConfig.h>
  18. #import "FirebaseRemoteConfig/Sources/FIRRemoteConfig_Internal.h"
  19. #import "FirebaseRemoteConfig/Sources/RCNConfigConstants.h"
  20. #import "FirebaseRemoteConfig/Sources/RCNConfigDBManager.h"
  21. #import "FirebaseRemoteConfig/Sources/RCNConfigFetch.h"
  22. #import "FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.h"
  23. #import "FirebaseRemoteConfig/Tests/Unit/RCNTestUtilities.h"
  24. #import <FirebaseCore/FIRAppInternal.h>
  25. #import <FirebaseCore/FIRLogger.h>
  26. #import <FirebaseCore/FIROptions.h>
  27. #import <GoogleUtilities/GULNSData+zlib.h>
  28. #import <OCMock/OCMock.h>
  29. @interface RCNConfigFetch (ForTest)
  30. - (instancetype)initWithContent:(RCNConfigContent *)content
  31. DBManager:(RCNConfigDBManager *)DBManager
  32. settings:(RCNConfigSettings *)settings
  33. experiment:(RCNConfigExperiment *)experiment
  34. queue:(dispatch_queue_t)queue
  35. namespace:firebaseNamespace
  36. app:firebaseApp;
  37. /// Skip fetching user properties from analytics because we cannot mock the action here. Instead
  38. /// overriding the method to skip.
  39. - (void)fetchWithUserPropertiesCompletionHandler:(FIRAInteropUserPropertiesCallback)block;
  40. - (NSURLSessionDataTask *)URLSessionDataTaskWithContent:(NSData *)content
  41. completionHandler:
  42. (RCNConfigFetcherCompletion)fetcherCompletion;
  43. - (void)fetchWithUserProperties:(NSDictionary *)userProperties
  44. completionHandler:(FIRRemoteConfigFetchCompletion)completionHandler;
  45. - (NSString *)constructServerURL;
  46. - (NSURLSession *)currentNetworkSession;
  47. @end
  48. @interface FIRRemoteConfig (ForTest)
  49. - (void)updateWithNewInstancesForConfigFetch:(RCNConfigFetch *)configFetch
  50. configContent:(RCNConfigContent *)configContent
  51. configSettings:(RCNConfigSettings *)configSettings
  52. configExperiment:(RCNConfigExperiment *)configExperiment;
  53. @end
  54. @implementation FIRRemoteConfig (ForTest)
  55. - (void)updateWithNewInstancesForConfigFetch:(RCNConfigFetch *)configFetch
  56. configContent:(RCNConfigContent *)configContent
  57. configSettings:(RCNConfigSettings *)configSettings
  58. configExperiment:(RCNConfigExperiment *)configExperiment {
  59. [self setValue:configFetch forKey:@"_configFetch"];
  60. [self setValue:configContent forKey:@"_configContent"];
  61. [self setValue:configSettings forKey:@"_settings"];
  62. [self setValue:configExperiment forKey:@"_configExperiment"];
  63. }
  64. @end
  65. @interface RCNConfigDBManager (Test)
  66. - (void)removeDatabaseOnDatabaseQueueAtPath:(NSString *)path;
  67. @end
  68. @interface RCNUserDefaultsManager (Test)
  69. + (NSUserDefaults *)sharedUserDefaultsForBundleIdentifier:(NSString *)bundleIdentifier;
  70. @end
  71. typedef NS_ENUM(NSInteger, RCNTestRCInstance) {
  72. RCNTestRCInstanceDefault,
  73. RCNTestRCInstanceSecondNamespace,
  74. RCNTestRCInstanceSecondApp,
  75. RCNTestRCNumTotalInstances
  76. };
  77. @interface RCNRemoteConfigTest : XCTestCase {
  78. NSTimeInterval _expectationTimeout;
  79. NSTimeInterval _checkCompletionTimeout;
  80. NSMutableArray<FIRRemoteConfig *> *_configInstances;
  81. NSMutableArray<NSDictionary<NSString *, NSString *> *> *_entries;
  82. NSMutableArray<NSDictionary<NSString *, id> *> *_response;
  83. NSMutableArray<NSData *> *_responseData;
  84. NSMutableArray<NSURLResponse *> *_URLResponse;
  85. NSMutableArray<RCNConfigFetch *> *_configFetch;
  86. RCNConfigDBManager *_DBManager;
  87. NSUserDefaults *_userDefaults;
  88. NSString *_userDefaultsSuiteName;
  89. NSString *_DBPath;
  90. }
  91. @end
  92. @implementation RCNRemoteConfigTest
  93. - (void)setUp {
  94. [super setUp];
  95. FIRSetLoggerLevel(FIRLoggerLevelMax);
  96. _expectationTimeout = 5;
  97. _checkCompletionTimeout = 1.0;
  98. // Always remove the database at the start of testing.
  99. _DBPath = [RCNTestUtilities remoteConfigPathForTestDatabase];
  100. id classMock = OCMClassMock([RCNConfigDBManager class]);
  101. OCMStub([classMock remoteConfigPathForDatabase]).andReturn(_DBPath);
  102. _DBManager = [[RCNConfigDBManager alloc] init];
  103. _userDefaultsSuiteName = [RCNTestUtilities userDefaultsSuiteNameForTestSuite];
  104. _userDefaults = [[NSUserDefaults alloc] initWithSuiteName:_userDefaultsSuiteName];
  105. id userDefaultsClassMock = OCMClassMock([RCNUserDefaultsManager class]);
  106. OCMStub([userDefaultsClassMock sharedUserDefaultsForBundleIdentifier:[OCMArg any]])
  107. .andReturn(_userDefaults);
  108. RCNConfigContent *configContent = [[RCNConfigContent alloc] initWithDBManager:_DBManager];
  109. _configInstances = [[NSMutableArray alloc] initWithCapacity:3];
  110. _entries = [[NSMutableArray alloc] initWithCapacity:3];
  111. _response = [[NSMutableArray alloc] initWithCapacity:3];
  112. _responseData = [[NSMutableArray alloc] initWithCapacity:3];
  113. _URLResponse = [[NSMutableArray alloc] initWithCapacity:3];
  114. _configFetch = [[NSMutableArray alloc] initWithCapacity:3];
  115. // Populate the default, second app, second namespace instances.
  116. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  117. // Fake a response for default instance.
  118. NSMutableDictionary<NSString *, NSString *> *valuesDict = [[NSMutableDictionary alloc] init];
  119. for (int count = 1; count <= 100; count++) {
  120. NSString *key = [NSString stringWithFormat:@"key%d-%d", count, i];
  121. NSString *value = [NSString stringWithFormat:@"value%d-%d", count, i];
  122. valuesDict[key] = value;
  123. }
  124. _entries[i] = valuesDict;
  125. NSString *currentAppName = nil;
  126. FIROptions *currentOptions = nil;
  127. NSString *currentNamespace = nil;
  128. switch (i) {
  129. case RCNTestRCInstanceSecondNamespace:
  130. currentAppName = RCNTestsDefaultFIRAppName;
  131. currentOptions = [self firstAppOptions];
  132. currentNamespace = RCNTestsPerfNamespace;
  133. break;
  134. case RCNTestRCInstanceSecondApp:
  135. currentAppName = RCNTestsSecondFIRAppName;
  136. currentOptions = [self secondAppOptions];
  137. currentNamespace = FIRNamespaceGoogleMobilePlatform;
  138. break;
  139. case RCNTestRCInstanceDefault:
  140. default:
  141. currentAppName = RCNTestsDefaultFIRAppName;
  142. currentOptions = [self firstAppOptions];
  143. currentNamespace = RCNTestsFIRNamespace;
  144. break;
  145. }
  146. NSString *fullyQualifiedNamespace =
  147. [NSString stringWithFormat:@"%@:%@", currentNamespace, currentAppName];
  148. FIRRemoteConfig *config =
  149. OCMPartialMock([[FIRRemoteConfig alloc] initWithAppName:currentAppName
  150. FIROptions:currentOptions
  151. namespace:currentNamespace
  152. DBManager:_DBManager
  153. configContent:configContent
  154. analytics:nil]);
  155. _configInstances[i] = config;
  156. RCNConfigSettings *settings =
  157. [[RCNConfigSettings alloc] initWithDatabaseManager:_DBManager
  158. namespace:fullyQualifiedNamespace
  159. firebaseAppName:currentAppName
  160. googleAppID:currentOptions.googleAppID];
  161. dispatch_queue_t queue = dispatch_queue_create(
  162. [[NSString stringWithFormat:@"testqueue: %d", i] cStringUsingEncoding:NSUTF8StringEncoding],
  163. DISPATCH_QUEUE_SERIAL);
  164. _configFetch[i] = OCMPartialMock([[RCNConfigFetch alloc] initWithContent:configContent
  165. DBManager:_DBManager
  166. settings:settings
  167. analytics:nil
  168. experiment:nil
  169. queue:queue
  170. namespace:fullyQualifiedNamespace
  171. options:currentOptions]);
  172. OCMStub([_configFetch[i] fetchAllConfigsWithExpirationDuration:43200
  173. completionHandler:OCMOCK_ANY])
  174. .andDo(^(NSInvocation *invocation) {
  175. void (^handler)(FIRRemoteConfigFetchStatus status, NSError *_Nullable error) = nil;
  176. // void (^handler)(FIRRemoteConfigFetchCompletion);
  177. [invocation getArgument:&handler atIndex:3];
  178. [_configFetch[i] fetchWithUserProperties:[[NSDictionary alloc] init]
  179. completionHandler:handler];
  180. });
  181. _response[i] = @{@"state" : @"UPDATE", @"entries" : _entries[i]};
  182. _responseData[i] = [NSJSONSerialization dataWithJSONObject:_response[i] options:0 error:nil];
  183. _URLResponse[i] = [[NSHTTPURLResponse alloc]
  184. initWithURL:[NSURL URLWithString:@"https://firebase.com"]
  185. statusCode:200
  186. HTTPVersion:nil
  187. headerFields:@{@"etag" : [NSString stringWithFormat:@"etag1-%d", i]}];
  188. id completionBlock =
  189. [OCMArg invokeBlockWithArgs:_responseData[i], _URLResponse[i], [NSNull null], nil];
  190. OCMExpect([_configFetch[i] URLSessionDataTaskWithContent:[OCMArg any]
  191. completionHandler:completionBlock])
  192. .andReturn(nil);
  193. [_configInstances[i] updateWithNewInstancesForConfigFetch:_configFetch[i]
  194. configContent:configContent
  195. configSettings:settings
  196. configExperiment:nil];
  197. }
  198. }
  199. - (void)tearDown {
  200. // Causes crash if main thread exits before the RCNConfigDB queue cleans up
  201. // [_DBManager removeDatabaseOnDatabaseQueueAtPath:_DBPath];
  202. [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:_userDefaultsSuiteName];
  203. [super tearDown];
  204. }
  205. - (void)testFetchConfigWithNilCallback {
  206. NSMutableArray<XCTestExpectation *> *expectations =
  207. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  208. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  209. expectations[i] = [self
  210. expectationWithDescription:
  211. [NSString stringWithFormat:@"Set defaults no callback expectation - instance %d", i]];
  212. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  213. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:nil];
  214. dispatch_after(
  215. dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_checkCompletionTimeout * NSEC_PER_SEC)),
  216. dispatch_get_main_queue(), ^{
  217. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  218. [expectations[i] fulfill];
  219. });
  220. }
  221. [self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
  222. }
  223. - (void)testFetchConfigsSuccessfully {
  224. NSMutableArray<XCTestExpectation *> *expectations =
  225. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  226. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  227. expectations[i] =
  228. [self expectationWithDescription:
  229. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  230. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  231. FIRRemoteConfigFetchCompletion fetchCompletion =
  232. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  233. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  234. XCTAssertNil(error);
  235. #pragma clang diagnostic push
  236. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  237. XCTAssertTrue([_configInstances[i] activateFetched]);
  238. #pragma clang diagnostic pop
  239. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  240. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  241. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  242. NSString *value2 = [NSString stringWithFormat:@"value2-%d", i];
  243. XCTAssertEqualObjects(_configInstances[i][key1].stringValue, value1);
  244. XCTAssertEqualObjects(_configInstances[i][key2].stringValue, value2);
  245. OCMVerify([_configInstances[i] objectForKeyedSubscript:key1]);
  246. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  247. @"Callback of first successful config "
  248. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccessFresh.");
  249. XCTAssertNotNil(_configInstances[i].lastFetchTime);
  250. XCTAssertGreaterThan(_configInstances[i].lastFetchTime.timeIntervalSince1970, 0,
  251. @"last fetch time interval should be set.");
  252. [expectations[i] fulfill];
  253. };
  254. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  255. }
  256. [self waitForExpectationsWithTimeout:_expectationTimeout
  257. handler:^(NSError *error) {
  258. XCTAssertNil(error);
  259. }];
  260. }
  261. - (void)testFetchAndActivate {
  262. NSMutableArray<XCTestExpectation *> *expectations =
  263. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  264. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  265. expectations[i] =
  266. [self expectationWithDescription:
  267. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  268. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  269. FIRRemoteConfigFetchAndActivateCompletion fetchAndActivateCompletion = ^void(
  270. FIRRemoteConfigFetchAndActivateStatus status, NSError *error) {
  271. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  272. XCTAssertNil(error);
  273. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  274. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  275. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  276. NSString *value2 = [NSString stringWithFormat:@"value2-%d", i];
  277. XCTAssertEqualObjects(_configInstances[i][key1].stringValue, value1);
  278. XCTAssertEqualObjects(_configInstances[i][key2].stringValue, value2);
  279. OCMVerify([_configInstances[i] objectForKeyedSubscript:key1]);
  280. XCTAssertEqual(
  281. status, FIRRemoteConfigFetchAndActivateStatusSuccessFetchedFromRemote,
  282. @"Callback of first successful config "
  283. @"fetchAndActivate status must equal to FIRRemoteConfigFetchAndStatusSuccessFromRemote.");
  284. XCTAssertNotNil(_configInstances[i].lastFetchTime);
  285. XCTAssertGreaterThan(_configInstances[i].lastFetchTime.timeIntervalSince1970, 0,
  286. @"last fetch time interval should be set.");
  287. [expectations[i] fulfill];
  288. };
  289. // Update the minimum fetch interval to 0. This disables the cache and forces a remote fetch
  290. // request.
  291. FIRRemoteConfigSettings *settings = [[FIRRemoteConfigSettings alloc] init];
  292. settings.minimumFetchInterval = 0;
  293. [_configInstances[i] setConfigSettings:settings];
  294. [_configInstances[i] fetchAndActivateWithCompletionHandler:fetchAndActivateCompletion];
  295. }
  296. [self waitForExpectationsWithTimeout:_expectationTimeout
  297. handler:^(NSError *error) {
  298. XCTAssertNil(error);
  299. }];
  300. }
  301. // TODO: Try splitting into smaller tests.
  302. - (void)testFetchConfigsSuccessfullyWithNewActivateMethodSignature {
  303. NSMutableArray<XCTestExpectation *> *expectations =
  304. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  305. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  306. expectations[i] =
  307. [self expectationWithDescription:
  308. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  309. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  310. FIRRemoteConfigFetchCompletion fetchCompletion =
  311. ^(FIRRemoteConfigFetchStatus status, NSError *error) {
  312. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  313. XCTAssertNil(error);
  314. [_configInstances[i] activateWithCompletionHandler:^(NSError *_Nullable error) {
  315. XCTAssertNil(error);
  316. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  317. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  318. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  319. NSString *value2 = [NSString stringWithFormat:@"value2-%d", i];
  320. XCTAssertEqualObjects(_configInstances[i][key1].stringValue, value1);
  321. XCTAssertEqualObjects(_configInstances[i][key2].stringValue, value2);
  322. OCMVerify([_configInstances[i] objectForKeyedSubscript:key1]);
  323. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  324. @"Callback of first successful config "
  325. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccessFresh.");
  326. XCTAssertNotNil(_configInstances[i].lastFetchTime);
  327. XCTAssertGreaterThan(_configInstances[i].lastFetchTime.timeIntervalSince1970, 0,
  328. @"last fetch time interval should be set.");
  329. // A second activate should return an error.
  330. [_configInstances[i] activateWithCompletionHandler:^(NSError *_Nullable error) {
  331. XCTAssertNotNil(error);
  332. XCTAssertEqualObjects(error.domain, FIRRemoteConfigErrorDomain);
  333. }];
  334. [expectations[i] fulfill];
  335. }];
  336. };
  337. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  338. }
  339. [self waitForExpectationsWithTimeout:_expectationTimeout
  340. handler:^(NSError *error) {
  341. XCTAssertNil(error);
  342. }];
  343. }
  344. - (void)testEnumeratingConfigResults {
  345. NSMutableArray<XCTestExpectation *> *expectations =
  346. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  347. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  348. expectations[i] = [self
  349. expectationWithDescription:
  350. [NSString stringWithFormat:@"Test enumerating configs successfully - instance %d", i]];
  351. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  352. FIRRemoteConfigFetchCompletion fetchCompletion =
  353. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  354. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  355. XCTAssertNil(error);
  356. #pragma clang diagnostic push
  357. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  358. XCTAssertTrue([_configInstances[i] activateFetched]);
  359. #pragma clang diagnostic pop
  360. NSString *key5 = [NSString stringWithFormat:@"key5-%d", i];
  361. NSString *key19 = [NSString stringWithFormat:@"key19-%d", i];
  362. NSString *value5 = [NSString stringWithFormat:@"value5-%d", i];
  363. NSString *value19 = [NSString stringWithFormat:@"value19-%d", i];
  364. XCTAssertEqualObjects(_configInstances[i][key5].stringValue, value5);
  365. XCTAssertEqualObjects(_configInstances[i][key19].stringValue, value19);
  366. // Test enumerating config values.
  367. for (NSString *key in _configInstances[i]) {
  368. if ([key isEqualToString:key5]) {
  369. XCTAssertEqualObjects(_configInstances[i][key5].stringValue, value5);
  370. }
  371. if ([key isEqualToString:key19]) {
  372. XCTAssertEqualObjects(_configInstances[i][key19].stringValue, value19);
  373. }
  374. }
  375. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  376. @"Callback of first successful config "
  377. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccessFresh.");
  378. XCTAssertNotNil(_configInstances[i].lastFetchTime);
  379. XCTAssertGreaterThan(_configInstances[i].lastFetchTime.timeIntervalSince1970, 0,
  380. @"last fetch time interval should be set.");
  381. [expectations[i] fulfill];
  382. };
  383. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  384. }
  385. [self waitForExpectationsWithTimeout:_expectationTimeout
  386. handler:^(NSError *error) {
  387. XCTAssertNil(error);
  388. }];
  389. }
  390. - (void)testFetchConfigsFailed {
  391. // Override the setup values to return back an error status.
  392. RCNConfigContent *configContent = [[RCNConfigContent alloc] initWithDBManager:_DBManager];
  393. // Populate the default, second app, second namespace instances.
  394. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  395. NSString *currentAppName = nil;
  396. FIROptions *currentOptions = nil;
  397. NSString *currentNamespace = nil;
  398. switch (i) {
  399. case RCNTestRCInstanceSecondNamespace:
  400. currentAppName = RCNTestsDefaultFIRAppName;
  401. currentOptions = [self firstAppOptions];
  402. currentNamespace = RCNTestsPerfNamespace;
  403. break;
  404. case RCNTestRCInstanceSecondApp:
  405. currentAppName = RCNTestsSecondFIRAppName;
  406. currentOptions = [self secondAppOptions];
  407. currentNamespace = FIRNamespaceGoogleMobilePlatform;
  408. break;
  409. case RCNTestRCInstanceDefault:
  410. default:
  411. currentAppName = RCNTestsDefaultFIRAppName;
  412. currentOptions = [self firstAppOptions];
  413. currentNamespace = RCNTestsFIRNamespace;
  414. break;
  415. }
  416. NSString *fullyQualifiedNamespace =
  417. [NSString stringWithFormat:@"%@:%@", currentNamespace, currentAppName];
  418. RCNUserDefaultsManager *userDefaultsManager =
  419. [[RCNUserDefaultsManager alloc] initWithAppName:currentAppName
  420. bundleID:[NSBundle mainBundle].bundleIdentifier
  421. namespace:fullyQualifiedNamespace];
  422. userDefaultsManager.lastFetchTime = 0;
  423. FIRRemoteConfig *config =
  424. OCMPartialMock([[FIRRemoteConfig alloc] initWithAppName:currentAppName
  425. FIROptions:currentOptions
  426. namespace:currentNamespace
  427. DBManager:_DBManager
  428. configContent:configContent
  429. analytics:nil]);
  430. _configInstances[i] = config;
  431. RCNConfigSettings *settings =
  432. [[RCNConfigSettings alloc] initWithDatabaseManager:_DBManager
  433. namespace:fullyQualifiedNamespace
  434. firebaseAppName:currentAppName
  435. googleAppID:currentOptions.googleAppID];
  436. dispatch_queue_t queue = dispatch_queue_create(
  437. [[NSString stringWithFormat:@"testqueue: %d", i] cStringUsingEncoding:NSUTF8StringEncoding],
  438. DISPATCH_QUEUE_SERIAL);
  439. _configFetch[i] = OCMPartialMock([[RCNConfigFetch alloc] initWithContent:configContent
  440. DBManager:_DBManager
  441. settings:settings
  442. analytics:nil
  443. experiment:nil
  444. queue:queue
  445. namespace:fullyQualifiedNamespace
  446. options:currentOptions]);
  447. OCMStub([_configFetch[i] fetchAllConfigsWithExpirationDuration:43200
  448. completionHandler:OCMOCK_ANY])
  449. .andDo(^(NSInvocation *invocation) {
  450. void (^handler)(FIRRemoteConfigFetchStatus status, NSError *_Nullable error) = nil;
  451. // void (^handler)(FIRRemoteConfigFetchCompletion);
  452. [invocation getArgument:&handler atIndex:3];
  453. [_configFetch[i] fetchWithUserProperties:[[NSDictionary alloc] init]
  454. completionHandler:handler];
  455. });
  456. _response[i] = @{};
  457. _responseData[i] = [NSJSONSerialization dataWithJSONObject:_response[i] options:0 error:nil];
  458. _URLResponse[i] =
  459. [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"https://firebase.com"]
  460. statusCode:500
  461. HTTPVersion:nil
  462. headerFields:@{@"etag" : @"etag1"}];
  463. id completionBlock =
  464. [OCMArg invokeBlockWithArgs:_responseData[i], _URLResponse[i], [NSNull null], nil];
  465. OCMExpect([_configFetch[i] URLSessionDataTaskWithContent:[OCMArg any]
  466. completionHandler:completionBlock])
  467. .andReturn(nil);
  468. [_configInstances[i] updateWithNewInstancesForConfigFetch:_configFetch[i]
  469. configContent:configContent
  470. configSettings:settings
  471. configExperiment:nil];
  472. }
  473. #pragma clang diagnostic push
  474. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  475. // Make the fetch calls for all instances.
  476. NSMutableArray<XCTestExpectation *> *expectations =
  477. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  478. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  479. expectations[i] = [self
  480. expectationWithDescription:
  481. [NSString stringWithFormat:@"Test enumerating configs successfully - instance %d", i]];
  482. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  483. FIRRemoteConfigFetchCompletion fetchCompletion =
  484. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  485. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusFailure);
  486. XCTAssertFalse([_configInstances[i] activateFetched]);
  487. XCTAssertNotNil(error);
  488. // No such key, still return a static value.
  489. FIRRemoteConfigValue *value = _configInstances[RCNTestRCInstanceDefault][@"key1"];
  490. XCTAssertEqual((int)value.source, (int)FIRRemoteConfigSourceStatic);
  491. XCTAssertEqualObjects(value.stringValue, @"");
  492. XCTAssertEqual(value.boolValue, NO);
  493. [expectations[i] fulfill];
  494. };
  495. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  496. }
  497. [self waitForExpectationsWithTimeout:_expectationTimeout
  498. handler:^(NSError *error) {
  499. XCTAssertNil(error);
  500. }];
  501. }
  502. - (void)testConfigValueForKey {
  503. NSMutableArray<XCTestExpectation *> *expectations =
  504. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  505. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  506. expectations[i] =
  507. [self expectationWithDescription:
  508. [NSString stringWithFormat:@"Test configValueForKey: method - instance %d", i]];
  509. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  510. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  511. NSError *error) {
  512. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess);
  513. XCTAssertNil(error);
  514. XCTAssertTrue([_configInstances[i] activateFetched]);
  515. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  516. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  517. NSString *key3 = [NSString stringWithFormat:@"key3-%d", i];
  518. NSString *key7 = [NSString stringWithFormat:@"key7-%d", i];
  519. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  520. NSString *value2 = [NSString stringWithFormat:@"value2-%d", i];
  521. NSString *value3 = [NSString stringWithFormat:@"value3-%d", i];
  522. NSString *value7 = [NSString stringWithFormat:@"value7-%d", i];
  523. XCTAssertEqualObjects(_configInstances[i][key1].stringValue, value1);
  524. XCTAssertEqualObjects(_configInstances[i][key2].stringValue, value2);
  525. OCMVerify([_configInstances[i] objectForKeyedSubscript:key1]);
  526. XCTAssertEqualObjects([_configInstances[i] configValueForKey:key3].stringValue, value3);
  527. if (i == RCNTestRCInstanceDefault) {
  528. XCTAssertEqualObjects(
  529. [_configInstances[i] configValueForKey:key7 namespace:FIRNamespaceGoogleMobilePlatform]
  530. .stringValue,
  531. value7);
  532. }
  533. XCTAssertEqualObjects([_configInstances[i] configValueForKey:key7].stringValue, value7);
  534. XCTAssertNotNil([_configInstances[i] configValueForKey:nil]);
  535. XCTAssertEqual([_configInstances[i] configValueForKey:nil].source,
  536. FIRRemoteConfigSourceStatic);
  537. XCTAssertEqual([_configInstances[i] configValueForKey:nil namespace:nil].source,
  538. FIRRemoteConfigSourceStatic);
  539. XCTAssertEqual([_configInstances[i] configValueForKey:nil namespace:nil source:-1].source,
  540. FIRRemoteConfigSourceStatic);
  541. [expectations[i] fulfill];
  542. };
  543. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  544. }
  545. [self waitForExpectationsWithTimeout:_expectationTimeout
  546. handler:^(NSError *error) {
  547. XCTAssertNil(error);
  548. }];
  549. }
  550. - (void)testFetchConfigWithDefaultSets {
  551. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  552. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  553. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  554. fetchConfigsExpectation[i] = [self
  555. expectationWithDescription:
  556. [NSString stringWithFormat:@"Test fetch configs with defaults set - instance %d", i]];
  557. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  558. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  559. NSString *key0 = [NSString stringWithFormat:@"key0-%d", i];
  560. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  561. NSString *value2 = [NSString stringWithFormat:@"value2-%d", i];
  562. NSDictionary<NSString *, NSString *> *defaults = @{key1 : @"default key1", key0 : @"value0-0"};
  563. [_configInstances[i] setDefaults:defaults];
  564. FIRRemoteConfigFetchCompletion fetchCompletion =
  565. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  566. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  567. XCTAssertNil(error);
  568. XCTAssertEqualObjects(_configInstances[i][key1].stringValue, @"default key1");
  569. XCTAssertEqual(_configInstances[i][key1].source, FIRRemoteConfigSourceDefault);
  570. #pragma clang diagnostic push
  571. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  572. XCTAssertTrue([_configInstances[i] activateFetched]);
  573. #pragma clang diagnostic pop
  574. XCTAssertEqualObjects(_configInstances[i][key1].stringValue, value1);
  575. XCTAssertEqual(_configInstances[i][key1].source, FIRRemoteConfigSourceRemote);
  576. XCTAssertEqualObjects([_configInstances[i] defaultValueForKey:key1].stringValue,
  577. @"default key1");
  578. XCTAssertEqualObjects(_configInstances[i][key2].stringValue, value2);
  579. XCTAssertEqualObjects(_configInstances[i][key0].stringValue, @"value0-0");
  580. #pragma clang diagnostic push
  581. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  582. XCTAssertNil([_configInstances[i] defaultValueForKey:nil namespace:nil]);
  583. #pragma clang diagnostic pop
  584. OCMVerify([_configInstances[i] objectForKeyedSubscript:key1]);
  585. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  586. @"Callback of first successful config "
  587. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccess.");
  588. [fetchConfigsExpectation[i] fulfill];
  589. };
  590. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  591. }
  592. [self waitForExpectationsWithTimeout:_expectationTimeout
  593. handler:^(NSError *error) {
  594. XCTAssertNil(error);
  595. }];
  596. }
  597. - (void)testDefaultsSetsOnly {
  598. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  599. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  600. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  601. NSString *key3 = [NSString stringWithFormat:@"key3-%d", i];
  602. NSString *key4 = [NSString stringWithFormat:@"key4-%d", i];
  603. NSString *key5 = [NSString stringWithFormat:@"key5-%d", i];
  604. NSString *defaultValue1 = @"default value1";
  605. NSData *defaultValue2 = [defaultValue1 dataUsingEncoding:NSUTF8StringEncoding];
  606. NSNumber *defaultValue3 = [NSNumber numberWithFloat:3.1415926];
  607. NSDate *defaultValue4 = [NSDate date];
  608. BOOL defaultValue5 = NO;
  609. NSMutableDictionary<NSString *, id> *defaults = [NSMutableDictionary dictionaryWithDictionary:@{
  610. key1 : defaultValue1,
  611. key2 : defaultValue2,
  612. key3 : defaultValue3,
  613. key4 : defaultValue4,
  614. key5 : @(defaultValue5),
  615. }];
  616. [_configInstances[i] setDefaults:defaults];
  617. if (i == RCNTestRCInstanceSecondNamespace) {
  618. [defaults setObject:@"2860" forKey:@"experience"];
  619. [_configInstances[i] setDefaults:defaults namespace:RCNTestsPerfNamespace];
  620. }
  621. // Remove objects right away to make sure dispatch_async gets the copy.
  622. [defaults removeAllObjects];
  623. FIRRemoteConfig *config = _configInstances[i];
  624. XCTAssertEqualObjects(config[key1].stringValue, defaultValue1, @"Should support string format");
  625. XCTAssertEqualObjects(config[key2].dataValue, defaultValue2, @"Should support data format");
  626. XCTAssertEqual(config[key3].numberValue.longValue, defaultValue3.longValue,
  627. @"Should support number format");
  628. NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  629. [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  630. NSString *strValueOfDate = [dateFormatter stringFromDate:(NSDate *)defaultValue4];
  631. XCTAssertEqualObjects(
  632. config[key4].stringValue, strValueOfDate,
  633. @"Date format can be set as an input from plist, but output coming out of "
  634. @"defaultConfig as string.");
  635. XCTAssertEqual(config[key5].boolValue, defaultValue5, @"Should support bool format");
  636. if (i == RCNTestRCInstanceSecondNamespace) {
  637. XCTAssertEqualObjects(
  638. [_configInstances[i] configValueForKey:@"experience" namespace:RCNTestsPerfNamespace]
  639. .stringValue,
  640. @"2860", @"Only default config has the key, must equal to default config value.");
  641. }
  642. // Reset default sets
  643. [_configInstances[i] setDefaults:nil];
  644. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceDefault].count, 0);
  645. }
  646. }
  647. - (void)testSetDefaultsWithNilParams {
  648. NSMutableArray<XCTestExpectation *> *expectations =
  649. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  650. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  651. expectations[i] = [self
  652. expectationWithDescription:
  653. [NSString stringWithFormat:@"Set defaults no callback expectation - instance %d", i]];
  654. // Should work when passing nil.
  655. [_configInstances[i] setDefaults:nil];
  656. [_configInstances[i] setDefaults:nil namespace:nil];
  657. dispatch_after(
  658. dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_checkCompletionTimeout * NSEC_PER_SEC)),
  659. dispatch_get_main_queue(), ^{
  660. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceDefault
  661. namespace:FIRNamespaceGoogleMobilePlatform]
  662. .count,
  663. 0);
  664. [expectations[i] fulfill];
  665. });
  666. }
  667. [self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
  668. }
  669. - (void)testFetchConfigOverwriteDefaultSet {
  670. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  671. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  672. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  673. fetchConfigsExpectation[i] = [self
  674. expectationWithDescription:
  675. [NSString stringWithFormat:@"Test fetch configs with defaults set - instance %d", i]];
  676. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  677. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  678. [_configInstances[i] setDefaults:@{key1 : @"default key1"}];
  679. FIRRemoteConfigValue *value = _configInstances[i][key1];
  680. XCTAssertEqualObjects(value.stringValue, @"default key1");
  681. XCTAssertEqual(value.source, FIRRemoteConfigSourceDefault);
  682. value = _configInstances[i][@"A key doesn't exist"];
  683. XCTAssertEqual(value.source, FIRRemoteConfigSourceStatic);
  684. FIRRemoteConfigFetchCompletion fetchCompletion =
  685. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  686. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  687. XCTAssertNil(error);
  688. XCTAssertTrue([_configInstances[i] activateFetched]);
  689. XCTAssertEqualObjects(_configInstances[i][key1].stringValue, value1);
  690. XCTAssertEqual(_configInstances[i][key1].source, FIRRemoteConfigSourceRemote);
  691. XCTAssertEqualObjects([_configInstances[i] defaultValueForKey:key1].stringValue,
  692. @"default key1");
  693. OCMVerify([_configInstances[i] objectForKeyedSubscript:key1]);
  694. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  695. @"Callback of first successful config "
  696. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccess.");
  697. [fetchConfigsExpectation[i] fulfill];
  698. };
  699. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  700. }
  701. [self waitForExpectationsWithTimeout:_expectationTimeout
  702. handler:^(NSError *error) {
  703. XCTAssertNil(error);
  704. }];
  705. }
  706. - (void)testGetConfigValueBySource {
  707. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  708. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  709. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  710. fetchConfigsExpectation[i] =
  711. [self expectationWithDescription:
  712. [NSString stringWithFormat:@"Test get config value by source - instance %d", i]];
  713. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  714. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  715. NSDictionary<NSString *, NSString *> *defaults = @{key1 : @"default value1"};
  716. [_configInstances[i] setDefaults:defaults];
  717. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  718. NSError *error) {
  719. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  720. XCTAssertNil(error);
  721. XCTAssertEqualObjects(_configInstances[i][key1].stringValue, @"default value1");
  722. XCTAssertEqual(_configInstances[i][key1].source, FIRRemoteConfigSourceDefault);
  723. XCTAssertTrue([_configInstances[i] activateFetched]);
  724. XCTAssertEqualObjects(_configInstances[i][key1].stringValue, value1);
  725. XCTAssertEqual(_configInstances[i][key1].source, FIRRemoteConfigSourceRemote);
  726. FIRRemoteConfigValue *value;
  727. if (i == RCNTestRCInstanceDefault) {
  728. value = [_configInstances[i] configValueForKey:key1
  729. namespace:FIRNamespaceGoogleMobilePlatform
  730. source:FIRRemoteConfigSourceRemote];
  731. XCTAssertEqualObjects(value.stringValue, value1);
  732. value = [_configInstances[i] configValueForKey:key1
  733. namespace:FIRNamespaceGoogleMobilePlatform
  734. source:FIRRemoteConfigSourceDefault];
  735. XCTAssertEqualObjects(value.stringValue, @"default value1");
  736. value = [_configInstances[i] configValueForKey:key1
  737. namespace:FIRNamespaceGoogleMobilePlatform
  738. source:FIRRemoteConfigSourceStatic];
  739. } else {
  740. value = [_configInstances[i] configValueForKey:key1 source:FIRRemoteConfigSourceRemote];
  741. XCTAssertEqualObjects(value.stringValue, value1);
  742. value = [_configInstances[i] configValueForKey:key1 source:FIRRemoteConfigSourceDefault];
  743. XCTAssertEqualObjects(value.stringValue, @"default value1");
  744. value = [_configInstances[i] configValueForKey:key1 source:FIRRemoteConfigSourceStatic];
  745. }
  746. XCTAssertEqualObjects(value.stringValue, @"");
  747. XCTAssertEqualObjects(value.numberValue, @(0));
  748. XCTAssertEqual(value.boolValue, NO);
  749. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  750. @"Callback of first successful config "
  751. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccess.");
  752. [fetchConfigsExpectation[i] fulfill];
  753. };
  754. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  755. }
  756. [self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
  757. }
  758. - (void)testInvalidKeyOrNamespace {
  759. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  760. FIRRemoteConfigValue *value = [_configInstances[i] configValueForKey:nil];
  761. XCTAssertNotNil(value);
  762. XCTAssertEqual(value.source, FIRRemoteConfigSourceStatic);
  763. value = [_configInstances[i] configValueForKey:nil namespace:nil];
  764. XCTAssertNotNil(value);
  765. XCTAssertEqual(value.source, FIRRemoteConfigSourceStatic);
  766. value = [_configInstances[i] configValueForKey:nil namespace:nil source:5];
  767. XCTAssertNotNil(value);
  768. XCTAssertEqual(value.source, FIRRemoteConfigSourceStatic);
  769. }
  770. }
  771. // Remote Config converts UTC times in the plists to local times. This utility function makes it
  772. // possible to check the times when running the tests in any timezone.
  773. static NSString *UTCToLocal(NSString *utcTime) {
  774. // Create a UTC dateFormatter.
  775. NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  776. [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  777. [dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
  778. NSDate *date = [dateFormatter dateFromString:utcTime];
  779. [dateFormatter setTimeZone:[NSTimeZone localTimeZone]];
  780. return [dateFormatter stringFromDate:date];
  781. }
  782. - (void)testSetDefaultsFromPlist {
  783. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  784. FIRRemoteConfig *config = _configInstances[i];
  785. [config setDefaultsFromPlistFileName:@"Defaults-testInfo"];
  786. XCTAssertEqualObjects(_configInstances[i][@"lastCheckTime"].stringValue,
  787. UTCToLocal(@"2016-02-28 18:33:31"));
  788. XCTAssertEqual(_configInstances[i][@"isPaidUser"].boolValue, YES);
  789. XCTAssertEqualObjects(_configInstances[i][@"dataValue"].stringValue, @"2.4");
  790. XCTAssertEqualObjects(_configInstances[i][@"New item"].numberValue, @(2.4));
  791. XCTAssertEqualObjects(_configInstances[i][@"Languages"].stringValue, @"English");
  792. XCTAssertEqualObjects(_configInstances[i][@"FileInfo"].stringValue,
  793. @"To setup default config.");
  794. XCTAssertEqualObjects(_configInstances[i][@"format"].stringValue, @"key to value.");
  795. // If given a wrong file name, the default will not be set and kept as previous results.
  796. [_configInstances[i] setDefaultsFromPlistFileName:@""];
  797. XCTAssertEqualObjects(_configInstances[i][@"lastCheckTime"].stringValue,
  798. UTCToLocal(@"2016-02-28 18:33:31"));
  799. [_configInstances[i] setDefaultsFromPlistFileName:@"non-existed_file"];
  800. XCTAssertEqualObjects(_configInstances[i][@"dataValue"].stringValue, @"2.4");
  801. [_configInstances[i] setDefaultsFromPlistFileName:nil namespace:nil];
  802. XCTAssertEqualObjects(_configInstances[i][@"New item"].numberValue, @(2.4));
  803. [_configInstances[i] setDefaultsFromPlistFileName:@"" namespace:@""];
  804. XCTAssertEqualObjects(_configInstances[i][@"Languages"].stringValue, @"English");
  805. }
  806. }
  807. - (void)testSetDefaultsAndNamespaceFromPlist {
  808. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  809. if (i == RCNTestRCInstanceDefault) {
  810. [_configInstances[i] setDefaultsFromPlistFileName:@"Defaults-testInfo"
  811. namespace:RCNTestsPerfNamespace];
  812. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"lastCheckTime"
  813. namespace:RCNTestsPerfNamespace]
  814. .stringValue,
  815. UTCToLocal(@"2016-02-28 18:33:31"));
  816. XCTAssertEqual([_configInstances[i] configValueForKey:@"isPaidUser"
  817. namespace:RCNTestsPerfNamespace]
  818. .boolValue,
  819. YES);
  820. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"dataValue"
  821. namespace:RCNTestsPerfNamespace]
  822. .stringValue,
  823. @"2.4");
  824. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"New item"
  825. namespace:RCNTestsPerfNamespace]
  826. .numberValue,
  827. @(2.4));
  828. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"Languages"
  829. namespace:RCNTestsPerfNamespace]
  830. .stringValue,
  831. @"English");
  832. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"FileInfo"
  833. namespace:RCNTestsPerfNamespace]
  834. .stringValue,
  835. @"To setup default config.");
  836. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"format"
  837. namespace:RCNTestsPerfNamespace]
  838. .stringValue,
  839. @"key to value.");
  840. } else {
  841. [_configInstances[i] setDefaultsFromPlistFileName:@"Defaults-testInfo"];
  842. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"lastCheckTime"].stringValue,
  843. UTCToLocal(@"2016-02-28 18:33:31"));
  844. XCTAssertEqual([_configInstances[i] configValueForKey:@"isPaidUser"].boolValue, YES);
  845. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"dataValue"].stringValue,
  846. @"2.4");
  847. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"New item"].numberValue,
  848. @(2.4));
  849. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"Languages"].stringValue,
  850. @"English");
  851. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"FileInfo"].stringValue,
  852. @"To setup default config.");
  853. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"format"].stringValue,
  854. @"key to value.");
  855. }
  856. }
  857. }
  858. - (void)testSetDeveloperMode {
  859. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  860. XCTAssertFalse(_configInstances[i].configSettings.isDeveloperModeEnabled);
  861. FIRRemoteConfigSettings *settings =
  862. [[FIRRemoteConfigSettings alloc] initWithDeveloperModeEnabled:YES];
  863. _configInstances[i].configSettings = settings;
  864. XCTAssertTrue(_configInstances[i].configSettings.isDeveloperModeEnabled);
  865. }
  866. }
  867. - (void)testAllKeysFromSource {
  868. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  869. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  870. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  871. fetchConfigsExpectation[i] = [self
  872. expectationWithDescription:[NSString
  873. stringWithFormat:@"Test allKeys methods - instance %d", i]];
  874. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  875. NSString *key0 = [NSString stringWithFormat:@"key0-%d", i];
  876. NSDictionary<NSString *, NSString *> *defaults = @{key1 : @"default key1", key0 : @"value0-0"};
  877. [_configInstances[i] setDefaults:defaults];
  878. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  879. NSError *error) {
  880. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess);
  881. XCTAssertNil(error);
  882. XCTAssertTrue([_configInstances[i] activateFetched]);
  883. if (i == RCNTestRCInstanceDefault) {
  884. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceRemote
  885. namespace:FIRNamespaceGoogleMobilePlatform]
  886. .count,
  887. 100);
  888. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceDefault
  889. namespace:FIRNamespaceGoogleMobilePlatform]
  890. .count,
  891. 2);
  892. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceStatic
  893. namespace:FIRNamespaceGoogleMobilePlatform]
  894. .count,
  895. 0);
  896. } else {
  897. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceRemote].count,
  898. 100);
  899. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceDefault].count,
  900. 2);
  901. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceStatic].count,
  902. 0);
  903. }
  904. XCTAssertNotNil([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceRemote
  905. namespace:@"invalid namespace"]);
  906. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceRemote
  907. namespace:@"invalid namespace"]
  908. .count,
  909. 0);
  910. XCTAssertNotNil([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceRemote
  911. namespace:nil]);
  912. XCTAssertEqual(
  913. [_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceRemote namespace:nil].count,
  914. 0);
  915. XCTAssertNotNil([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceDefault
  916. namespace:nil]);
  917. XCTAssertEqual(
  918. [_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceDefault namespace:nil].count,
  919. 0);
  920. [fetchConfigsExpectation[i] fulfill];
  921. };
  922. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  923. }
  924. [self waitForExpectationsWithTimeout:_expectationTimeout
  925. handler:^(NSError *error) {
  926. XCTAssertNil(error);
  927. }];
  928. }
  929. - (void)testAllKeysWithPrefix {
  930. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  931. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  932. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  933. fetchConfigsExpectation[i] = [self
  934. expectationWithDescription:[NSString
  935. stringWithFormat:@"Test allKeys methods - instance %d", i]];
  936. FIRRemoteConfigFetchCompletion fetchCompletion =
  937. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  938. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess);
  939. XCTAssertNil(error);
  940. NSLog(@"Testing _configInstances %d", i);
  941. #pragma clang diagnostic push
  942. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  943. XCTAssertTrue([_configInstances[i] activateFetched]);
  944. // Test keysWithPrefix:namespace: method.
  945. if (i == RCNTestRCInstanceDefault) {
  946. XCTAssertEqual([_configInstances[i] keysWithPrefix:@"key"
  947. namespace:FIRNamespaceGoogleMobilePlatform]
  948. .count,
  949. 100);
  950. } else {
  951. XCTAssertEqual([_configInstances[i] keysWithPrefix:@"key"].count, 100);
  952. }
  953. XCTAssertEqual(
  954. [_configInstances[i] keysWithPrefix:@"pl" namespace:@"invalid namespace"].count, 0);
  955. XCTAssertEqual([_configInstances[i] keysWithPrefix:@"pl" namespace:nil].count, 0);
  956. XCTAssertEqual([_configInstances[i] keysWithPrefix:@"pl" namespace:@""].count, 0);
  957. XCTAssertNotNil([_configInstances[i] keysWithPrefix:nil namespace:nil]);
  958. XCTAssertEqual([_configInstances[i] keysWithPrefix:nil namespace:nil].count, 0);
  959. #pragma clang diagnostic pop
  960. // Test keysWithPrefix: method.
  961. XCTAssertEqual([_configInstances[i] keysWithPrefix:@"key1"].count, 12);
  962. XCTAssertEqual([_configInstances[i] keysWithPrefix:@"key"].count, 100);
  963. XCTAssertEqual([_configInstances[i] keysWithPrefix:@"invalid key"].count, 0);
  964. XCTAssertEqual([_configInstances[i] keysWithPrefix:nil].count, 100);
  965. XCTAssertEqual([_configInstances[i] keysWithPrefix:@""].count, 100);
  966. [fetchConfigsExpectation[i] fulfill];
  967. };
  968. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  969. }
  970. [self waitForExpectationsWithTimeout:_expectationTimeout
  971. handler:^(NSError *error) {
  972. XCTAssertNil(error);
  973. }];
  974. }
  975. - (void)testSetDeveloperModeConfigSetting {
  976. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  977. #pragma clang diagnostic push
  978. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  979. FIRRemoteConfigSettings *settings =
  980. [[FIRRemoteConfigSettings alloc] initWithDeveloperModeEnabled:YES];
  981. [_configInstances[i] setConfigSettings:settings];
  982. XCTAssertTrue([_configInstances[i] configSettings].isDeveloperModeEnabled);
  983. settings = [[FIRRemoteConfigSettings alloc] initWithDeveloperModeEnabled:NO];
  984. [_configInstances[i] setConfigSettings:settings];
  985. XCTAssertFalse([_configInstances[i] configSettings].isDeveloperModeEnabled);
  986. #pragma clang diagnostic pop
  987. }
  988. }
  989. /// Test the minimum fetch interval is applied and read back correctly.
  990. - (void)testSetMinimumFetchIntervalConfigSetting {
  991. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  992. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  993. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  994. fetchConfigsExpectation[i] = [self
  995. expectationWithDescription:
  996. [NSString stringWithFormat:@"Test minimumFetchInterval expectation - instance %d", i]];
  997. FIRRemoteConfigSettings *settings = [[FIRRemoteConfigSettings alloc] init];
  998. settings.minimumFetchInterval = 123;
  999. [_configInstances[i] setConfigSettings:settings];
  1000. XCTAssertEqual([_configInstances[i] configSettings].minimumFetchInterval, 123);
  1001. FIRRemoteConfigFetchCompletion fetchCompletion =
  1002. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  1003. XCTAssertFalse([_configInstances[i].settings hasMinimumFetchIntervalElapsed:43200]);
  1004. // Update minimum fetch interval.
  1005. FIRRemoteConfigSettings *settings = [[FIRRemoteConfigSettings alloc] init];
  1006. settings.minimumFetchInterval = 0;
  1007. [_configInstances[i] setConfigSettings:settings];
  1008. XCTAssertEqual([_configInstances[i] configSettings].minimumFetchInterval, 0);
  1009. XCTAssertTrue([_configInstances[i].settings hasMinimumFetchIntervalElapsed:0]);
  1010. [fetchConfigsExpectation[i] fulfill];
  1011. };
  1012. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  1013. }
  1014. [self waitForExpectationsWithTimeout:_expectationTimeout
  1015. handler:^(NSError *error) {
  1016. XCTAssertNil(error);
  1017. }];
  1018. }
  1019. /// Test the fetch timeout is properly set and read back.
  1020. - (void)testSetFetchTimeoutConfigSetting {
  1021. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1022. FIRRemoteConfigSettings *settings = [[FIRRemoteConfigSettings alloc] init];
  1023. settings.fetchTimeout = 1;
  1024. [_configInstances[i] setConfigSettings:settings];
  1025. XCTAssertEqual([_configInstances[i] configSettings].fetchTimeout, 1);
  1026. NSURLSession *networkSession = [_configFetch[i] currentNetworkSession];
  1027. XCTAssertNotNil(networkSession);
  1028. XCTAssertEqual(networkSession.configuration.timeoutIntervalForResource, 1);
  1029. XCTAssertEqual(networkSession.configuration.timeoutIntervalForRequest, 1);
  1030. }
  1031. }
  1032. #pragma mark - Public Factory Methods
  1033. - (void)testConfigureConfigWithValidInput {
  1034. // Configure the default app with our options and ensure the Remote Config instance is set up
  1035. // properly.
  1036. XCTAssertNoThrow([FIRApp configureWithOptions:[self firstAppOptions]]);
  1037. XCTAssertNoThrow([FIRRemoteConfig remoteConfig]);
  1038. FIRRemoteConfig *config = [FIRRemoteConfig remoteConfig];
  1039. XCTAssertNotNil(config);
  1040. // Ensure the same instance is returned from the singleton.
  1041. FIRRemoteConfig *sameConfig = [FIRRemoteConfig remoteConfig];
  1042. XCTAssertNotNil(sameConfig);
  1043. XCTAssertEqual(config, sameConfig);
  1044. // Ensure the app name is stored properly.
  1045. XCTAssertEqual([config valueForKey:@"_appName"], kFIRDefaultAppName);
  1046. }
  1047. #pragma mark - Test Helpers
  1048. - (FIROptions *)firstAppOptions {
  1049. // TODO: Evaluate if we want to hardcode things here instead.
  1050. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"1:123:ios:123abc"
  1051. GCMSenderID:@"correct_gcm_sender_id"];
  1052. options.APIKey = @"correct_api_key";
  1053. options.projectID = @"abc-xyz-123";
  1054. return options;
  1055. }
  1056. - (FIROptions *)secondAppOptions {
  1057. FIROptions *options =
  1058. [[FIROptions alloc] initWithContentsOfFile:[[NSBundle bundleForClass:[self class]]
  1059. pathForResource:@"SecondApp-GoogleService-Info"
  1060. ofType:@"plist"]];
  1061. XCTAssertNotNil(options);
  1062. return options;
  1063. }
  1064. @end