RCNRemoteConfigTest.m 72 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461
  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 "OCMStubRecorder.h"
  18. #import "OCMock.h"
  19. #import "FirebaseRemoteConfig/Sources/Private/FIRRemoteConfig_Private.h"
  20. #import "FirebaseRemoteConfig/Sources/Private/RCNConfigFetch.h"
  21. #import "FirebaseRemoteConfig/Sources/Public/FirebaseRemoteConfig/FIRRemoteConfig.h"
  22. #import "FirebaseRemoteConfig/Sources/RCNConfigConstants.h"
  23. #import "FirebaseRemoteConfig/Sources/RCNConfigDBManager.h"
  24. #import "FirebaseRemoteConfig/Sources/RCNConfigExperiment.h"
  25. #import "FirebaseRemoteConfig/Sources/RCNUserDefaultsManager.h"
  26. #import "FirebaseRemoteConfig/Tests/Unit/RCNTestUtilities.h"
  27. #import <GoogleUtilities/GULNSData+zlib.h>
  28. #import "FirebaseCore/Sources/Private/FirebaseCoreInternal.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<id> *_configFetch;
  86. RCNConfigDBManager *_DBManager;
  87. NSUserDefaults *_userDefaults;
  88. NSString *_userDefaultsSuiteName;
  89. NSString *_DBPath;
  90. id _DBManagerMock;
  91. id _experimentMock;
  92. id _userDefaultsMock;
  93. NSString *_fullyQualifiedNamespace;
  94. RCNConfigSettings *_settings;
  95. dispatch_queue_t _queue;
  96. }
  97. @end
  98. @implementation RCNRemoteConfigTest
  99. - (void)setUp {
  100. [super setUp];
  101. FIRSetLoggerLevel(FIRLoggerLevelMax);
  102. _expectationTimeout = 5;
  103. _checkCompletionTimeout = 1.0;
  104. // Always remove the database at the start of testing.
  105. _DBPath = [RCNTestUtilities remoteConfigPathForTestDatabase];
  106. _DBManagerMock = OCMClassMock([RCNConfigDBManager class]);
  107. OCMStub([_DBManagerMock remoteConfigPathForDatabase]).andReturn(_DBPath);
  108. _DBManager = [[RCNConfigDBManager alloc] init];
  109. _userDefaultsSuiteName = [RCNTestUtilities userDefaultsSuiteNameForTestSuite];
  110. _userDefaults = [[NSUserDefaults alloc] initWithSuiteName:_userDefaultsSuiteName];
  111. _userDefaultsMock = OCMClassMock([RCNUserDefaultsManager class]);
  112. OCMStub([_userDefaultsMock sharedUserDefaultsForBundleIdentifier:[OCMArg any]])
  113. .andReturn(_userDefaults);
  114. _experimentMock = OCMClassMock([RCNConfigExperiment class]);
  115. OCMStub([_experimentMock
  116. updateExperimentsWithHandler:([OCMArg invokeBlockWithArgs:[NSNull null], nil])]);
  117. RCNConfigContent *configContent = [[RCNConfigContent alloc] initWithDBManager:_DBManager];
  118. _configInstances = [[NSMutableArray alloc] initWithCapacity:3];
  119. _entries = [[NSMutableArray alloc] initWithCapacity:3];
  120. _response = [[NSMutableArray alloc] initWithCapacity:3];
  121. _responseData = [[NSMutableArray alloc] initWithCapacity:3];
  122. _URLResponse = [[NSMutableArray alloc] initWithCapacity:3];
  123. _configFetch = [[NSMutableArray alloc] initWithCapacity:3];
  124. // Populate the default, second app, second namespace instances.
  125. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  126. // Fake a response for default instance.
  127. NSMutableDictionary<NSString *, NSString *> *valuesDict = [[NSMutableDictionary alloc] init];
  128. for (int count = 1; count <= 100; count++) {
  129. NSString *key = [NSString stringWithFormat:@"key%d-%d", count, i];
  130. NSString *value = [NSString stringWithFormat:@"value%d-%d", count, i];
  131. valuesDict[key] = value;
  132. }
  133. _entries[i] = valuesDict;
  134. NSString *currentAppName = nil;
  135. FIROptions *currentOptions = nil;
  136. NSString *currentNamespace = nil;
  137. switch (i) {
  138. case RCNTestRCInstanceSecondNamespace:
  139. currentAppName = RCNTestsDefaultFIRAppName;
  140. currentOptions = [self firstAppOptions];
  141. currentNamespace = RCNTestsPerfNamespace;
  142. break;
  143. case RCNTestRCInstanceSecondApp:
  144. currentAppName = RCNTestsSecondFIRAppName;
  145. currentOptions = [self secondAppOptions];
  146. currentNamespace = FIRNamespaceGoogleMobilePlatform;
  147. break;
  148. case RCNTestRCInstanceDefault:
  149. default:
  150. currentAppName = RCNTestsDefaultFIRAppName;
  151. currentOptions = [self firstAppOptions];
  152. currentNamespace = RCNTestsFIRNamespace;
  153. break;
  154. }
  155. _fullyQualifiedNamespace =
  156. [NSString stringWithFormat:@"%@:%@", currentNamespace, currentAppName];
  157. FIRRemoteConfig *config =
  158. OCMPartialMock([[FIRRemoteConfig alloc] initWithAppName:currentAppName
  159. FIROptions:currentOptions
  160. namespace:currentNamespace
  161. DBManager:_DBManager
  162. configContent:configContent
  163. analytics:nil]);
  164. _configInstances[i] = config;
  165. _settings = [[RCNConfigSettings alloc] initWithDatabaseManager:_DBManager
  166. namespace:_fullyQualifiedNamespace
  167. firebaseAppName:currentAppName
  168. googleAppID:currentOptions.googleAppID];
  169. _queue = dispatch_queue_create(
  170. [[NSString stringWithFormat:@"testqueue: %d", i] cStringUsingEncoding:NSUTF8StringEncoding],
  171. DISPATCH_QUEUE_SERIAL);
  172. _configFetch[i] =
  173. OCMPartialMock([[RCNConfigFetch alloc] initWithContent:configContent
  174. DBManager:_DBManager
  175. settings:_settings
  176. analytics:nil
  177. experiment:_experimentMock
  178. queue:_queue
  179. namespace:_fullyQualifiedNamespace
  180. options:currentOptions]);
  181. OCMStubRecorder *mock = OCMStub([_configFetch[i] fetchConfigWithExpirationDuration:0
  182. completionHandler:OCMOCK_ANY]);
  183. mock = [mock ignoringNonObjectArgs];
  184. mock.andDo(^(NSInvocation *invocation) {
  185. __unsafe_unretained void (^handler)(FIRRemoteConfigFetchStatus status,
  186. NSError *_Nullable error) = nil;
  187. [invocation getArgument:&handler atIndex:3];
  188. [self->_configFetch[i] fetchWithUserProperties:[[NSDictionary alloc] init]
  189. completionHandler:handler];
  190. });
  191. _response[i] = @{@"state" : @"UPDATE", @"entries" : _entries[i]};
  192. _responseData[i] = [NSJSONSerialization dataWithJSONObject:_response[i] options:0 error:nil];
  193. _URLResponse[i] = [[NSHTTPURLResponse alloc]
  194. initWithURL:[NSURL URLWithString:@"https://firebase.com"]
  195. statusCode:200
  196. HTTPVersion:nil
  197. headerFields:@{@"etag" : [NSString stringWithFormat:@"etag1-%d", i]}];
  198. id completionBlock =
  199. [OCMArg invokeBlockWithArgs:_responseData[i], _URLResponse[i], [NSNull null], nil];
  200. OCMStub([_configFetch[i] URLSessionDataTaskWithContent:[OCMArg any]
  201. completionHandler:completionBlock])
  202. .andReturn(nil);
  203. [_configInstances[i] updateWithNewInstancesForConfigFetch:_configFetch[i]
  204. configContent:configContent
  205. configSettings:_settings
  206. configExperiment:_experimentMock];
  207. }
  208. }
  209. - (void)tearDown {
  210. [_DBManager removeDatabaseOnDatabaseQueueAtPath:_DBPath];
  211. [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:_userDefaultsSuiteName];
  212. [_DBManagerMock stopMocking];
  213. _DBManagerMock = nil;
  214. [_userDefaultsMock stopMocking];
  215. _userDefaultsMock = nil;
  216. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  217. [(id)_configInstances[i] stopMocking];
  218. [(id)_configFetch[i] stopMocking];
  219. }
  220. [_configInstances removeAllObjects];
  221. [_configFetch removeAllObjects];
  222. _configInstances = nil;
  223. _configFetch = nil;
  224. [super tearDown];
  225. }
  226. - (void)testFetchConfigWithNilCallback {
  227. NSMutableArray<XCTestExpectation *> *expectations =
  228. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  229. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  230. expectations[i] = [self
  231. expectationWithDescription:
  232. [NSString stringWithFormat:@"Set defaults no callback expectation - instance %d", i]];
  233. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  234. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:nil];
  235. dispatch_after(
  236. dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_checkCompletionTimeout * NSEC_PER_SEC)),
  237. dispatch_get_main_queue(), ^{
  238. XCTAssertEqual(self->_configInstances[i].lastFetchStatus,
  239. FIRRemoteConfigFetchStatusSuccess);
  240. [expectations[i] fulfill];
  241. });
  242. }
  243. [self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
  244. }
  245. - (void)testFetchConfigsSuccessfully {
  246. NSMutableArray<XCTestExpectation *> *expectations =
  247. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  248. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  249. expectations[i] =
  250. [self expectationWithDescription:
  251. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  252. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  253. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  254. NSError *error) {
  255. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  256. XCTAssertNil(error);
  257. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  258. XCTAssertTrue(changed);
  259. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  260. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  261. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  262. NSString *value2 = [NSString stringWithFormat:@"value2-%d", i];
  263. XCTAssertEqualObjects(self->_configInstances[i][key1].stringValue, value1);
  264. XCTAssertEqualObjects(self->_configInstances[i][key2].stringValue, value2);
  265. OCMVerify([self->_configInstances[i] objectForKeyedSubscript:key1]);
  266. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  267. @"Callback of first successful config "
  268. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccessFresh.");
  269. XCTAssertNotNil(self->_configInstances[i].lastFetchTime);
  270. XCTAssertGreaterThan(self->_configInstances[i].lastFetchTime.timeIntervalSince1970, 0,
  271. @"last fetch time interval should be set.");
  272. [expectations[i] fulfill];
  273. }];
  274. };
  275. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  276. }
  277. [self waitForExpectationsWithTimeout:_expectationTimeout
  278. handler:^(NSError *error) {
  279. XCTAssertNil(error);
  280. }];
  281. }
  282. - (void)testFetchAndActivate {
  283. NSMutableArray<XCTestExpectation *> *expectations =
  284. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  285. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  286. expectations[i] =
  287. [self expectationWithDescription:
  288. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  289. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  290. FIRRemoteConfigFetchAndActivateCompletion fetchAndActivateCompletion = ^void(
  291. FIRRemoteConfigFetchAndActivateStatus status, NSError *error) {
  292. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  293. XCTAssertNil(error);
  294. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  295. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  296. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  297. NSString *value2 = [NSString stringWithFormat:@"value2-%d", i];
  298. XCTAssertEqualObjects(self->_configInstances[i][key1].stringValue, value1);
  299. XCTAssertEqualObjects(self->_configInstances[i][key2].stringValue, value2);
  300. OCMVerify([self->_configInstances[i] objectForKeyedSubscript:key1]);
  301. XCTAssertEqual(
  302. status, FIRRemoteConfigFetchAndActivateStatusSuccessFetchedFromRemote,
  303. @"Callback of first successful config "
  304. @"fetchAndActivate status must equal to FIRRemoteConfigFetchAndStatusSuccessFromRemote.");
  305. XCTAssertNotNil(self->_configInstances[i].lastFetchTime);
  306. XCTAssertGreaterThan(self->_configInstances[i].lastFetchTime.timeIntervalSince1970, 0,
  307. @"last fetch time interval should be set.");
  308. [expectations[i] fulfill];
  309. };
  310. // Update the minimum fetch interval to 0. This disables the cache and forces a remote fetch
  311. // request.
  312. FIRRemoteConfigSettings *settings = [[FIRRemoteConfigSettings alloc] init];
  313. settings.minimumFetchInterval = 0;
  314. [_configInstances[i] setConfigSettings:settings];
  315. [_configInstances[i] fetchAndActivateWithCompletionHandler:fetchAndActivateCompletion];
  316. }
  317. [self waitForExpectationsWithTimeout:_expectationTimeout
  318. handler:^(NSError *error) {
  319. XCTAssertNil(error);
  320. }];
  321. }
  322. // TODO: Try splitting into smaller tests.
  323. - (void)testFetchConfigsSuccessfullyWithNewActivateMethodSignature {
  324. NSMutableArray<XCTestExpectation *> *expectations =
  325. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  326. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  327. expectations[i] =
  328. [self expectationWithDescription:
  329. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  330. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  331. FIRRemoteConfigFetchCompletion fetchCompletion = ^(FIRRemoteConfigFetchStatus status,
  332. NSError *error) {
  333. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  334. XCTAssertNil(error);
  335. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  336. XCTAssertTrue(changed);
  337. XCTAssertNil(error);
  338. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  339. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  340. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  341. NSString *value2 = [NSString stringWithFormat:@"value2-%d", i];
  342. XCTAssertEqualObjects(self->_configInstances[i][key1].stringValue, value1);
  343. XCTAssertEqualObjects(self->_configInstances[i][key2].stringValue, value2);
  344. OCMVerify([self->_configInstances[i] objectForKeyedSubscript:key1]);
  345. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  346. @"Callback of first successful config "
  347. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccessFresh.");
  348. XCTAssertNotNil(self->_configInstances[i].lastFetchTime);
  349. XCTAssertGreaterThan(self->_configInstances[i].lastFetchTime.timeIntervalSince1970, 0,
  350. @"last fetch time interval should be set.");
  351. // A second activate should have no effect.
  352. [self->_configInstances[i]
  353. activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  354. XCTAssertFalse(changed);
  355. XCTAssertNil(error);
  356. }];
  357. [expectations[i] fulfill];
  358. }];
  359. };
  360. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  361. }
  362. [self waitForExpectationsWithTimeout:_expectationTimeout
  363. handler:^(NSError *error) {
  364. XCTAssertNil(error);
  365. }];
  366. }
  367. - (void)testEnumeratingConfigResults {
  368. NSMutableArray<XCTestExpectation *> *expectations =
  369. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  370. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  371. expectations[i] = [self
  372. expectationWithDescription:
  373. [NSString stringWithFormat:@"Test enumerating configs successfully - instance %d", i]];
  374. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  375. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  376. NSError *error) {
  377. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  378. XCTAssertNil(error);
  379. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  380. XCTAssertTrue(changed);
  381. NSString *key5 = [NSString stringWithFormat:@"key5-%d", i];
  382. NSString *key19 = [NSString stringWithFormat:@"key19-%d", i];
  383. NSString *value5 = [NSString stringWithFormat:@"value5-%d", i];
  384. NSString *value19 = [NSString stringWithFormat:@"value19-%d", i];
  385. XCTAssertEqualObjects(self->_configInstances[i][key5].stringValue, value5);
  386. XCTAssertEqualObjects(self->_configInstances[i][key19].stringValue, value19);
  387. // Test enumerating config values.
  388. for (NSString *key in self->_configInstances[i]) {
  389. if ([key isEqualToString:key5]) {
  390. XCTAssertEqualObjects(self->_configInstances[i][key5].stringValue, value5);
  391. }
  392. if ([key isEqualToString:key19]) {
  393. XCTAssertEqualObjects(self->_configInstances[i][key19].stringValue, value19);
  394. }
  395. }
  396. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  397. @"Callback of first successful config "
  398. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccessFresh.");
  399. XCTAssertNotNil(self->_configInstances[i].lastFetchTime);
  400. XCTAssertGreaterThan(self->_configInstances[i].lastFetchTime.timeIntervalSince1970, 0,
  401. @"last fetch time interval should be set.");
  402. [expectations[i] fulfill];
  403. }];
  404. };
  405. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  406. }
  407. [self waitForExpectationsWithTimeout:_expectationTimeout
  408. handler:^(NSError *error) {
  409. XCTAssertNil(error);
  410. }];
  411. }
  412. - (void)testFetch3pNamespaceUpdatesExperiments {
  413. [[_experimentMock expect] updateExperimentsWithResponse:[OCMArg any]];
  414. XCTestExpectation *expectation = [self
  415. expectationWithDescription:
  416. [NSString
  417. stringWithFormat:@"Fetch call for 'firebase' namespace updates experiment data"]];
  418. XCTAssertEqual(_configInstances[RCNTestRCInstanceDefault].lastFetchStatus,
  419. FIRRemoteConfigFetchStatusNoFetchYet);
  420. FIRRemoteConfigFetchCompletion fetchCompletion =
  421. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  422. XCTAssertEqual(self->_configInstances[RCNTestRCInstanceDefault].lastFetchStatus,
  423. FIRRemoteConfigFetchStatusSuccess);
  424. XCTAssertNil(error);
  425. [expectation fulfill];
  426. };
  427. [_configInstances[RCNTestRCInstanceDefault] fetchWithExpirationDuration:43200
  428. completionHandler:fetchCompletion];
  429. [self waitForExpectationsWithTimeout:_expectationTimeout
  430. handler:^(NSError *error) {
  431. XCTAssertNil(error);
  432. }];
  433. }
  434. - (void)testFetchOtherNamespaceDoesntUpdateExperiments {
  435. [[_experimentMock reject] updateExperimentsWithResponse:[OCMArg any]];
  436. XCTestExpectation *expectation =
  437. [self expectationWithDescription:
  438. [NSString stringWithFormat:@"Fetch call for namespace other than 'firebase' "
  439. @"doesn't update experiment data"]];
  440. XCTAssertEqual(_configInstances[RCNTestRCInstanceSecondNamespace].lastFetchStatus,
  441. FIRRemoteConfigFetchStatusNoFetchYet);
  442. FIRRemoteConfigFetchCompletion fetchCompletion =
  443. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  444. XCTAssertEqual(self->_configInstances[RCNTestRCInstanceSecondNamespace].lastFetchStatus,
  445. FIRRemoteConfigFetchStatusSuccess);
  446. XCTAssertNil(error);
  447. [expectation fulfill];
  448. };
  449. [_configInstances[RCNTestRCInstanceSecondNamespace] fetchWithExpirationDuration:43200
  450. completionHandler:fetchCompletion];
  451. [self waitForExpectationsWithTimeout:_expectationTimeout
  452. handler:^(NSError *error) {
  453. XCTAssertNil(error);
  454. }];
  455. }
  456. - (void)testFetchConfigsFailed {
  457. // Override the setup values to return back an error status.
  458. RCNConfigContent *configContent = [[RCNConfigContent alloc] initWithDBManager:_DBManager];
  459. // Populate the default, second app, second namespace instances.
  460. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  461. NSString *currentAppName = nil;
  462. FIROptions *currentOptions = nil;
  463. NSString *currentNamespace = nil;
  464. switch (i) {
  465. case RCNTestRCInstanceSecondNamespace:
  466. currentAppName = RCNTestsDefaultFIRAppName;
  467. currentOptions = [self firstAppOptions];
  468. currentNamespace = RCNTestsPerfNamespace;
  469. break;
  470. case RCNTestRCInstanceSecondApp:
  471. currentAppName = RCNTestsSecondFIRAppName;
  472. currentOptions = [self secondAppOptions];
  473. currentNamespace = FIRNamespaceGoogleMobilePlatform;
  474. break;
  475. case RCNTestRCInstanceDefault:
  476. default:
  477. currentAppName = RCNTestsDefaultFIRAppName;
  478. currentOptions = [self firstAppOptions];
  479. currentNamespace = RCNTestsFIRNamespace;
  480. break;
  481. }
  482. RCNUserDefaultsManager *userDefaultsManager =
  483. [[RCNUserDefaultsManager alloc] initWithAppName:currentAppName
  484. bundleID:[NSBundle mainBundle].bundleIdentifier
  485. namespace:_fullyQualifiedNamespace];
  486. userDefaultsManager.lastFetchTime = 0;
  487. FIRRemoteConfig *config =
  488. OCMPartialMock([[FIRRemoteConfig alloc] initWithAppName:currentAppName
  489. FIROptions:currentOptions
  490. namespace:currentNamespace
  491. DBManager:_DBManager
  492. configContent:configContent
  493. analytics:nil]);
  494. _configInstances[i] = config;
  495. _configFetch[i] =
  496. OCMPartialMock([[RCNConfigFetch alloc] initWithContent:configContent
  497. DBManager:_DBManager
  498. settings:_settings
  499. analytics:nil
  500. experiment:nil
  501. queue:_queue
  502. namespace:_fullyQualifiedNamespace
  503. options:currentOptions]);
  504. OCMStub([_configFetch[i] fetchConfigWithExpirationDuration:43200 completionHandler:OCMOCK_ANY])
  505. .andDo(^(NSInvocation *invocation) {
  506. __unsafe_unretained void (^handler)(FIRRemoteConfigFetchStatus status,
  507. NSError *_Nullable error) = nil;
  508. [invocation getArgument:&handler atIndex:3];
  509. [self->_configFetch[i] fetchWithUserProperties:[[NSDictionary alloc] init]
  510. completionHandler:handler];
  511. });
  512. _response[i] = @{};
  513. _responseData[i] = [NSJSONSerialization dataWithJSONObject:_response[i] options:0 error:nil];
  514. _URLResponse[i] =
  515. [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"https://firebase.com"]
  516. statusCode:500
  517. HTTPVersion:nil
  518. headerFields:@{@"etag" : @"etag1"}];
  519. [_configInstances[i] updateWithNewInstancesForConfigFetch:_configFetch[i]
  520. configContent:configContent
  521. configSettings:_settings
  522. configExperiment:nil];
  523. }
  524. // Make the fetch calls for all instances.
  525. NSMutableArray<XCTestExpectation *> *expectations =
  526. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  527. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  528. expectations[i] = [self
  529. expectationWithDescription:
  530. [NSString stringWithFormat:@"Test enumerating configs successfully - instance %d", i]];
  531. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  532. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  533. NSError *error) {
  534. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusFailure);
  535. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  536. XCTAssertFalse(changed);
  537. XCTAssertNil(error);
  538. FIRRemoteConfigValue *value = self->_configInstances[RCNTestRCInstanceDefault][@"key1"];
  539. XCTAssertEqual((int)value.source, (int)FIRRemoteConfigSourceStatic);
  540. XCTAssertEqualObjects(value.stringValue, @"");
  541. XCTAssertEqual(value.boolValue, NO);
  542. [expectations[i] fulfill];
  543. }];
  544. };
  545. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  546. }
  547. [self waitForExpectationsWithTimeout:_expectationTimeout
  548. handler:^(NSError *error) {
  549. XCTAssertNil(error);
  550. }];
  551. }
  552. // TODO(mandard): Break up test with helper methods.
  553. - (void)testFetchConfigsFailedErrorNoNetwork {
  554. // Override the setup values to return back an error status.
  555. RCNConfigContent *configContent = [[RCNConfigContent alloc] initWithDBManager:_DBManager];
  556. // Populate the default, second app, second namespace instances.
  557. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  558. NSString *currentAppName = nil;
  559. FIROptions *currentOptions = nil;
  560. NSString *currentNamespace = nil;
  561. switch (i) {
  562. case RCNTestRCInstanceSecondNamespace:
  563. currentAppName = RCNTestsDefaultFIRAppName;
  564. currentOptions = [self firstAppOptions];
  565. currentNamespace = RCNTestsPerfNamespace;
  566. break;
  567. case RCNTestRCInstanceSecondApp:
  568. currentAppName = RCNTestsSecondFIRAppName;
  569. currentOptions = [self secondAppOptions];
  570. currentNamespace = FIRNamespaceGoogleMobilePlatform;
  571. break;
  572. case RCNTestRCInstanceDefault:
  573. default:
  574. currentAppName = RCNTestsDefaultFIRAppName;
  575. currentOptions = [self firstAppOptions];
  576. currentNamespace = RCNTestsFIRNamespace;
  577. break;
  578. }
  579. NSString *fullyQualifiedNamespace =
  580. [NSString stringWithFormat:@"%@:%@", currentNamespace, currentAppName];
  581. RCNUserDefaultsManager *userDefaultsManager =
  582. [[RCNUserDefaultsManager alloc] initWithAppName:currentAppName
  583. bundleID:[NSBundle mainBundle].bundleIdentifier
  584. namespace:fullyQualifiedNamespace];
  585. userDefaultsManager.lastFetchTime = 0;
  586. FIRRemoteConfig *config =
  587. OCMPartialMock([[FIRRemoteConfig alloc] initWithAppName:currentAppName
  588. FIROptions:currentOptions
  589. namespace:currentNamespace
  590. DBManager:_DBManager
  591. configContent:configContent
  592. analytics:nil]);
  593. _configInstances[i] = config;
  594. RCNConfigSettings *settings =
  595. [[RCNConfigSettings alloc] initWithDatabaseManager:_DBManager
  596. namespace:fullyQualifiedNamespace
  597. firebaseAppName:currentAppName
  598. googleAppID:currentOptions.googleAppID];
  599. dispatch_queue_t queue = dispatch_queue_create(
  600. [[NSString stringWithFormat:@"testqueue: %d", i] cStringUsingEncoding:NSUTF8StringEncoding],
  601. DISPATCH_QUEUE_SERIAL);
  602. _configFetch[i] = OCMPartialMock([[RCNConfigFetch alloc] initWithContent:configContent
  603. DBManager:_DBManager
  604. settings:settings
  605. analytics:nil
  606. experiment:nil
  607. queue:queue
  608. namespace:fullyQualifiedNamespace
  609. options:currentOptions]);
  610. OCMStub([_configFetch[i] fetchConfigWithExpirationDuration:43200 completionHandler:OCMOCK_ANY])
  611. .andDo(^(NSInvocation *invocation) {
  612. __unsafe_unretained void (^handler)(FIRRemoteConfigFetchStatus status,
  613. NSError *_Nullable error) = nil;
  614. [invocation getArgument:&handler atIndex:3];
  615. [self->_configFetch[i] fetchWithUserProperties:[[NSDictionary alloc] init]
  616. completionHandler:handler];
  617. });
  618. _response[i] = @{};
  619. _responseData[i] = [NSJSONSerialization dataWithJSONObject:_response[i] options:0 error:nil];
  620. // A no network error is accompanied with an HTTP status code of 0.
  621. _URLResponse[i] =
  622. [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"https://firebase.com"]
  623. statusCode:0
  624. HTTPVersion:nil
  625. headerFields:@{@"etag" : @"etag1"}];
  626. [_configInstances[i] updateWithNewInstancesForConfigFetch:_configFetch[i]
  627. configContent:configContent
  628. configSettings:settings
  629. configExperiment:nil];
  630. }
  631. // Make the fetch calls for all instances.
  632. NSMutableArray<XCTestExpectation *> *expectations =
  633. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  634. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  635. expectations[i] = [self
  636. expectationWithDescription:
  637. [NSString stringWithFormat:@"Test enumerating configs successfully - instance %d", i]];
  638. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  639. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  640. NSError *error) {
  641. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusFailure);
  642. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  643. XCTAssertFalse(changed);
  644. XCTAssertNil(error);
  645. // No such key, still return a static value.
  646. FIRRemoteConfigValue *value = self->_configInstances[RCNTestRCInstanceDefault][@"key1"];
  647. XCTAssertEqual((int)value.source, (int)FIRRemoteConfigSourceStatic);
  648. XCTAssertEqualObjects(value.stringValue, @"");
  649. XCTAssertEqual(value.boolValue, NO);
  650. [expectations[i] fulfill];
  651. }];
  652. };
  653. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  654. }
  655. [self waitForExpectationsWithTimeout:_expectationTimeout
  656. handler:^(NSError *error) {
  657. XCTAssertNil(error);
  658. }];
  659. }
  660. - (void)testFetchFailedNoNetworkErrorDoesNotThrottle {
  661. RCNConfigContent *configContent = [[RCNConfigContent alloc] initWithDBManager:_DBManager];
  662. NSString *currentAppName = RCNTestsDefaultFIRAppName;
  663. FIROptions *currentOptions = [self firstAppOptions];
  664. NSString *currentNamespace = RCNTestsFIRNamespace;
  665. NSString *fullyQualifiedNamespace =
  666. [NSString stringWithFormat:@"%@:%@", currentNamespace, currentAppName];
  667. RCNUserDefaultsManager *userDefaultsManager =
  668. [[RCNUserDefaultsManager alloc] initWithAppName:currentAppName
  669. bundleID:[NSBundle mainBundle].bundleIdentifier
  670. namespace:fullyQualifiedNamespace];
  671. userDefaultsManager.lastFetchTime = 0;
  672. FIRRemoteConfig *config = OCMPartialMock([[FIRRemoteConfig alloc] initWithAppName:currentAppName
  673. FIROptions:currentOptions
  674. namespace:currentNamespace
  675. DBManager:_DBManager
  676. configContent:configContent
  677. analytics:nil]);
  678. RCNConfigSettings *settings =
  679. [[RCNConfigSettings alloc] initWithDatabaseManager:_DBManager
  680. namespace:fullyQualifiedNamespace
  681. firebaseAppName:currentAppName
  682. googleAppID:currentOptions.googleAppID];
  683. dispatch_queue_t queue = dispatch_queue_create(
  684. [[NSString stringWithFormat:@"testqueue"] cStringUsingEncoding:NSUTF8StringEncoding],
  685. DISPATCH_QUEUE_SERIAL);
  686. RCNConfigFetch *configFetch =
  687. OCMPartialMock([[RCNConfigFetch alloc] initWithContent:configContent
  688. DBManager:_DBManager
  689. settings:settings
  690. analytics:nil
  691. experiment:nil
  692. queue:queue
  693. namespace:fullyQualifiedNamespace
  694. options:currentOptions]);
  695. OCMStub([configFetch fetchConfigWithExpirationDuration:43200 completionHandler:OCMOCK_ANY])
  696. .andDo(^(NSInvocation *invocation) {
  697. __unsafe_unretained void (^handler)(FIRRemoteConfigFetchStatus status,
  698. NSError *_Nullable error) = nil;
  699. [invocation getArgument:&handler atIndex:3];
  700. [configFetch fetchWithUserProperties:[[NSDictionary alloc] init] completionHandler:handler];
  701. });
  702. _responseData[0] = [NSJSONSerialization dataWithJSONObject:@{} options:0 error:nil];
  703. // A no network error is accompanied with an HTTP status code of 0.
  704. _URLResponse[0] =
  705. [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"https://firebase.com"]
  706. statusCode:0
  707. HTTPVersion:nil
  708. headerFields:@{@"etag" : @"etag1"}];
  709. [config updateWithNewInstancesForConfigFetch:configFetch
  710. configContent:configContent
  711. configSettings:settings
  712. configExperiment:nil];
  713. XCTestExpectation *expectation =
  714. [self expectationWithDescription:@"Network error doesn't increase throttle interval"];
  715. XCTAssertEqual(config.lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  716. FIRRemoteConfigFetchCompletion fetchCompletion =
  717. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  718. XCTAssertEqual(config.lastFetchStatus, FIRRemoteConfigFetchStatusFailure);
  719. XCTAssertEqual(settings.exponentialBackoffRetryInterval, 0);
  720. [expectation fulfill];
  721. };
  722. [config fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  723. [self waitForExpectationsWithTimeout:_expectationTimeout
  724. handler:^(NSError *error) {
  725. XCTAssertNil(error);
  726. }];
  727. }
  728. // Activate should return false if a fetch response returns 200 with NO_CHANGE as the response body.
  729. - (void)testActivateOnFetchNoChangeStatus {
  730. // Override the setup values to return back an error status.
  731. RCNConfigContent *configContent = [[RCNConfigContent alloc] initWithDBManager:_DBManager];
  732. // Populate the default, second app, second namespace instances.
  733. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  734. NSString *currentAppName = nil;
  735. FIROptions *currentOptions = nil;
  736. NSString *currentNamespace = nil;
  737. switch (i) {
  738. case RCNTestRCInstanceSecondNamespace:
  739. currentAppName = RCNTestsDefaultFIRAppName;
  740. currentOptions = [self firstAppOptions];
  741. currentNamespace = RCNTestsPerfNamespace;
  742. break;
  743. case RCNTestRCInstanceSecondApp:
  744. currentAppName = RCNTestsSecondFIRAppName;
  745. currentOptions = [self secondAppOptions];
  746. currentNamespace = FIRNamespaceGoogleMobilePlatform;
  747. break;
  748. case RCNTestRCInstanceDefault:
  749. default:
  750. currentAppName = RCNTestsDefaultFIRAppName;
  751. currentOptions = [self firstAppOptions];
  752. currentNamespace = RCNTestsFIRNamespace;
  753. break;
  754. }
  755. NSString *fullyQualifiedNamespace =
  756. [NSString stringWithFormat:@"%@:%@", currentNamespace, currentAppName];
  757. RCNUserDefaultsManager *userDefaultsManager =
  758. [[RCNUserDefaultsManager alloc] initWithAppName:currentAppName
  759. bundleID:[NSBundle mainBundle].bundleIdentifier
  760. namespace:fullyQualifiedNamespace];
  761. userDefaultsManager.lastFetchTime = 10;
  762. FIRRemoteConfig *config =
  763. OCMPartialMock([[FIRRemoteConfig alloc] initWithAppName:currentAppName
  764. FIROptions:currentOptions
  765. namespace:currentNamespace
  766. DBManager:_DBManager
  767. configContent:configContent
  768. analytics:nil]);
  769. _configInstances[i] = config;
  770. RCNConfigSettings *settings =
  771. [[RCNConfigSettings alloc] initWithDatabaseManager:_DBManager
  772. namespace:fullyQualifiedNamespace
  773. firebaseAppName:currentAppName
  774. googleAppID:currentOptions.googleAppID];
  775. // Start the test with the assumption that we have some data that was fetched and activated.
  776. settings.lastETag = @"etag1";
  777. settings.lastETagUpdateTime = 100;
  778. settings.lastApplyTimeInterval = 101;
  779. dispatch_queue_t queue =
  780. dispatch_queue_create([[NSString stringWithFormat:@"testNoStatusFetchQueue: %d", i]
  781. cStringUsingEncoding:NSUTF8StringEncoding],
  782. DISPATCH_QUEUE_SERIAL);
  783. _configFetch[i] = OCMPartialMock([[RCNConfigFetch alloc] initWithContent:configContent
  784. DBManager:_DBManager
  785. settings:settings
  786. analytics:nil
  787. experiment:nil
  788. queue:queue
  789. namespace:fullyQualifiedNamespace
  790. options:currentOptions]);
  791. OCMStub([_configFetch[i] fetchConfigWithExpirationDuration:43200 completionHandler:OCMOCK_ANY])
  792. .andDo(^(NSInvocation *invocation) {
  793. __unsafe_unretained void (^handler)(FIRRemoteConfigFetchStatus status,
  794. NSError *_Nullable error) = nil;
  795. [invocation getArgument:&handler atIndex:3];
  796. [self->_configFetch[i] fetchWithUserProperties:[[NSDictionary alloc] init]
  797. completionHandler:handler];
  798. });
  799. _response[i] = @{@"state" : @"NO_CHANGE"};
  800. _responseData[i] = [NSJSONSerialization dataWithJSONObject:_response[i] options:0 error:nil];
  801. _URLResponse[i] =
  802. [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"https://firebase.com"]
  803. statusCode:200
  804. HTTPVersion:nil
  805. headerFields:@{@"etag" : @"etag1"}];
  806. id completionBlock =
  807. [OCMArg invokeBlockWithArgs:_responseData[i], _URLResponse[i], [NSNull null], nil];
  808. OCMStub([_configFetch[i] URLSessionDataTaskWithContent:[OCMArg any]
  809. completionHandler:completionBlock])
  810. .andReturn(nil);
  811. [_configInstances[i] updateWithNewInstancesForConfigFetch:_configFetch[i]
  812. configContent:configContent
  813. configSettings:settings
  814. configExperiment:nil];
  815. }
  816. // Make the fetch calls for all instances.
  817. NSMutableArray<XCTestExpectation *> *expectations =
  818. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  819. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  820. expectations[i] = [self
  821. expectationWithDescription:
  822. [NSString stringWithFormat:@"Test enumerating configs successfully - instance %d", i]];
  823. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  824. // Make sure activate returns false in fetch completion.
  825. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  826. NSError *error) {
  827. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  828. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  829. XCTAssertFalse(changed);
  830. XCTAssertNil(error);
  831. [expectations[i] fulfill];
  832. }];
  833. };
  834. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  835. }
  836. [self waitForExpectationsWithTimeout:_expectationTimeout
  837. handler:^(NSError *error) {
  838. XCTAssertNil(error);
  839. }];
  840. }
  841. - (void)testConfigValueForKey {
  842. NSMutableArray<XCTestExpectation *> *expectations =
  843. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  844. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  845. expectations[i] =
  846. [self expectationWithDescription:
  847. [NSString stringWithFormat:@"Test configValueForKey: method - instance %d", i]];
  848. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  849. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  850. NSError *error) {
  851. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess);
  852. XCTAssertNil(error);
  853. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  854. XCTAssertTrue(changed);
  855. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  856. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  857. NSString *key3 = [NSString stringWithFormat:@"key3-%d", i];
  858. NSString *key7 = [NSString stringWithFormat:@"key7-%d", i];
  859. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  860. NSString *value2 = [NSString stringWithFormat:@"value2-%d", i];
  861. NSString *value3 = [NSString stringWithFormat:@"value3-%d", i];
  862. NSString *value7 = [NSString stringWithFormat:@"value7-%d", i];
  863. XCTAssertEqualObjects(self->_configInstances[i][key1].stringValue, value1);
  864. XCTAssertEqualObjects(self->_configInstances[i][key2].stringValue, value2);
  865. OCMVerify([self->_configInstances[i] objectForKeyedSubscript:key1]);
  866. XCTAssertEqualObjects([self->_configInstances[i] configValueForKey:key3].stringValue,
  867. value3);
  868. if (i == RCNTestRCInstanceDefault) {
  869. XCTAssertEqualObjects([self->_configInstances[i] configValueForKey:key7].stringValue,
  870. value7);
  871. }
  872. XCTAssertEqualObjects([self->_configInstances[i] configValueForKey:key7].stringValue,
  873. value7);
  874. XCTAssertNotNil([self->_configInstances[i] configValueForKey:nil]);
  875. XCTAssertEqual([self->_configInstances[i] configValueForKey:nil].source,
  876. FIRRemoteConfigSourceStatic);
  877. XCTAssertEqual([self->_configInstances[i] configValueForKey:nil].source,
  878. FIRRemoteConfigSourceStatic);
  879. XCTAssertEqual([self->_configInstances[i] configValueForKey:nil source:-1].source,
  880. FIRRemoteConfigSourceStatic);
  881. [expectations[i] fulfill];
  882. }];
  883. };
  884. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  885. }
  886. [self waitForExpectationsWithTimeout:_expectationTimeout
  887. handler:^(NSError *error) {
  888. XCTAssertNil(error);
  889. }];
  890. }
  891. - (void)testFetchConfigWithDefaultSets {
  892. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  893. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  894. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  895. fetchConfigsExpectation[i] = [self
  896. expectationWithDescription:
  897. [NSString stringWithFormat:@"Test fetch configs with defaults set - instance %d", i]];
  898. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  899. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  900. NSString *key0 = [NSString stringWithFormat:@"key0-%d", i];
  901. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  902. NSString *value2 = [NSString stringWithFormat:@"value2-%d", i];
  903. NSDictionary<NSString *, NSString *> *defaults = @{key1 : @"default key1", key0 : @"value0-0"};
  904. [_configInstances[i] setDefaults:defaults];
  905. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  906. NSError *error) {
  907. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  908. XCTAssertNil(error);
  909. XCTAssertEqualObjects(self->_configInstances[i][key1].stringValue, @"default key1");
  910. XCTAssertEqual(self->_configInstances[i][key1].source, FIRRemoteConfigSourceDefault);
  911. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  912. XCTAssertTrue(changed);
  913. XCTAssertEqualObjects(self->_configInstances[i][key1].stringValue, value1);
  914. XCTAssertEqual(self->_configInstances[i][key1].source, FIRRemoteConfigSourceRemote);
  915. XCTAssertEqualObjects([self->_configInstances[i] defaultValueForKey:key1].stringValue,
  916. @"default key1");
  917. XCTAssertEqualObjects(self->_configInstances[i][key2].stringValue, value2);
  918. XCTAssertEqualObjects(self->_configInstances[i][key0].stringValue, @"value0-0");
  919. XCTAssertNil([self->_configInstances[i] defaultValueForKey:nil]);
  920. OCMVerify([self->_configInstances[i] objectForKeyedSubscript:key1]);
  921. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  922. @"Callback of first successful config "
  923. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccess.");
  924. [fetchConfigsExpectation[i] fulfill];
  925. }];
  926. };
  927. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  928. }
  929. [self waitForExpectationsWithTimeout:_expectationTimeout
  930. handler:^(NSError *error) {
  931. XCTAssertNil(error);
  932. }];
  933. }
  934. - (void)testDefaultsSetsOnly {
  935. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  936. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  937. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  938. NSString *key3 = [NSString stringWithFormat:@"key3-%d", i];
  939. NSString *key4 = [NSString stringWithFormat:@"key4-%d", i];
  940. NSString *key5 = [NSString stringWithFormat:@"key5-%d", i];
  941. NSString *defaultValue1 = @"default value1";
  942. NSData *defaultValue2 = [defaultValue1 dataUsingEncoding:NSUTF8StringEncoding];
  943. NSNumber *defaultValue3 = [NSNumber numberWithFloat:3.1415926];
  944. NSDate *defaultValue4 = [NSDate date];
  945. BOOL defaultValue5 = NO;
  946. NSMutableDictionary<NSString *, id> *defaults = [NSMutableDictionary dictionaryWithDictionary:@{
  947. key1 : defaultValue1,
  948. key2 : defaultValue2,
  949. key3 : defaultValue3,
  950. key4 : defaultValue4,
  951. key5 : @(defaultValue5),
  952. }];
  953. [_configInstances[i] setDefaults:defaults];
  954. if (i == RCNTestRCInstanceSecondNamespace) {
  955. [defaults setObject:@"2860" forKey:@"experience"];
  956. [_configInstances[i] setDefaults:defaults];
  957. }
  958. // Remove objects right away to make sure dispatch_async gets the copy.
  959. [defaults removeAllObjects];
  960. FIRRemoteConfig *config = _configInstances[i];
  961. XCTAssertEqualObjects(config[key1].stringValue, defaultValue1, @"Should support string format");
  962. XCTAssertEqualObjects(config[key2].dataValue, defaultValue2, @"Should support data format");
  963. XCTAssertEqual(config[key3].numberValue.longValue, defaultValue3.longValue,
  964. @"Should support number format");
  965. NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  966. [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  967. NSString *strValueOfDate = [dateFormatter stringFromDate:(NSDate *)defaultValue4];
  968. XCTAssertEqualObjects(
  969. config[key4].stringValue, strValueOfDate,
  970. @"Date format can be set as an input from plist, but output coming out of "
  971. @"defaultConfig as string.");
  972. XCTAssertEqual(config[key5].boolValue, defaultValue5, @"Should support bool format");
  973. if (i == RCNTestRCInstanceSecondNamespace) {
  974. XCTAssertEqualObjects(
  975. [_configInstances[i] configValueForKey:@"experience"].stringValue, @"2860",
  976. @"Only default config has the key, must equal to default config value.");
  977. }
  978. // Reset default sets
  979. [_configInstances[i] setDefaults:nil];
  980. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceDefault].count, 0);
  981. }
  982. }
  983. - (void)testSetDefaultsWithNilParams {
  984. NSMutableArray<XCTestExpectation *> *expectations =
  985. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  986. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  987. expectations[i] = [self
  988. expectationWithDescription:
  989. [NSString stringWithFormat:@"Set defaults no callback expectation - instance %d", i]];
  990. // Should work when passing nil.
  991. [_configInstances[i] setDefaults:nil];
  992. [_configInstances[i] setDefaults:nil];
  993. dispatch_after(
  994. dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_checkCompletionTimeout * NSEC_PER_SEC)),
  995. dispatch_get_main_queue(), ^{
  996. XCTAssertEqual(
  997. [self->_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceDefault].count, 0);
  998. [expectations[i] fulfill];
  999. });
  1000. }
  1001. [self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
  1002. }
  1003. - (void)testFetchConfigOverwriteDefaultSet {
  1004. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  1005. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  1006. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1007. fetchConfigsExpectation[i] = [self
  1008. expectationWithDescription:
  1009. [NSString stringWithFormat:@"Test fetch configs with defaults set - instance %d", i]];
  1010. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  1011. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  1012. [_configInstances[i] setDefaults:@{key1 : @"default key1"}];
  1013. FIRRemoteConfigValue *value = _configInstances[i][key1];
  1014. XCTAssertEqualObjects(value.stringValue, @"default key1");
  1015. XCTAssertEqual(value.source, FIRRemoteConfigSourceDefault);
  1016. value = _configInstances[i][@"A key doesn't exist"];
  1017. XCTAssertEqual(value.source, FIRRemoteConfigSourceStatic);
  1018. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  1019. NSError *error) {
  1020. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  1021. XCTAssertNil(error);
  1022. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  1023. XCTAssertTrue(changed);
  1024. XCTAssertEqualObjects(self->_configInstances[i][key1].stringValue, value1);
  1025. XCTAssertEqual(self->_configInstances[i][key1].source, FIRRemoteConfigSourceRemote);
  1026. XCTAssertEqualObjects([self->_configInstances[i] defaultValueForKey:key1].stringValue,
  1027. @"default key1");
  1028. OCMVerify([self->_configInstances[i] objectForKeyedSubscript:key1]);
  1029. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  1030. @"Callback of first successful config "
  1031. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccess.");
  1032. [fetchConfigsExpectation[i] fulfill];
  1033. }];
  1034. };
  1035. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  1036. }
  1037. [self waitForExpectationsWithTimeout:_expectationTimeout
  1038. handler:^(NSError *error) {
  1039. XCTAssertNil(error);
  1040. }];
  1041. }
  1042. - (void)testGetConfigValueBySource {
  1043. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  1044. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  1045. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1046. fetchConfigsExpectation[i] =
  1047. [self expectationWithDescription:
  1048. [NSString stringWithFormat:@"Test get config value by source - instance %d", i]];
  1049. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  1050. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  1051. NSDictionary<NSString *, NSString *> *defaults = @{key1 : @"default value1"};
  1052. [_configInstances[i] setDefaults:defaults];
  1053. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  1054. NSError *error) {
  1055. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  1056. XCTAssertNil(error);
  1057. XCTAssertEqualObjects(self->_configInstances[i][key1].stringValue, @"default value1");
  1058. XCTAssertEqual(self->_configInstances[i][key1].source, FIRRemoteConfigSourceDefault);
  1059. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  1060. XCTAssertTrue(changed);
  1061. XCTAssertEqualObjects(self->_configInstances[i][key1].stringValue, value1);
  1062. XCTAssertEqual(self->_configInstances[i][key1].source, FIRRemoteConfigSourceRemote);
  1063. FIRRemoteConfigValue *value;
  1064. if (i == RCNTestRCInstanceDefault) {
  1065. value = [self->_configInstances[i] configValueForKey:key1
  1066. source:FIRRemoteConfigSourceRemote];
  1067. XCTAssertEqualObjects(value.stringValue, value1);
  1068. value = [self->_configInstances[i] configValueForKey:key1
  1069. source:FIRRemoteConfigSourceDefault];
  1070. XCTAssertEqualObjects(value.stringValue, @"default value1");
  1071. value = [self->_configInstances[i] configValueForKey:key1
  1072. source:FIRRemoteConfigSourceStatic];
  1073. } else {
  1074. value = [self->_configInstances[i] configValueForKey:key1
  1075. source:FIRRemoteConfigSourceRemote];
  1076. XCTAssertEqualObjects(value.stringValue, value1);
  1077. value = [self->_configInstances[i] configValueForKey:key1
  1078. source:FIRRemoteConfigSourceDefault];
  1079. XCTAssertEqualObjects(value.stringValue, @"default value1");
  1080. value = [self->_configInstances[i] configValueForKey:key1
  1081. source:FIRRemoteConfigSourceStatic];
  1082. }
  1083. XCTAssertEqualObjects(value.stringValue, @"");
  1084. XCTAssertEqualObjects(value.numberValue, @(0));
  1085. XCTAssertEqual(value.boolValue, NO);
  1086. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  1087. @"Callback of first successful config "
  1088. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccess.");
  1089. [fetchConfigsExpectation[i] fulfill];
  1090. }];
  1091. };
  1092. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  1093. }
  1094. [self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
  1095. }
  1096. - (void)testInvalidKeyOrNamespace {
  1097. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1098. FIRRemoteConfigValue *value = [_configInstances[i] configValueForKey:nil];
  1099. XCTAssertNotNil(value);
  1100. XCTAssertEqual(value.source, FIRRemoteConfigSourceStatic);
  1101. value = [_configInstances[i] configValueForKey:nil];
  1102. XCTAssertNotNil(value);
  1103. XCTAssertEqual(value.source, FIRRemoteConfigSourceStatic);
  1104. value = [_configInstances[i] configValueForKey:nil source:5];
  1105. XCTAssertNotNil(value);
  1106. XCTAssertEqual(value.source, FIRRemoteConfigSourceStatic);
  1107. }
  1108. }
  1109. // Remote Config converts UTC times in the plists to local times. This utility function makes it
  1110. // possible to check the times when running the tests in any timezone.
  1111. static NSString *UTCToLocal(NSString *utcTime) {
  1112. // Create a UTC dateFormatter.
  1113. NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  1114. [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  1115. [dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
  1116. NSDate *date = [dateFormatter dateFromString:utcTime];
  1117. [dateFormatter setTimeZone:[NSTimeZone localTimeZone]];
  1118. return [dateFormatter stringFromDate:date];
  1119. }
  1120. // Manage different bundle locations for Swift Package Manager, CocoaPods static, CocoaPods dynamic.
  1121. - (void)setDefaultsFor:(FIRRemoteConfig *)config {
  1122. #if SWIFT_PACKAGE
  1123. NSBundle *bundle = Firebase_RemoteConfigUnit_SWIFTPM_MODULE_BUNDLE();
  1124. NSString *plistFile = [bundle pathForResource:@"Defaults-testInfo" ofType:@"plist"];
  1125. #else
  1126. NSBundle *bundle = [NSBundle mainBundle];
  1127. NSString *plistFile = [bundle pathForResource:@"Defaults-testInfo" ofType:@"plist"];
  1128. if (plistFile != nil) {
  1129. [config setDefaultsFromPlistFileName:@"Defaults-testInfo"];
  1130. return;
  1131. }
  1132. // We've linked dynamically and the plist file is in the test's bundle.
  1133. for (bundle in [NSBundle allBundles]) {
  1134. plistFile = [bundle pathForResource:@"Defaults-testInfo" ofType:@"plist"];
  1135. if (plistFile != nil) {
  1136. break;
  1137. }
  1138. }
  1139. #endif
  1140. NSDictionary *defaults = [[NSDictionary alloc] initWithContentsOfFile:plistFile];
  1141. [config setDefaults:defaults];
  1142. }
  1143. - (void)testSetDefaultsFromPlist {
  1144. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1145. FIRRemoteConfig *config = _configInstances[i];
  1146. [self setDefaultsFor:config];
  1147. XCTAssertEqualObjects(_configInstances[i][@"lastCheckTime"].stringValue,
  1148. UTCToLocal(@"2016-02-28 18:33:31"));
  1149. XCTAssertEqual(_configInstances[i][@"isPaidUser"].boolValue, YES);
  1150. XCTAssertEqualObjects(_configInstances[i][@"dataValue"].stringValue, @"2.4");
  1151. XCTAssertEqualObjects(_configInstances[i][@"New item"].numberValue, @(2.4));
  1152. XCTAssertEqualObjects(_configInstances[i][@"Languages"].stringValue, @"English");
  1153. XCTAssertEqualObjects(_configInstances[i][@"FileInfo"].stringValue,
  1154. @"To setup default config.");
  1155. XCTAssertEqualObjects(_configInstances[i][@"format"].stringValue, @"key to value.");
  1156. // If given a wrong file name, the default will not be set and kept as previous results.
  1157. [_configInstances[i] setDefaultsFromPlistFileName:@""];
  1158. XCTAssertEqualObjects(_configInstances[i][@"lastCheckTime"].stringValue,
  1159. UTCToLocal(@"2016-02-28 18:33:31"));
  1160. [_configInstances[i] setDefaultsFromPlistFileName:@"non-existed_file"];
  1161. XCTAssertEqualObjects(_configInstances[i][@"dataValue"].stringValue, @"2.4");
  1162. [_configInstances[i] setDefaultsFromPlistFileName:nil];
  1163. XCTAssertEqualObjects(_configInstances[i][@"New item"].numberValue, @(2.4));
  1164. [_configInstances[i] setDefaultsFromPlistFileName:@""];
  1165. XCTAssertEqualObjects(_configInstances[i][@"Languages"].stringValue, @"English");
  1166. }
  1167. }
  1168. - (void)testAllKeysFromSource {
  1169. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  1170. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  1171. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1172. fetchConfigsExpectation[i] = [self
  1173. expectationWithDescription:[NSString
  1174. stringWithFormat:@"Test allKeys methods - instance %d", i]];
  1175. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  1176. NSString *key0 = [NSString stringWithFormat:@"key0-%d", i];
  1177. NSDictionary<NSString *, NSString *> *defaults = @{key1 : @"default key1", key0 : @"value0-0"};
  1178. [_configInstances[i] setDefaults:defaults];
  1179. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  1180. NSError *error) {
  1181. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess);
  1182. XCTAssertNil(error);
  1183. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  1184. XCTAssertTrue(changed);
  1185. XCTAssertEqual(
  1186. [self->_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceRemote].count, 100);
  1187. XCTAssertEqual(
  1188. [self->_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceDefault].count, 2);
  1189. XCTAssertEqual(
  1190. [self->_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceStatic].count, 0);
  1191. [fetchConfigsExpectation[i] fulfill];
  1192. }];
  1193. };
  1194. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  1195. }
  1196. [self waitForExpectationsWithTimeout:_expectationTimeout
  1197. handler:^(NSError *error) {
  1198. XCTAssertNil(error);
  1199. }];
  1200. }
  1201. - (void)testAllKeysWithPrefix {
  1202. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  1203. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  1204. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1205. fetchConfigsExpectation[i] = [self
  1206. expectationWithDescription:[NSString
  1207. stringWithFormat:@"Test allKeys methods - instance %d", i]];
  1208. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  1209. NSError *error) {
  1210. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess);
  1211. XCTAssertNil(error);
  1212. NSLog(@"Testing _configInstances %d", i);
  1213. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  1214. XCTAssertTrue(changed);
  1215. // Test keysWithPrefix: method.
  1216. XCTAssertEqual([self->_configInstances[i] keysWithPrefix:@"key1"].count, 12);
  1217. XCTAssertEqual([self->_configInstances[i] keysWithPrefix:@"key"].count, 100);
  1218. XCTAssertEqual([self->_configInstances[i] keysWithPrefix:@"invalid key"].count, 0);
  1219. XCTAssertEqual([self->_configInstances[i] keysWithPrefix:nil].count, 100);
  1220. XCTAssertEqual([self->_configInstances[i] keysWithPrefix:@""].count, 100);
  1221. [fetchConfigsExpectation[i] fulfill];
  1222. }];
  1223. };
  1224. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  1225. }
  1226. [self waitForExpectationsWithTimeout:_expectationTimeout
  1227. handler:^(NSError *error) {
  1228. XCTAssertNil(error);
  1229. }];
  1230. }
  1231. /// Test the minimum fetch interval is applied and read back correctly.
  1232. - (void)testSetMinimumFetchIntervalConfigSetting {
  1233. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  1234. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  1235. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1236. fetchConfigsExpectation[i] = [self
  1237. expectationWithDescription:
  1238. [NSString stringWithFormat:@"Test minimumFetchInterval expectation - instance %d", i]];
  1239. FIRRemoteConfigSettings *settings = [[FIRRemoteConfigSettings alloc] init];
  1240. settings.minimumFetchInterval = 123;
  1241. [_configInstances[i] setConfigSettings:settings];
  1242. XCTAssertEqual([_configInstances[i] configSettings].minimumFetchInterval, 123);
  1243. FIRRemoteConfigFetchCompletion fetchCompletion =
  1244. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  1245. XCTAssertFalse([self->_configInstances[i].settings hasMinimumFetchIntervalElapsed:43200]);
  1246. // Update minimum fetch interval.
  1247. FIRRemoteConfigSettings *settings = [[FIRRemoteConfigSettings alloc] init];
  1248. settings.minimumFetchInterval = 0;
  1249. [self->_configInstances[i] setConfigSettings:settings];
  1250. XCTAssertEqual([self->_configInstances[i] configSettings].minimumFetchInterval, 0);
  1251. XCTAssertTrue([self->_configInstances[i].settings hasMinimumFetchIntervalElapsed:0]);
  1252. [fetchConfigsExpectation[i] fulfill];
  1253. };
  1254. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  1255. }
  1256. [self waitForExpectationsWithTimeout:_expectationTimeout
  1257. handler:^(NSError *error) {
  1258. XCTAssertNil(error);
  1259. }];
  1260. }
  1261. /// Test the fetch timeout is properly set and read back.
  1262. - (void)testSetFetchTimeoutConfigSetting {
  1263. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1264. FIRRemoteConfigSettings *settings = [[FIRRemoteConfigSettings alloc] init];
  1265. settings.fetchTimeout = 1;
  1266. [_configInstances[i] setConfigSettings:settings];
  1267. XCTAssertEqual([_configInstances[i] configSettings].fetchTimeout, 1);
  1268. NSURLSession *networkSession = [_configFetch[i] currentNetworkSession];
  1269. XCTAssertNotNil(networkSession);
  1270. XCTAssertEqual(networkSession.configuration.timeoutIntervalForResource, 1);
  1271. XCTAssertEqual(networkSession.configuration.timeoutIntervalForRequest, 1);
  1272. }
  1273. }
  1274. #pragma mark - Public Factory Methods
  1275. - (void)testConfigureConfigWithValidInput {
  1276. // Configure the default app with our options and ensure the Remote Config instance is set up
  1277. // properly.
  1278. if (![FIRApp isDefaultAppConfigured]) {
  1279. XCTAssertNoThrow([FIRApp configureWithOptions:[self firstAppOptions]]);
  1280. }
  1281. XCTAssertNoThrow([FIRRemoteConfig remoteConfig]);
  1282. FIRRemoteConfig *config = [FIRRemoteConfig remoteConfig];
  1283. XCTAssertNotNil(config);
  1284. // Ensure the same instance is returned from the singleton.
  1285. FIRRemoteConfig *sameConfig = [FIRRemoteConfig remoteConfig];
  1286. XCTAssertNotNil(sameConfig);
  1287. XCTAssertEqual(config, sameConfig);
  1288. // Ensure the app name is stored properly.
  1289. XCTAssertEqual([config valueForKey:@"_appName"], kFIRDefaultAppName);
  1290. }
  1291. #pragma mark - Test Helpers
  1292. - (FIROptions *)firstAppOptions {
  1293. // TODO: Evaluate if we want to hardcode things here instead.
  1294. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"1:123:ios:123abc"
  1295. GCMSenderID:@"correct_gcm_sender_id"];
  1296. options.APIKey = @"AIzaSy-ApiKeyWithValidFormat_0123456789";
  1297. options.projectID = @"abc-xyz-123";
  1298. return options;
  1299. }
  1300. - (FIROptions *)secondAppOptions {
  1301. NSBundle *bundle = [NSBundle bundleForClass:[self class]];
  1302. #if SWIFT_PACKAGE
  1303. bundle = Firebase_RemoteConfigUnit_SWIFTPM_MODULE_BUNDLE();
  1304. #endif
  1305. NSString *plistPath = [bundle pathForResource:@"SecondApp-GoogleService-Info" ofType:@"plist"];
  1306. FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:plistPath];
  1307. XCTAssertNotNil(options);
  1308. return options;
  1309. }
  1310. @end