RCNRemoteConfigTest.m 65 KB

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