RCNRemoteConfigTest.m 88 KB

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