RCNRemoteConfigTest.m 59 KB

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