RCNRemoteConfigTest.m 68 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384
  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. // Activate should return false if a fetch response returns 200 with NO_CHANGE as the response body.
  661. - (void)testActivateOnFetchNoChangeStatus {
  662. // Override the setup values to return back an error status.
  663. RCNConfigContent *configContent = [[RCNConfigContent alloc] initWithDBManager:_DBManager];
  664. // Populate the default, second app, second namespace instances.
  665. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  666. NSString *currentAppName = nil;
  667. FIROptions *currentOptions = nil;
  668. NSString *currentNamespace = nil;
  669. switch (i) {
  670. case RCNTestRCInstanceSecondNamespace:
  671. currentAppName = RCNTestsDefaultFIRAppName;
  672. currentOptions = [self firstAppOptions];
  673. currentNamespace = RCNTestsPerfNamespace;
  674. break;
  675. case RCNTestRCInstanceSecondApp:
  676. currentAppName = RCNTestsSecondFIRAppName;
  677. currentOptions = [self secondAppOptions];
  678. currentNamespace = FIRNamespaceGoogleMobilePlatform;
  679. break;
  680. case RCNTestRCInstanceDefault:
  681. default:
  682. currentAppName = RCNTestsDefaultFIRAppName;
  683. currentOptions = [self firstAppOptions];
  684. currentNamespace = RCNTestsFIRNamespace;
  685. break;
  686. }
  687. NSString *fullyQualifiedNamespace =
  688. [NSString stringWithFormat:@"%@:%@", currentNamespace, currentAppName];
  689. RCNUserDefaultsManager *userDefaultsManager =
  690. [[RCNUserDefaultsManager alloc] initWithAppName:currentAppName
  691. bundleID:[NSBundle mainBundle].bundleIdentifier
  692. namespace:fullyQualifiedNamespace];
  693. userDefaultsManager.lastFetchTime = 10;
  694. FIRRemoteConfig *config =
  695. OCMPartialMock([[FIRRemoteConfig alloc] initWithAppName:currentAppName
  696. FIROptions:currentOptions
  697. namespace:currentNamespace
  698. DBManager:_DBManager
  699. configContent:configContent
  700. analytics:nil]);
  701. _configInstances[i] = config;
  702. RCNConfigSettings *settings =
  703. [[RCNConfigSettings alloc] initWithDatabaseManager:_DBManager
  704. namespace:fullyQualifiedNamespace
  705. firebaseAppName:currentAppName
  706. googleAppID:currentOptions.googleAppID];
  707. // Start the test with the assumption that we have some data that was fetched and activated.
  708. settings.lastETag = @"etag1";
  709. settings.lastETagUpdateTime = 100;
  710. settings.lastApplyTimeInterval = 101;
  711. dispatch_queue_t queue =
  712. dispatch_queue_create([[NSString stringWithFormat:@"testNoStatusFetchQueue: %d", i]
  713. cStringUsingEncoding:NSUTF8StringEncoding],
  714. DISPATCH_QUEUE_SERIAL);
  715. _configFetch[i] = OCMPartialMock([[RCNConfigFetch alloc] initWithContent:configContent
  716. DBManager:_DBManager
  717. settings:settings
  718. analytics:nil
  719. experiment:nil
  720. queue:queue
  721. namespace:fullyQualifiedNamespace
  722. options:currentOptions]);
  723. OCMStub([_configFetch[i] fetchConfigWithExpirationDuration:43200 completionHandler:OCMOCK_ANY])
  724. .andDo(^(NSInvocation *invocation) {
  725. __unsafe_unretained void (^handler)(FIRRemoteConfigFetchStatus status,
  726. NSError *_Nullable error) = nil;
  727. [invocation getArgument:&handler atIndex:3];
  728. [self->_configFetch[i] fetchWithUserProperties:[[NSDictionary alloc] init]
  729. completionHandler:handler];
  730. });
  731. _response[i] = @{@"state" : @"NO_CHANGE"};
  732. _responseData[i] = [NSJSONSerialization dataWithJSONObject:_response[i] options:0 error:nil];
  733. _URLResponse[i] =
  734. [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"https://firebase.com"]
  735. statusCode:200
  736. HTTPVersion:nil
  737. headerFields:@{@"etag" : @"etag1"}];
  738. id completionBlock =
  739. [OCMArg invokeBlockWithArgs:_responseData[i], _URLResponse[i], [NSNull null], nil];
  740. OCMStub([_configFetch[i] URLSessionDataTaskWithContent:[OCMArg any]
  741. completionHandler:completionBlock])
  742. .andReturn(nil);
  743. [_configInstances[i] updateWithNewInstancesForConfigFetch:_configFetch[i]
  744. configContent:configContent
  745. configSettings:settings
  746. configExperiment:nil];
  747. }
  748. // Make the fetch calls for all instances.
  749. NSMutableArray<XCTestExpectation *> *expectations =
  750. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  751. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  752. expectations[i] = [self
  753. expectationWithDescription:
  754. [NSString stringWithFormat:@"Test enumerating configs successfully - instance %d", i]];
  755. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  756. // Make sure activate returns false in fetch completion.
  757. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  758. NSError *error) {
  759. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  760. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  761. XCTAssertFalse(changed);
  762. XCTAssertNil(error);
  763. [expectations[i] fulfill];
  764. }];
  765. };
  766. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  767. }
  768. [self waitForExpectationsWithTimeout:_expectationTimeout
  769. handler:^(NSError *error) {
  770. XCTAssertNil(error);
  771. }];
  772. }
  773. - (void)testConfigValueForKey {
  774. NSMutableArray<XCTestExpectation *> *expectations =
  775. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  776. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  777. expectations[i] =
  778. [self expectationWithDescription:
  779. [NSString stringWithFormat:@"Test configValueForKey: method - instance %d", i]];
  780. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  781. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  782. NSError *error) {
  783. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess);
  784. XCTAssertNil(error);
  785. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  786. XCTAssertTrue(changed);
  787. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  788. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  789. NSString *key3 = [NSString stringWithFormat:@"key3-%d", i];
  790. NSString *key7 = [NSString stringWithFormat:@"key7-%d", i];
  791. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  792. NSString *value2 = [NSString stringWithFormat:@"value2-%d", i];
  793. NSString *value3 = [NSString stringWithFormat:@"value3-%d", i];
  794. NSString *value7 = [NSString stringWithFormat:@"value7-%d", i];
  795. XCTAssertEqualObjects(self->_configInstances[i][key1].stringValue, value1);
  796. XCTAssertEqualObjects(self->_configInstances[i][key2].stringValue, value2);
  797. OCMVerify([self->_configInstances[i] objectForKeyedSubscript:key1]);
  798. XCTAssertEqualObjects([self->_configInstances[i] configValueForKey:key3].stringValue,
  799. value3);
  800. if (i == RCNTestRCInstanceDefault) {
  801. XCTAssertEqualObjects([self->_configInstances[i] configValueForKey:key7].stringValue,
  802. value7);
  803. }
  804. XCTAssertEqualObjects([self->_configInstances[i] configValueForKey:key7].stringValue,
  805. value7);
  806. XCTAssertNotNil([self->_configInstances[i] configValueForKey:nil]);
  807. XCTAssertEqual([self->_configInstances[i] configValueForKey:nil].source,
  808. FIRRemoteConfigSourceStatic);
  809. XCTAssertEqual([self->_configInstances[i] configValueForKey:nil].source,
  810. FIRRemoteConfigSourceStatic);
  811. XCTAssertEqual([self->_configInstances[i] configValueForKey:nil source:-1].source,
  812. FIRRemoteConfigSourceStatic);
  813. [expectations[i] fulfill];
  814. }];
  815. };
  816. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  817. }
  818. [self waitForExpectationsWithTimeout:_expectationTimeout
  819. handler:^(NSError *error) {
  820. XCTAssertNil(error);
  821. }];
  822. }
  823. - (void)testFetchConfigWithDefaultSets {
  824. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  825. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  826. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  827. fetchConfigsExpectation[i] = [self
  828. expectationWithDescription:
  829. [NSString stringWithFormat:@"Test fetch configs with defaults set - instance %d", i]];
  830. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  831. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  832. NSString *key0 = [NSString stringWithFormat:@"key0-%d", i];
  833. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  834. NSString *value2 = [NSString stringWithFormat:@"value2-%d", i];
  835. NSDictionary<NSString *, NSString *> *defaults = @{key1 : @"default key1", key0 : @"value0-0"};
  836. [_configInstances[i] setDefaults:defaults];
  837. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  838. NSError *error) {
  839. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  840. XCTAssertNil(error);
  841. XCTAssertEqualObjects(self->_configInstances[i][key1].stringValue, @"default key1");
  842. XCTAssertEqual(self->_configInstances[i][key1].source, FIRRemoteConfigSourceDefault);
  843. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  844. XCTAssertTrue(changed);
  845. XCTAssertEqualObjects(self->_configInstances[i][key1].stringValue, value1);
  846. XCTAssertEqual(self->_configInstances[i][key1].source, FIRRemoteConfigSourceRemote);
  847. XCTAssertEqualObjects([self->_configInstances[i] defaultValueForKey:key1].stringValue,
  848. @"default key1");
  849. XCTAssertEqualObjects(self->_configInstances[i][key2].stringValue, value2);
  850. XCTAssertEqualObjects(self->_configInstances[i][key0].stringValue, @"value0-0");
  851. XCTAssertNil([self->_configInstances[i] defaultValueForKey:nil]);
  852. OCMVerify([self->_configInstances[i] objectForKeyedSubscript:key1]);
  853. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  854. @"Callback of first successful config "
  855. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccess.");
  856. [fetchConfigsExpectation[i] fulfill];
  857. }];
  858. };
  859. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  860. }
  861. [self waitForExpectationsWithTimeout:_expectationTimeout
  862. handler:^(NSError *error) {
  863. XCTAssertNil(error);
  864. }];
  865. }
  866. - (void)testDefaultsSetsOnly {
  867. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  868. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  869. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  870. NSString *key3 = [NSString stringWithFormat:@"key3-%d", i];
  871. NSString *key4 = [NSString stringWithFormat:@"key4-%d", i];
  872. NSString *key5 = [NSString stringWithFormat:@"key5-%d", i];
  873. NSString *defaultValue1 = @"default value1";
  874. NSData *defaultValue2 = [defaultValue1 dataUsingEncoding:NSUTF8StringEncoding];
  875. NSNumber *defaultValue3 = [NSNumber numberWithFloat:3.1415926];
  876. NSDate *defaultValue4 = [NSDate date];
  877. BOOL defaultValue5 = NO;
  878. NSMutableDictionary<NSString *, id> *defaults = [NSMutableDictionary dictionaryWithDictionary:@{
  879. key1 : defaultValue1,
  880. key2 : defaultValue2,
  881. key3 : defaultValue3,
  882. key4 : defaultValue4,
  883. key5 : @(defaultValue5),
  884. }];
  885. [_configInstances[i] setDefaults:defaults];
  886. if (i == RCNTestRCInstanceSecondNamespace) {
  887. [defaults setObject:@"2860" forKey:@"experience"];
  888. [_configInstances[i] setDefaults:defaults];
  889. }
  890. // Remove objects right away to make sure dispatch_async gets the copy.
  891. [defaults removeAllObjects];
  892. FIRRemoteConfig *config = _configInstances[i];
  893. XCTAssertEqualObjects(config[key1].stringValue, defaultValue1, @"Should support string format");
  894. XCTAssertEqualObjects(config[key2].dataValue, defaultValue2, @"Should support data format");
  895. XCTAssertEqual(config[key3].numberValue.longValue, defaultValue3.longValue,
  896. @"Should support number format");
  897. NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  898. [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  899. NSString *strValueOfDate = [dateFormatter stringFromDate:(NSDate *)defaultValue4];
  900. XCTAssertEqualObjects(
  901. config[key4].stringValue, strValueOfDate,
  902. @"Date format can be set as an input from plist, but output coming out of "
  903. @"defaultConfig as string.");
  904. XCTAssertEqual(config[key5].boolValue, defaultValue5, @"Should support bool format");
  905. if (i == RCNTestRCInstanceSecondNamespace) {
  906. XCTAssertEqualObjects(
  907. [_configInstances[i] configValueForKey:@"experience"].stringValue, @"2860",
  908. @"Only default config has the key, must equal to default config value.");
  909. }
  910. // Reset default sets
  911. [_configInstances[i] setDefaults:nil];
  912. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceDefault].count, 0);
  913. }
  914. }
  915. - (void)testSetDefaultsWithNilParams {
  916. NSMutableArray<XCTestExpectation *> *expectations =
  917. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  918. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  919. expectations[i] = [self
  920. expectationWithDescription:
  921. [NSString stringWithFormat:@"Set defaults no callback expectation - instance %d", i]];
  922. // Should work when passing nil.
  923. [_configInstances[i] setDefaults:nil];
  924. [_configInstances[i] setDefaults:nil];
  925. dispatch_after(
  926. dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_checkCompletionTimeout * NSEC_PER_SEC)),
  927. dispatch_get_main_queue(), ^{
  928. XCTAssertEqual(
  929. [self->_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceDefault].count, 0);
  930. [expectations[i] fulfill];
  931. });
  932. }
  933. [self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
  934. }
  935. - (void)testFetchConfigOverwriteDefaultSet {
  936. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  937. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  938. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  939. fetchConfigsExpectation[i] = [self
  940. expectationWithDescription:
  941. [NSString stringWithFormat:@"Test fetch configs with defaults set - instance %d", i]];
  942. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  943. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  944. [_configInstances[i] setDefaults:@{key1 : @"default key1"}];
  945. FIRRemoteConfigValue *value = _configInstances[i][key1];
  946. XCTAssertEqualObjects(value.stringValue, @"default key1");
  947. XCTAssertEqual(value.source, FIRRemoteConfigSourceDefault);
  948. value = _configInstances[i][@"A key doesn't exist"];
  949. XCTAssertEqual(value.source, FIRRemoteConfigSourceStatic);
  950. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  951. NSError *error) {
  952. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  953. XCTAssertNil(error);
  954. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  955. XCTAssertTrue(changed);
  956. XCTAssertEqualObjects(self->_configInstances[i][key1].stringValue, value1);
  957. XCTAssertEqual(self->_configInstances[i][key1].source, FIRRemoteConfigSourceRemote);
  958. XCTAssertEqualObjects([self->_configInstances[i] defaultValueForKey:key1].stringValue,
  959. @"default key1");
  960. OCMVerify([self->_configInstances[i] objectForKeyedSubscript:key1]);
  961. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  962. @"Callback of first successful config "
  963. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccess.");
  964. [fetchConfigsExpectation[i] fulfill];
  965. }];
  966. };
  967. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  968. }
  969. [self waitForExpectationsWithTimeout:_expectationTimeout
  970. handler:^(NSError *error) {
  971. XCTAssertNil(error);
  972. }];
  973. }
  974. - (void)testGetConfigValueBySource {
  975. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  976. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  977. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  978. fetchConfigsExpectation[i] =
  979. [self expectationWithDescription:
  980. [NSString stringWithFormat:@"Test get config value by source - instance %d", i]];
  981. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  982. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  983. NSDictionary<NSString *, NSString *> *defaults = @{key1 : @"default value1"};
  984. [_configInstances[i] setDefaults:defaults];
  985. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  986. NSError *error) {
  987. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  988. XCTAssertNil(error);
  989. XCTAssertEqualObjects(self->_configInstances[i][key1].stringValue, @"default value1");
  990. XCTAssertEqual(self->_configInstances[i][key1].source, FIRRemoteConfigSourceDefault);
  991. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  992. XCTAssertTrue(changed);
  993. XCTAssertEqualObjects(self->_configInstances[i][key1].stringValue, value1);
  994. XCTAssertEqual(self->_configInstances[i][key1].source, FIRRemoteConfigSourceRemote);
  995. FIRRemoteConfigValue *value;
  996. if (i == RCNTestRCInstanceDefault) {
  997. value = [self->_configInstances[i] configValueForKey:key1
  998. source:FIRRemoteConfigSourceRemote];
  999. XCTAssertEqualObjects(value.stringValue, value1);
  1000. value = [self->_configInstances[i] configValueForKey:key1
  1001. source:FIRRemoteConfigSourceDefault];
  1002. XCTAssertEqualObjects(value.stringValue, @"default value1");
  1003. value = [self->_configInstances[i] configValueForKey:key1
  1004. source:FIRRemoteConfigSourceStatic];
  1005. } else {
  1006. value = [self->_configInstances[i] configValueForKey:key1
  1007. source:FIRRemoteConfigSourceRemote];
  1008. XCTAssertEqualObjects(value.stringValue, value1);
  1009. value = [self->_configInstances[i] configValueForKey:key1
  1010. source:FIRRemoteConfigSourceDefault];
  1011. XCTAssertEqualObjects(value.stringValue, @"default value1");
  1012. value = [self->_configInstances[i] configValueForKey:key1
  1013. source:FIRRemoteConfigSourceStatic];
  1014. }
  1015. XCTAssertEqualObjects(value.stringValue, @"");
  1016. XCTAssertEqualObjects(value.numberValue, @(0));
  1017. XCTAssertEqual(value.boolValue, NO);
  1018. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  1019. @"Callback of first successful config "
  1020. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccess.");
  1021. [fetchConfigsExpectation[i] fulfill];
  1022. }];
  1023. };
  1024. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  1025. }
  1026. [self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
  1027. }
  1028. - (void)testInvalidKeyOrNamespace {
  1029. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1030. FIRRemoteConfigValue *value = [_configInstances[i] configValueForKey:nil];
  1031. XCTAssertNotNil(value);
  1032. XCTAssertEqual(value.source, FIRRemoteConfigSourceStatic);
  1033. value = [_configInstances[i] configValueForKey:nil];
  1034. XCTAssertNotNil(value);
  1035. XCTAssertEqual(value.source, FIRRemoteConfigSourceStatic);
  1036. value = [_configInstances[i] configValueForKey:nil source:5];
  1037. XCTAssertNotNil(value);
  1038. XCTAssertEqual(value.source, FIRRemoteConfigSourceStatic);
  1039. }
  1040. }
  1041. // Remote Config converts UTC times in the plists to local times. This utility function makes it
  1042. // possible to check the times when running the tests in any timezone.
  1043. static NSString *UTCToLocal(NSString *utcTime) {
  1044. // Create a UTC dateFormatter.
  1045. NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  1046. [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  1047. [dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
  1048. NSDate *date = [dateFormatter dateFromString:utcTime];
  1049. [dateFormatter setTimeZone:[NSTimeZone localTimeZone]];
  1050. return [dateFormatter stringFromDate:date];
  1051. }
  1052. // Manage different bundle locations for Swift Package Manager, CocoaPods static, CocoaPods dynamic.
  1053. - (void)setDefaultsFor:(FIRRemoteConfig *)config {
  1054. #if SWIFT_PACKAGE
  1055. NSBundle *bundle = Firebase_RemoteConfigUnit_SWIFTPM_MODULE_BUNDLE();
  1056. NSString *plistFile = [bundle pathForResource:@"Defaults-testInfo" ofType:@"plist"];
  1057. #else
  1058. NSBundle *bundle = [NSBundle mainBundle];
  1059. NSString *plistFile = [bundle pathForResource:@"Defaults-testInfo" ofType:@"plist"];
  1060. if (plistFile != nil) {
  1061. [config setDefaultsFromPlistFileName:@"Defaults-testInfo"];
  1062. return;
  1063. }
  1064. // We've linked dynamically and the plist file is in the test's bundle.
  1065. for (bundle in [NSBundle allBundles]) {
  1066. plistFile = [bundle pathForResource:@"Defaults-testInfo" ofType:@"plist"];
  1067. if (plistFile != nil) {
  1068. break;
  1069. }
  1070. }
  1071. #endif
  1072. NSDictionary *defaults = [[NSDictionary alloc] initWithContentsOfFile:plistFile];
  1073. [config setDefaults:defaults];
  1074. }
  1075. - (void)testSetDefaultsFromPlist {
  1076. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1077. FIRRemoteConfig *config = _configInstances[i];
  1078. [self setDefaultsFor:config];
  1079. XCTAssertEqualObjects(_configInstances[i][@"lastCheckTime"].stringValue,
  1080. UTCToLocal(@"2016-02-28 18:33:31"));
  1081. XCTAssertEqual(_configInstances[i][@"isPaidUser"].boolValue, YES);
  1082. XCTAssertEqualObjects(_configInstances[i][@"dataValue"].stringValue, @"2.4");
  1083. XCTAssertEqualObjects(_configInstances[i][@"New item"].numberValue, @(2.4));
  1084. XCTAssertEqualObjects(_configInstances[i][@"Languages"].stringValue, @"English");
  1085. XCTAssertEqualObjects(_configInstances[i][@"FileInfo"].stringValue,
  1086. @"To setup default config.");
  1087. XCTAssertEqualObjects(_configInstances[i][@"format"].stringValue, @"key to value.");
  1088. // If given a wrong file name, the default will not be set and kept as previous results.
  1089. [_configInstances[i] setDefaultsFromPlistFileName:@""];
  1090. XCTAssertEqualObjects(_configInstances[i][@"lastCheckTime"].stringValue,
  1091. UTCToLocal(@"2016-02-28 18:33:31"));
  1092. [_configInstances[i] setDefaultsFromPlistFileName:@"non-existed_file"];
  1093. XCTAssertEqualObjects(_configInstances[i][@"dataValue"].stringValue, @"2.4");
  1094. [_configInstances[i] setDefaultsFromPlistFileName:nil];
  1095. XCTAssertEqualObjects(_configInstances[i][@"New item"].numberValue, @(2.4));
  1096. [_configInstances[i] setDefaultsFromPlistFileName:@""];
  1097. XCTAssertEqualObjects(_configInstances[i][@"Languages"].stringValue, @"English");
  1098. }
  1099. }
  1100. - (void)testAllKeysFromSource {
  1101. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  1102. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  1103. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1104. fetchConfigsExpectation[i] = [self
  1105. expectationWithDescription:[NSString
  1106. stringWithFormat:@"Test allKeys methods - instance %d", i]];
  1107. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  1108. NSString *key0 = [NSString stringWithFormat:@"key0-%d", i];
  1109. NSDictionary<NSString *, NSString *> *defaults = @{key1 : @"default key1", key0 : @"value0-0"};
  1110. [_configInstances[i] setDefaults:defaults];
  1111. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  1112. NSError *error) {
  1113. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess);
  1114. XCTAssertNil(error);
  1115. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  1116. XCTAssertTrue(changed);
  1117. XCTAssertEqual(
  1118. [self->_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceRemote].count, 100);
  1119. XCTAssertEqual(
  1120. [self->_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceDefault].count, 2);
  1121. XCTAssertEqual(
  1122. [self->_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceStatic].count, 0);
  1123. [fetchConfigsExpectation[i] fulfill];
  1124. }];
  1125. };
  1126. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  1127. }
  1128. [self waitForExpectationsWithTimeout:_expectationTimeout
  1129. handler:^(NSError *error) {
  1130. XCTAssertNil(error);
  1131. }];
  1132. }
  1133. - (void)testAllKeysWithPrefix {
  1134. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  1135. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  1136. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1137. fetchConfigsExpectation[i] = [self
  1138. expectationWithDescription:[NSString
  1139. stringWithFormat:@"Test allKeys methods - instance %d", i]];
  1140. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  1141. NSError *error) {
  1142. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess);
  1143. XCTAssertNil(error);
  1144. NSLog(@"Testing _configInstances %d", i);
  1145. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  1146. XCTAssertTrue(changed);
  1147. // Test keysWithPrefix: method.
  1148. XCTAssertEqual([self->_configInstances[i] keysWithPrefix:@"key1"].count, 12);
  1149. XCTAssertEqual([self->_configInstances[i] keysWithPrefix:@"key"].count, 100);
  1150. XCTAssertEqual([self->_configInstances[i] keysWithPrefix:@"invalid key"].count, 0);
  1151. XCTAssertEqual([self->_configInstances[i] keysWithPrefix:nil].count, 100);
  1152. XCTAssertEqual([self->_configInstances[i] keysWithPrefix:@""].count, 100);
  1153. [fetchConfigsExpectation[i] fulfill];
  1154. }];
  1155. };
  1156. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  1157. }
  1158. [self waitForExpectationsWithTimeout:_expectationTimeout
  1159. handler:^(NSError *error) {
  1160. XCTAssertNil(error);
  1161. }];
  1162. }
  1163. /// Test the minimum fetch interval is applied and read back correctly.
  1164. - (void)testSetMinimumFetchIntervalConfigSetting {
  1165. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  1166. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  1167. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1168. fetchConfigsExpectation[i] = [self
  1169. expectationWithDescription:
  1170. [NSString stringWithFormat:@"Test minimumFetchInterval expectation - instance %d", i]];
  1171. FIRRemoteConfigSettings *settings = [[FIRRemoteConfigSettings alloc] init];
  1172. settings.minimumFetchInterval = 123;
  1173. [_configInstances[i] setConfigSettings:settings];
  1174. XCTAssertEqual([_configInstances[i] configSettings].minimumFetchInterval, 123);
  1175. FIRRemoteConfigFetchCompletion fetchCompletion =
  1176. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  1177. XCTAssertFalse([self->_configInstances[i].settings hasMinimumFetchIntervalElapsed:43200]);
  1178. // Update minimum fetch interval.
  1179. FIRRemoteConfigSettings *settings = [[FIRRemoteConfigSettings alloc] init];
  1180. settings.minimumFetchInterval = 0;
  1181. [self->_configInstances[i] setConfigSettings:settings];
  1182. XCTAssertEqual([self->_configInstances[i] configSettings].minimumFetchInterval, 0);
  1183. XCTAssertTrue([self->_configInstances[i].settings hasMinimumFetchIntervalElapsed:0]);
  1184. [fetchConfigsExpectation[i] fulfill];
  1185. };
  1186. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  1187. }
  1188. [self waitForExpectationsWithTimeout:_expectationTimeout
  1189. handler:^(NSError *error) {
  1190. XCTAssertNil(error);
  1191. }];
  1192. }
  1193. /// Test the fetch timeout is properly set and read back.
  1194. - (void)testSetFetchTimeoutConfigSetting {
  1195. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1196. FIRRemoteConfigSettings *settings = [[FIRRemoteConfigSettings alloc] init];
  1197. settings.fetchTimeout = 1;
  1198. [_configInstances[i] setConfigSettings:settings];
  1199. XCTAssertEqual([_configInstances[i] configSettings].fetchTimeout, 1);
  1200. NSURLSession *networkSession = [_configFetch[i] currentNetworkSession];
  1201. XCTAssertNotNil(networkSession);
  1202. XCTAssertEqual(networkSession.configuration.timeoutIntervalForResource, 1);
  1203. XCTAssertEqual(networkSession.configuration.timeoutIntervalForRequest, 1);
  1204. }
  1205. }
  1206. #pragma mark - Public Factory Methods
  1207. - (void)testConfigureConfigWithValidInput {
  1208. // Configure the default app with our options and ensure the Remote Config instance is set up
  1209. // properly.
  1210. if (![FIRApp isDefaultAppConfigured]) {
  1211. XCTAssertNoThrow([FIRApp configureWithOptions:[self firstAppOptions]]);
  1212. }
  1213. XCTAssertNoThrow([FIRRemoteConfig remoteConfig]);
  1214. FIRRemoteConfig *config = [FIRRemoteConfig remoteConfig];
  1215. XCTAssertNotNil(config);
  1216. // Ensure the same instance is returned from the singleton.
  1217. FIRRemoteConfig *sameConfig = [FIRRemoteConfig remoteConfig];
  1218. XCTAssertNotNil(sameConfig);
  1219. XCTAssertEqual(config, sameConfig);
  1220. // Ensure the app name is stored properly.
  1221. XCTAssertEqual([config valueForKey:@"_appName"], kFIRDefaultAppName);
  1222. }
  1223. #pragma mark - Test Helpers
  1224. - (FIROptions *)firstAppOptions {
  1225. // TODO: Evaluate if we want to hardcode things here instead.
  1226. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"1:123:ios:123abc"
  1227. GCMSenderID:@"correct_gcm_sender_id"];
  1228. options.APIKey = @"AIzaSy-ApiKeyWithValidFormat_0123456789";
  1229. options.projectID = @"abc-xyz-123";
  1230. return options;
  1231. }
  1232. - (FIROptions *)secondAppOptions {
  1233. NSBundle *bundle = [NSBundle bundleForClass:[self class]];
  1234. #if SWIFT_PACKAGE
  1235. bundle = Firebase_RemoteConfigUnit_SWIFTPM_MODULE_BUNDLE();
  1236. #endif
  1237. NSString *plistPath = [bundle pathForResource:@"SecondApp-GoogleService-Info" ofType:@"plist"];
  1238. FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:plistPath];
  1239. XCTAssertNotNil(options);
  1240. return options;
  1241. }
  1242. @end