RCNRemoteConfigTest.m 65 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313
  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. // Activate should return false if a fetch response returns 200 with NO_CHANGE as the response body.
  502. - (void)testActivateOnFetchNoChangeStatus {
  503. // Override the setup values to return back an error status.
  504. RCNConfigContent *configContent = [[RCNConfigContent alloc] initWithDBManager:_DBManager];
  505. // Populate the default, second app, second namespace instances.
  506. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  507. NSString *currentAppName = nil;
  508. FIROptions *currentOptions = nil;
  509. NSString *currentNamespace = nil;
  510. switch (i) {
  511. case RCNTestRCInstanceSecondNamespace:
  512. currentAppName = RCNTestsDefaultFIRAppName;
  513. currentOptions = [self firstAppOptions];
  514. currentNamespace = RCNTestsPerfNamespace;
  515. break;
  516. case RCNTestRCInstanceSecondApp:
  517. currentAppName = RCNTestsSecondFIRAppName;
  518. currentOptions = [self secondAppOptions];
  519. currentNamespace = FIRNamespaceGoogleMobilePlatform;
  520. break;
  521. case RCNTestRCInstanceDefault:
  522. default:
  523. currentAppName = RCNTestsDefaultFIRAppName;
  524. currentOptions = [self firstAppOptions];
  525. currentNamespace = RCNTestsFIRNamespace;
  526. break;
  527. }
  528. NSString *fullyQualifiedNamespace =
  529. [NSString stringWithFormat:@"%@:%@", currentNamespace, currentAppName];
  530. RCNUserDefaultsManager *userDefaultsManager =
  531. [[RCNUserDefaultsManager alloc] initWithAppName:currentAppName
  532. bundleID:[NSBundle mainBundle].bundleIdentifier
  533. namespace:fullyQualifiedNamespace];
  534. userDefaultsManager.lastFetchTime = 10;
  535. FIRRemoteConfig *config =
  536. OCMPartialMock([[FIRRemoteConfig alloc] initWithAppName:currentAppName
  537. FIROptions:currentOptions
  538. namespace:currentNamespace
  539. DBManager:_DBManager
  540. configContent:configContent
  541. analytics:nil]);
  542. _configInstances[i] = config;
  543. RCNConfigSettings *settings =
  544. [[RCNConfigSettings alloc] initWithDatabaseManager:_DBManager
  545. namespace:fullyQualifiedNamespace
  546. firebaseAppName:currentAppName
  547. googleAppID:currentOptions.googleAppID];
  548. // Start the test with the assumption that we have some data that was fetched and activated.
  549. settings.lastETag = @"etag1";
  550. settings.lastETagUpdateTime = 100;
  551. settings.lastApplyTimeInterval = 101;
  552. dispatch_queue_t queue =
  553. dispatch_queue_create([[NSString stringWithFormat:@"testNoStatusFetchQueue: %d", i]
  554. cStringUsingEncoding:NSUTF8StringEncoding],
  555. DISPATCH_QUEUE_SERIAL);
  556. _configFetch[i] = OCMPartialMock([[RCNConfigFetch alloc] initWithContent:configContent
  557. DBManager:_DBManager
  558. settings:settings
  559. analytics:nil
  560. experiment:nil
  561. queue:queue
  562. namespace:fullyQualifiedNamespace
  563. options:currentOptions]);
  564. OCMStub([_configFetch[i] fetchAllConfigsWithExpirationDuration:43200
  565. completionHandler:OCMOCK_ANY])
  566. .andDo(^(NSInvocation *invocation) {
  567. void (^handler)(FIRRemoteConfigFetchStatus status, NSError *_Nullable error) = nil;
  568. [invocation getArgument:&handler atIndex:3];
  569. [_configFetch[i] fetchWithUserProperties:[[NSDictionary alloc] init]
  570. completionHandler:handler];
  571. });
  572. _response[i] = @{@"state" : @"NO_CHANGE"};
  573. _responseData[i] = [NSJSONSerialization dataWithJSONObject:_response[i] options:0 error:nil];
  574. _URLResponse[i] =
  575. [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"https://firebase.com"]
  576. statusCode:200
  577. HTTPVersion:nil
  578. headerFields:@{@"etag" : @"etag1"}];
  579. id completionBlock =
  580. [OCMArg invokeBlockWithArgs:_responseData[i], _URLResponse[i], [NSNull null], nil];
  581. OCMExpect([_configFetch[i] URLSessionDataTaskWithContent:[OCMArg any]
  582. completionHandler:completionBlock])
  583. .andReturn(nil);
  584. [_configInstances[i] updateWithNewInstancesForConfigFetch:_configFetch[i]
  585. configContent:configContent
  586. configSettings:settings
  587. configExperiment:nil];
  588. }
  589. #pragma clang diagnostic push
  590. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  591. // Make the fetch calls for all instances.
  592. NSMutableArray<XCTestExpectation *> *expectations =
  593. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  594. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  595. expectations[i] = [self
  596. expectationWithDescription:
  597. [NSString stringWithFormat:@"Test enumerating configs successfully - instance %d", i]];
  598. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  599. // Make sure activate returns false in fetch completion.
  600. FIRRemoteConfigFetchCompletion fetchCompletion =
  601. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  602. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  603. XCTAssertFalse([_configInstances[i] activateFetched]);
  604. XCTAssertNil(error);
  605. [expectations[i] fulfill];
  606. };
  607. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  608. }
  609. [self waitForExpectationsWithTimeout:_expectationTimeout
  610. handler:^(NSError *error) {
  611. XCTAssertNil(error);
  612. }];
  613. }
  614. - (void)testConfigValueForKey {
  615. NSMutableArray<XCTestExpectation *> *expectations =
  616. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  617. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  618. expectations[i] =
  619. [self expectationWithDescription:
  620. [NSString stringWithFormat:@"Test configValueForKey: method - instance %d", i]];
  621. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  622. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  623. NSError *error) {
  624. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess);
  625. XCTAssertNil(error);
  626. XCTAssertTrue([_configInstances[i] activateFetched]);
  627. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  628. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  629. NSString *key3 = [NSString stringWithFormat:@"key3-%d", i];
  630. NSString *key7 = [NSString stringWithFormat:@"key7-%d", i];
  631. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  632. NSString *value2 = [NSString stringWithFormat:@"value2-%d", i];
  633. NSString *value3 = [NSString stringWithFormat:@"value3-%d", i];
  634. NSString *value7 = [NSString stringWithFormat:@"value7-%d", i];
  635. XCTAssertEqualObjects(_configInstances[i][key1].stringValue, value1);
  636. XCTAssertEqualObjects(_configInstances[i][key2].stringValue, value2);
  637. OCMVerify([_configInstances[i] objectForKeyedSubscript:key1]);
  638. XCTAssertEqualObjects([_configInstances[i] configValueForKey:key3].stringValue, value3);
  639. if (i == RCNTestRCInstanceDefault) {
  640. XCTAssertEqualObjects(
  641. [_configInstances[i] configValueForKey:key7 namespace:FIRNamespaceGoogleMobilePlatform]
  642. .stringValue,
  643. value7);
  644. }
  645. XCTAssertEqualObjects([_configInstances[i] configValueForKey:key7].stringValue, value7);
  646. XCTAssertNotNil([_configInstances[i] configValueForKey:nil]);
  647. XCTAssertEqual([_configInstances[i] configValueForKey:nil].source,
  648. FIRRemoteConfigSourceStatic);
  649. XCTAssertEqual([_configInstances[i] configValueForKey:nil namespace:nil].source,
  650. FIRRemoteConfigSourceStatic);
  651. XCTAssertEqual([_configInstances[i] configValueForKey:nil namespace:nil source:-1].source,
  652. FIRRemoteConfigSourceStatic);
  653. [expectations[i] fulfill];
  654. };
  655. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  656. }
  657. [self waitForExpectationsWithTimeout:_expectationTimeout
  658. handler:^(NSError *error) {
  659. XCTAssertNil(error);
  660. }];
  661. }
  662. - (void)testFetchConfigWithDefaultSets {
  663. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  664. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  665. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  666. fetchConfigsExpectation[i] = [self
  667. expectationWithDescription:
  668. [NSString stringWithFormat:@"Test fetch configs with defaults set - instance %d", i]];
  669. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  670. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  671. NSString *key0 = [NSString stringWithFormat:@"key0-%d", i];
  672. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  673. NSString *value2 = [NSString stringWithFormat:@"value2-%d", i];
  674. NSDictionary<NSString *, NSString *> *defaults = @{key1 : @"default key1", key0 : @"value0-0"};
  675. [_configInstances[i] setDefaults:defaults];
  676. FIRRemoteConfigFetchCompletion fetchCompletion =
  677. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  678. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  679. XCTAssertNil(error);
  680. XCTAssertEqualObjects(_configInstances[i][key1].stringValue, @"default key1");
  681. XCTAssertEqual(_configInstances[i][key1].source, FIRRemoteConfigSourceDefault);
  682. #pragma clang diagnostic push
  683. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  684. XCTAssertTrue([_configInstances[i] activateFetched]);
  685. #pragma clang diagnostic pop
  686. XCTAssertEqualObjects(_configInstances[i][key1].stringValue, value1);
  687. XCTAssertEqual(_configInstances[i][key1].source, FIRRemoteConfigSourceRemote);
  688. XCTAssertEqualObjects([_configInstances[i] defaultValueForKey:key1].stringValue,
  689. @"default key1");
  690. XCTAssertEqualObjects(_configInstances[i][key2].stringValue, value2);
  691. XCTAssertEqualObjects(_configInstances[i][key0].stringValue, @"value0-0");
  692. #pragma clang diagnostic push
  693. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  694. XCTAssertNil([_configInstances[i] defaultValueForKey:nil namespace:nil]);
  695. #pragma clang diagnostic pop
  696. OCMVerify([_configInstances[i] objectForKeyedSubscript:key1]);
  697. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  698. @"Callback of first successful config "
  699. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccess.");
  700. [fetchConfigsExpectation[i] fulfill];
  701. };
  702. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  703. }
  704. [self waitForExpectationsWithTimeout:_expectationTimeout
  705. handler:^(NSError *error) {
  706. XCTAssertNil(error);
  707. }];
  708. }
  709. - (void)testDefaultsSetsOnly {
  710. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  711. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  712. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  713. NSString *key3 = [NSString stringWithFormat:@"key3-%d", i];
  714. NSString *key4 = [NSString stringWithFormat:@"key4-%d", i];
  715. NSString *key5 = [NSString stringWithFormat:@"key5-%d", i];
  716. NSString *defaultValue1 = @"default value1";
  717. NSData *defaultValue2 = [defaultValue1 dataUsingEncoding:NSUTF8StringEncoding];
  718. NSNumber *defaultValue3 = [NSNumber numberWithFloat:3.1415926];
  719. NSDate *defaultValue4 = [NSDate date];
  720. BOOL defaultValue5 = NO;
  721. NSMutableDictionary<NSString *, id> *defaults = [NSMutableDictionary dictionaryWithDictionary:@{
  722. key1 : defaultValue1,
  723. key2 : defaultValue2,
  724. key3 : defaultValue3,
  725. key4 : defaultValue4,
  726. key5 : @(defaultValue5),
  727. }];
  728. [_configInstances[i] setDefaults:defaults];
  729. if (i == RCNTestRCInstanceSecondNamespace) {
  730. [defaults setObject:@"2860" forKey:@"experience"];
  731. [_configInstances[i] setDefaults:defaults namespace:RCNTestsPerfNamespace];
  732. }
  733. // Remove objects right away to make sure dispatch_async gets the copy.
  734. [defaults removeAllObjects];
  735. FIRRemoteConfig *config = _configInstances[i];
  736. XCTAssertEqualObjects(config[key1].stringValue, defaultValue1, @"Should support string format");
  737. XCTAssertEqualObjects(config[key2].dataValue, defaultValue2, @"Should support data format");
  738. XCTAssertEqual(config[key3].numberValue.longValue, defaultValue3.longValue,
  739. @"Should support number format");
  740. NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  741. [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  742. NSString *strValueOfDate = [dateFormatter stringFromDate:(NSDate *)defaultValue4];
  743. XCTAssertEqualObjects(
  744. config[key4].stringValue, strValueOfDate,
  745. @"Date format can be set as an input from plist, but output coming out of "
  746. @"defaultConfig as string.");
  747. XCTAssertEqual(config[key5].boolValue, defaultValue5, @"Should support bool format");
  748. if (i == RCNTestRCInstanceSecondNamespace) {
  749. XCTAssertEqualObjects(
  750. [_configInstances[i] configValueForKey:@"experience" namespace:RCNTestsPerfNamespace]
  751. .stringValue,
  752. @"2860", @"Only default config has the key, must equal to default config value.");
  753. }
  754. // Reset default sets
  755. [_configInstances[i] setDefaults:nil];
  756. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceDefault].count, 0);
  757. }
  758. }
  759. - (void)testSetDefaultsWithNilParams {
  760. NSMutableArray<XCTestExpectation *> *expectations =
  761. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  762. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  763. expectations[i] = [self
  764. expectationWithDescription:
  765. [NSString stringWithFormat:@"Set defaults no callback expectation - instance %d", i]];
  766. // Should work when passing nil.
  767. [_configInstances[i] setDefaults:nil];
  768. [_configInstances[i] setDefaults:nil namespace:nil];
  769. dispatch_after(
  770. dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_checkCompletionTimeout * NSEC_PER_SEC)),
  771. dispatch_get_main_queue(), ^{
  772. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceDefault
  773. namespace:FIRNamespaceGoogleMobilePlatform]
  774. .count,
  775. 0);
  776. [expectations[i] fulfill];
  777. });
  778. }
  779. [self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
  780. }
  781. - (void)testFetchConfigOverwriteDefaultSet {
  782. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  783. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  784. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  785. fetchConfigsExpectation[i] = [self
  786. expectationWithDescription:
  787. [NSString stringWithFormat:@"Test fetch configs with defaults set - instance %d", i]];
  788. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  789. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  790. [_configInstances[i] setDefaults:@{key1 : @"default key1"}];
  791. FIRRemoteConfigValue *value = _configInstances[i][key1];
  792. XCTAssertEqualObjects(value.stringValue, @"default key1");
  793. XCTAssertEqual(value.source, FIRRemoteConfigSourceDefault);
  794. value = _configInstances[i][@"A key doesn't exist"];
  795. XCTAssertEqual(value.source, FIRRemoteConfigSourceStatic);
  796. FIRRemoteConfigFetchCompletion fetchCompletion =
  797. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  798. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  799. XCTAssertNil(error);
  800. XCTAssertTrue([_configInstances[i] activateFetched]);
  801. XCTAssertEqualObjects(_configInstances[i][key1].stringValue, value1);
  802. XCTAssertEqual(_configInstances[i][key1].source, FIRRemoteConfigSourceRemote);
  803. XCTAssertEqualObjects([_configInstances[i] defaultValueForKey:key1].stringValue,
  804. @"default key1");
  805. OCMVerify([_configInstances[i] objectForKeyedSubscript:key1]);
  806. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  807. @"Callback of first successful config "
  808. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccess.");
  809. [fetchConfigsExpectation[i] fulfill];
  810. };
  811. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  812. }
  813. [self waitForExpectationsWithTimeout:_expectationTimeout
  814. handler:^(NSError *error) {
  815. XCTAssertNil(error);
  816. }];
  817. }
  818. - (void)testGetConfigValueBySource {
  819. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  820. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  821. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  822. fetchConfigsExpectation[i] =
  823. [self expectationWithDescription:
  824. [NSString stringWithFormat:@"Test get config value by source - instance %d", i]];
  825. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  826. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  827. NSDictionary<NSString *, NSString *> *defaults = @{key1 : @"default value1"};
  828. [_configInstances[i] setDefaults:defaults];
  829. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  830. NSError *error) {
  831. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  832. XCTAssertNil(error);
  833. XCTAssertEqualObjects(_configInstances[i][key1].stringValue, @"default value1");
  834. XCTAssertEqual(_configInstances[i][key1].source, FIRRemoteConfigSourceDefault);
  835. XCTAssertTrue([_configInstances[i] activateFetched]);
  836. XCTAssertEqualObjects(_configInstances[i][key1].stringValue, value1);
  837. XCTAssertEqual(_configInstances[i][key1].source, FIRRemoteConfigSourceRemote);
  838. FIRRemoteConfigValue *value;
  839. if (i == RCNTestRCInstanceDefault) {
  840. value = [_configInstances[i] configValueForKey:key1
  841. namespace:FIRNamespaceGoogleMobilePlatform
  842. source:FIRRemoteConfigSourceRemote];
  843. XCTAssertEqualObjects(value.stringValue, value1);
  844. value = [_configInstances[i] configValueForKey:key1
  845. namespace:FIRNamespaceGoogleMobilePlatform
  846. source:FIRRemoteConfigSourceDefault];
  847. XCTAssertEqualObjects(value.stringValue, @"default value1");
  848. value = [_configInstances[i] configValueForKey:key1
  849. namespace:FIRNamespaceGoogleMobilePlatform
  850. source:FIRRemoteConfigSourceStatic];
  851. } else {
  852. value = [_configInstances[i] configValueForKey:key1 source:FIRRemoteConfigSourceRemote];
  853. XCTAssertEqualObjects(value.stringValue, value1);
  854. value = [_configInstances[i] configValueForKey:key1 source:FIRRemoteConfigSourceDefault];
  855. XCTAssertEqualObjects(value.stringValue, @"default value1");
  856. value = [_configInstances[i] configValueForKey:key1 source:FIRRemoteConfigSourceStatic];
  857. }
  858. XCTAssertEqualObjects(value.stringValue, @"");
  859. XCTAssertEqualObjects(value.numberValue, @(0));
  860. XCTAssertEqual(value.boolValue, NO);
  861. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  862. @"Callback of first successful config "
  863. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccess.");
  864. [fetchConfigsExpectation[i] fulfill];
  865. };
  866. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  867. }
  868. [self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
  869. }
  870. - (void)testInvalidKeyOrNamespace {
  871. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  872. FIRRemoteConfigValue *value = [_configInstances[i] configValueForKey:nil];
  873. XCTAssertNotNil(value);
  874. XCTAssertEqual(value.source, FIRRemoteConfigSourceStatic);
  875. value = [_configInstances[i] configValueForKey:nil namespace:nil];
  876. XCTAssertNotNil(value);
  877. XCTAssertEqual(value.source, FIRRemoteConfigSourceStatic);
  878. value = [_configInstances[i] configValueForKey:nil namespace:nil source:5];
  879. XCTAssertNotNil(value);
  880. XCTAssertEqual(value.source, FIRRemoteConfigSourceStatic);
  881. }
  882. }
  883. // Remote Config converts UTC times in the plists to local times. This utility function makes it
  884. // possible to check the times when running the tests in any timezone.
  885. static NSString *UTCToLocal(NSString *utcTime) {
  886. // Create a UTC dateFormatter.
  887. NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  888. [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  889. [dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
  890. NSDate *date = [dateFormatter dateFromString:utcTime];
  891. [dateFormatter setTimeZone:[NSTimeZone localTimeZone]];
  892. return [dateFormatter stringFromDate:date];
  893. }
  894. - (void)testSetDefaultsFromPlist {
  895. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  896. FIRRemoteConfig *config = _configInstances[i];
  897. [config setDefaultsFromPlistFileName:@"Defaults-testInfo"];
  898. XCTAssertEqualObjects(_configInstances[i][@"lastCheckTime"].stringValue,
  899. UTCToLocal(@"2016-02-28 18:33:31"));
  900. XCTAssertEqual(_configInstances[i][@"isPaidUser"].boolValue, YES);
  901. XCTAssertEqualObjects(_configInstances[i][@"dataValue"].stringValue, @"2.4");
  902. XCTAssertEqualObjects(_configInstances[i][@"New item"].numberValue, @(2.4));
  903. XCTAssertEqualObjects(_configInstances[i][@"Languages"].stringValue, @"English");
  904. XCTAssertEqualObjects(_configInstances[i][@"FileInfo"].stringValue,
  905. @"To setup default config.");
  906. XCTAssertEqualObjects(_configInstances[i][@"format"].stringValue, @"key to value.");
  907. // If given a wrong file name, the default will not be set and kept as previous results.
  908. [_configInstances[i] setDefaultsFromPlistFileName:@""];
  909. XCTAssertEqualObjects(_configInstances[i][@"lastCheckTime"].stringValue,
  910. UTCToLocal(@"2016-02-28 18:33:31"));
  911. [_configInstances[i] setDefaultsFromPlistFileName:@"non-existed_file"];
  912. XCTAssertEqualObjects(_configInstances[i][@"dataValue"].stringValue, @"2.4");
  913. [_configInstances[i] setDefaultsFromPlistFileName:nil namespace:nil];
  914. XCTAssertEqualObjects(_configInstances[i][@"New item"].numberValue, @(2.4));
  915. [_configInstances[i] setDefaultsFromPlistFileName:@"" namespace:@""];
  916. XCTAssertEqualObjects(_configInstances[i][@"Languages"].stringValue, @"English");
  917. }
  918. }
  919. - (void)testSetDefaultsAndNamespaceFromPlist {
  920. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  921. if (i == RCNTestRCInstanceDefault) {
  922. [_configInstances[i] setDefaultsFromPlistFileName:@"Defaults-testInfo"
  923. namespace:RCNTestsPerfNamespace];
  924. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"lastCheckTime"
  925. namespace:RCNTestsPerfNamespace]
  926. .stringValue,
  927. UTCToLocal(@"2016-02-28 18:33:31"));
  928. XCTAssertEqual([_configInstances[i] configValueForKey:@"isPaidUser"
  929. namespace:RCNTestsPerfNamespace]
  930. .boolValue,
  931. YES);
  932. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"dataValue"
  933. namespace:RCNTestsPerfNamespace]
  934. .stringValue,
  935. @"2.4");
  936. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"New item"
  937. namespace:RCNTestsPerfNamespace]
  938. .numberValue,
  939. @(2.4));
  940. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"Languages"
  941. namespace:RCNTestsPerfNamespace]
  942. .stringValue,
  943. @"English");
  944. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"FileInfo"
  945. namespace:RCNTestsPerfNamespace]
  946. .stringValue,
  947. @"To setup default config.");
  948. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"format"
  949. namespace:RCNTestsPerfNamespace]
  950. .stringValue,
  951. @"key to value.");
  952. } else {
  953. [_configInstances[i] setDefaultsFromPlistFileName:@"Defaults-testInfo"];
  954. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"lastCheckTime"].stringValue,
  955. UTCToLocal(@"2016-02-28 18:33:31"));
  956. XCTAssertEqual([_configInstances[i] configValueForKey:@"isPaidUser"].boolValue, YES);
  957. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"dataValue"].stringValue,
  958. @"2.4");
  959. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"New item"].numberValue,
  960. @(2.4));
  961. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"Languages"].stringValue,
  962. @"English");
  963. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"FileInfo"].stringValue,
  964. @"To setup default config.");
  965. XCTAssertEqualObjects([_configInstances[i] configValueForKey:@"format"].stringValue,
  966. @"key to value.");
  967. }
  968. }
  969. }
  970. - (void)testSetDeveloperMode {
  971. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  972. XCTAssertFalse(_configInstances[i].configSettings.isDeveloperModeEnabled);
  973. FIRRemoteConfigSettings *settings =
  974. [[FIRRemoteConfigSettings alloc] initWithDeveloperModeEnabled:YES];
  975. _configInstances[i].configSettings = settings;
  976. XCTAssertTrue(_configInstances[i].configSettings.isDeveloperModeEnabled);
  977. }
  978. }
  979. - (void)testAllKeysFromSource {
  980. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  981. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  982. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  983. fetchConfigsExpectation[i] = [self
  984. expectationWithDescription:[NSString
  985. stringWithFormat:@"Test allKeys methods - instance %d", i]];
  986. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  987. NSString *key0 = [NSString stringWithFormat:@"key0-%d", i];
  988. NSDictionary<NSString *, NSString *> *defaults = @{key1 : @"default key1", key0 : @"value0-0"};
  989. [_configInstances[i] setDefaults:defaults];
  990. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  991. NSError *error) {
  992. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess);
  993. XCTAssertNil(error);
  994. XCTAssertTrue([_configInstances[i] activateFetched]);
  995. if (i == RCNTestRCInstanceDefault) {
  996. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceRemote
  997. namespace:FIRNamespaceGoogleMobilePlatform]
  998. .count,
  999. 100);
  1000. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceDefault
  1001. namespace:FIRNamespaceGoogleMobilePlatform]
  1002. .count,
  1003. 2);
  1004. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceStatic
  1005. namespace:FIRNamespaceGoogleMobilePlatform]
  1006. .count,
  1007. 0);
  1008. } else {
  1009. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceRemote].count,
  1010. 100);
  1011. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceDefault].count,
  1012. 2);
  1013. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceStatic].count,
  1014. 0);
  1015. }
  1016. XCTAssertNotNil([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceRemote
  1017. namespace:@"invalid namespace"]);
  1018. XCTAssertEqual([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceRemote
  1019. namespace:@"invalid namespace"]
  1020. .count,
  1021. 0);
  1022. XCTAssertNotNil([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceRemote
  1023. namespace:nil]);
  1024. XCTAssertEqual(
  1025. [_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceRemote namespace:nil].count,
  1026. 0);
  1027. XCTAssertNotNil([_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceDefault
  1028. namespace:nil]);
  1029. XCTAssertEqual(
  1030. [_configInstances[i] allKeysFromSource:FIRRemoteConfigSourceDefault namespace:nil].count,
  1031. 0);
  1032. [fetchConfigsExpectation[i] fulfill];
  1033. };
  1034. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  1035. }
  1036. [self waitForExpectationsWithTimeout:_expectationTimeout
  1037. handler:^(NSError *error) {
  1038. XCTAssertNil(error);
  1039. }];
  1040. }
  1041. - (void)testAllKeysWithPrefix {
  1042. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  1043. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  1044. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1045. fetchConfigsExpectation[i] = [self
  1046. expectationWithDescription:[NSString
  1047. stringWithFormat:@"Test allKeys methods - instance %d", i]];
  1048. FIRRemoteConfigFetchCompletion fetchCompletion =
  1049. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  1050. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess);
  1051. XCTAssertNil(error);
  1052. NSLog(@"Testing _configInstances %d", i);
  1053. #pragma clang diagnostic push
  1054. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  1055. XCTAssertTrue([_configInstances[i] activateFetched]);
  1056. // Test keysWithPrefix:namespace: method.
  1057. if (i == RCNTestRCInstanceDefault) {
  1058. XCTAssertEqual([_configInstances[i] keysWithPrefix:@"key"
  1059. namespace:FIRNamespaceGoogleMobilePlatform]
  1060. .count,
  1061. 100);
  1062. } else {
  1063. XCTAssertEqual([_configInstances[i] keysWithPrefix:@"key"].count, 100);
  1064. }
  1065. XCTAssertEqual(
  1066. [_configInstances[i] keysWithPrefix:@"pl" namespace:@"invalid namespace"].count, 0);
  1067. XCTAssertEqual([_configInstances[i] keysWithPrefix:@"pl" namespace:nil].count, 0);
  1068. XCTAssertEqual([_configInstances[i] keysWithPrefix:@"pl" namespace:@""].count, 0);
  1069. XCTAssertNotNil([_configInstances[i] keysWithPrefix:nil namespace:nil]);
  1070. XCTAssertEqual([_configInstances[i] keysWithPrefix:nil namespace:nil].count, 0);
  1071. #pragma clang diagnostic pop
  1072. // Test keysWithPrefix: method.
  1073. XCTAssertEqual([_configInstances[i] keysWithPrefix:@"key1"].count, 12);
  1074. XCTAssertEqual([_configInstances[i] keysWithPrefix:@"key"].count, 100);
  1075. XCTAssertEqual([_configInstances[i] keysWithPrefix:@"invalid key"].count, 0);
  1076. XCTAssertEqual([_configInstances[i] keysWithPrefix:nil].count, 100);
  1077. XCTAssertEqual([_configInstances[i] keysWithPrefix:@""].count, 100);
  1078. [fetchConfigsExpectation[i] fulfill];
  1079. };
  1080. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  1081. }
  1082. [self waitForExpectationsWithTimeout:_expectationTimeout
  1083. handler:^(NSError *error) {
  1084. XCTAssertNil(error);
  1085. }];
  1086. }
  1087. - (void)testSetDeveloperModeConfigSetting {
  1088. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1089. #pragma clang diagnostic push
  1090. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  1091. FIRRemoteConfigSettings *settings =
  1092. [[FIRRemoteConfigSettings alloc] initWithDeveloperModeEnabled:YES];
  1093. [_configInstances[i] setConfigSettings:settings];
  1094. XCTAssertTrue([_configInstances[i] configSettings].isDeveloperModeEnabled);
  1095. settings = [[FIRRemoteConfigSettings alloc] initWithDeveloperModeEnabled:NO];
  1096. [_configInstances[i] setConfigSettings:settings];
  1097. XCTAssertFalse([_configInstances[i] configSettings].isDeveloperModeEnabled);
  1098. #pragma clang diagnostic pop
  1099. }
  1100. }
  1101. /// Test the minimum fetch interval is applied and read back correctly.
  1102. - (void)testSetMinimumFetchIntervalConfigSetting {
  1103. NSMutableArray<XCTestExpectation *> *fetchConfigsExpectation =
  1104. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  1105. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1106. fetchConfigsExpectation[i] = [self
  1107. expectationWithDescription:
  1108. [NSString stringWithFormat:@"Test minimumFetchInterval expectation - instance %d", i]];
  1109. FIRRemoteConfigSettings *settings = [[FIRRemoteConfigSettings alloc] init];
  1110. settings.minimumFetchInterval = 123;
  1111. [_configInstances[i] setConfigSettings:settings];
  1112. XCTAssertEqual([_configInstances[i] configSettings].minimumFetchInterval, 123);
  1113. FIRRemoteConfigFetchCompletion fetchCompletion =
  1114. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  1115. XCTAssertFalse([_configInstances[i].settings hasMinimumFetchIntervalElapsed:43200]);
  1116. // Update minimum fetch interval.
  1117. FIRRemoteConfigSettings *settings = [[FIRRemoteConfigSettings alloc] init];
  1118. settings.minimumFetchInterval = 0;
  1119. [_configInstances[i] setConfigSettings:settings];
  1120. XCTAssertEqual([_configInstances[i] configSettings].minimumFetchInterval, 0);
  1121. XCTAssertTrue([_configInstances[i].settings hasMinimumFetchIntervalElapsed:0]);
  1122. [fetchConfigsExpectation[i] fulfill];
  1123. };
  1124. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  1125. }
  1126. [self waitForExpectationsWithTimeout:_expectationTimeout
  1127. handler:^(NSError *error) {
  1128. XCTAssertNil(error);
  1129. }];
  1130. }
  1131. /// Test the fetch timeout is properly set and read back.
  1132. - (void)testSetFetchTimeoutConfigSetting {
  1133. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1134. FIRRemoteConfigSettings *settings = [[FIRRemoteConfigSettings alloc] init];
  1135. settings.fetchTimeout = 1;
  1136. [_configInstances[i] setConfigSettings:settings];
  1137. XCTAssertEqual([_configInstances[i] configSettings].fetchTimeout, 1);
  1138. NSURLSession *networkSession = [_configFetch[i] currentNetworkSession];
  1139. XCTAssertNotNil(networkSession);
  1140. XCTAssertEqual(networkSession.configuration.timeoutIntervalForResource, 1);
  1141. XCTAssertEqual(networkSession.configuration.timeoutIntervalForRequest, 1);
  1142. }
  1143. }
  1144. #pragma mark - Public Factory Methods
  1145. - (void)testConfigureConfigWithValidInput {
  1146. // Configure the default app with our options and ensure the Remote Config instance is set up
  1147. // properly.
  1148. XCTAssertNoThrow([FIRApp configureWithOptions:[self firstAppOptions]]);
  1149. XCTAssertNoThrow([FIRRemoteConfig remoteConfig]);
  1150. FIRRemoteConfig *config = [FIRRemoteConfig remoteConfig];
  1151. XCTAssertNotNil(config);
  1152. // Ensure the same instance is returned from the singleton.
  1153. FIRRemoteConfig *sameConfig = [FIRRemoteConfig remoteConfig];
  1154. XCTAssertNotNil(sameConfig);
  1155. XCTAssertEqual(config, sameConfig);
  1156. // Ensure the app name is stored properly.
  1157. XCTAssertEqual([config valueForKey:@"_appName"], kFIRDefaultAppName);
  1158. }
  1159. #pragma mark - Test Helpers
  1160. - (FIROptions *)firstAppOptions {
  1161. // TODO: Evaluate if we want to hardcode things here instead.
  1162. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"1:123:ios:123abc"
  1163. GCMSenderID:@"correct_gcm_sender_id"];
  1164. options.APIKey = @"correct_api_key";
  1165. options.projectID = @"abc-xyz-123";
  1166. return options;
  1167. }
  1168. - (FIROptions *)secondAppOptions {
  1169. FIROptions *options =
  1170. [[FIROptions alloc] initWithContentsOfFile:[[NSBundle bundleForClass:[self class]]
  1171. pathForResource:@"SecondApp-GoogleService-Info"
  1172. ofType:@"plist"]];
  1173. XCTAssertNotNil(options);
  1174. return options;
  1175. }
  1176. @end