RCNRemoteConfigTest.m 73 KB

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