RCNRemoteConfigTest.m 73 KB

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