RCNRemoteConfigTest.m 87 KB

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