RCNRemoteConfigTest.m 88 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807
  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. - (NSData *)createRequestBody;
  68. - (FIRConfigUpdateListenerRegistration *_Nonnull)addConfigUpdateListener:
  69. (RCNConfigUpdateCompletion _Nonnull)listener;
  70. - (void)removeConfigUpdateListener:(RCNConfigUpdateCompletion _Nonnull)listener;
  71. - (void)evaluateStreamResponse:(NSDictionary *)response error:(NSError *)dataError;
  72. @end
  73. @interface FIRRemoteConfig (ForTest)
  74. - (void)updateWithNewInstancesForConfigFetch:(RCNConfigFetch *)configFetch
  75. configContent:(RCNConfigContent *)configContent
  76. configSettings:(RCNConfigSettings *)configSettings
  77. configExperiment:(RCNConfigExperiment *)configExperiment;
  78. - (void)updateWithNewInstancesForConfigRealtime:(RCNConfigRealtime *)configRealtime;
  79. @end
  80. @implementation FIRRemoteConfig (ForTest)
  81. - (void)updateWithNewInstancesForConfigFetch:(RCNConfigFetch *)configFetch
  82. configContent:(RCNConfigContent *)configContent
  83. configSettings:(RCNConfigSettings *)configSettings
  84. configExperiment:(RCNConfigExperiment *)configExperiment {
  85. [self setValue:configFetch forKey:@"_configFetch"];
  86. [self setValue:configContent forKey:@"_configContent"];
  87. [self setValue:configSettings forKey:@"_settings"];
  88. [self setValue:configExperiment forKey:@"_configExperiment"];
  89. }
  90. - (void)updateWithNewInstancesForConfigRealtime:(RCNConfigRealtime *)configRealtime {
  91. [self setValue:configRealtime forKey:@"_configRealtime"];
  92. }
  93. @end
  94. @interface RCNConfigDBManager (Test)
  95. - (void)removeDatabaseOnDatabaseQueueAtPath:(NSString *)path;
  96. @end
  97. @interface RCNUserDefaultsManager (Test)
  98. + (NSUserDefaults *)sharedUserDefaultsForBundleIdentifier:(NSString *)bundleIdentifier;
  99. @end
  100. @interface RCNConfigSettings (Test)
  101. - (NSString *)nextRequestWithUserProperties:(NSDictionary *)userProperties;
  102. @end
  103. typedef NS_ENUM(NSInteger, RCNTestRCInstance) {
  104. RCNTestRCInstanceDefault,
  105. RCNTestRCInstanceSecondNamespace,
  106. RCNTestRCInstanceSecondApp,
  107. RCNTestRCNumTotalInstances
  108. };
  109. @interface RCNRemoteConfigTest : XCTestCase {
  110. NSTimeInterval _expectationTimeout;
  111. NSTimeInterval _checkCompletionTimeout;
  112. NSMutableArray<FIRRemoteConfig *> *_configInstances;
  113. NSMutableArray<NSDictionary<NSString *, NSString *> *> *_entries;
  114. NSMutableArray<NSDictionary<NSString *, id> *> *_response;
  115. NSMutableArray<NSData *> *_responseData;
  116. NSMutableArray<NSURLResponse *> *_URLResponse;
  117. NSMutableArray<id> *_configFetch;
  118. NSMutableArray<id> *_configRealtime;
  119. RCNConfigDBManager *_DBManager;
  120. NSUserDefaults *_userDefaults;
  121. NSString *_userDefaultsSuiteName;
  122. NSString *_DBPath;
  123. id _DBManagerMock;
  124. id _experimentMock;
  125. id _userDefaultsMock;
  126. NSString *_fullyQualifiedNamespace;
  127. RCNConfigSettings *_settings;
  128. dispatch_queue_t _queue;
  129. }
  130. @end
  131. @implementation RCNRemoteConfigTest
  132. - (void)setUp {
  133. [super setUp];
  134. FIRSetLoggerLevel(FIRLoggerLevelMax);
  135. _expectationTimeout = 5;
  136. _checkCompletionTimeout = 1.0;
  137. // Always remove the database at the start of testing.
  138. _DBPath = [RCNTestUtilities remoteConfigPathForTestDatabase];
  139. _DBManagerMock = OCMClassMock([RCNConfigDBManager class]);
  140. OCMStub([_DBManagerMock remoteConfigPathForDatabase]).andReturn(_DBPath);
  141. _DBManager = [[RCNConfigDBManager alloc] init];
  142. _userDefaultsSuiteName = [RCNTestUtilities userDefaultsSuiteNameForTestSuite];
  143. _userDefaults = [[NSUserDefaults alloc] initWithSuiteName:_userDefaultsSuiteName];
  144. _userDefaultsMock = OCMClassMock([RCNUserDefaultsManager class]);
  145. OCMStub([_userDefaultsMock sharedUserDefaultsForBundleIdentifier:[OCMArg any]])
  146. .andReturn(_userDefaults);
  147. _experimentMock = OCMClassMock([RCNConfigExperiment class]);
  148. OCMStub([_experimentMock
  149. updateExperimentsWithHandler:([OCMArg invokeBlockWithArgs:[NSNull null], nil])]);
  150. RCNConfigContent *configContent = [[RCNConfigContent alloc] initWithDBManager:_DBManager];
  151. _configInstances = [[NSMutableArray alloc] initWithCapacity:3];
  152. _entries = [[NSMutableArray alloc] initWithCapacity:3];
  153. _response = [[NSMutableArray alloc] initWithCapacity:3];
  154. _responseData = [[NSMutableArray alloc] initWithCapacity:3];
  155. _URLResponse = [[NSMutableArray alloc] initWithCapacity:3];
  156. _configFetch = [[NSMutableArray alloc] initWithCapacity:3];
  157. _configRealtime = [[NSMutableArray alloc] initWithCapacity:3];
  158. // Populate the default, second app, second namespace instances.
  159. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  160. // Fake a response for default instance.
  161. NSMutableDictionary<NSString *, NSString *> *valuesDict = [[NSMutableDictionary alloc] init];
  162. for (int count = 1; count <= 100; count++) {
  163. NSString *key = [NSString stringWithFormat:@"key%d-%d", count, i];
  164. NSString *value = [NSString stringWithFormat:@"value%d-%d", count, i];
  165. valuesDict[key] = value;
  166. }
  167. _entries[i] = valuesDict;
  168. NSString *currentAppName = nil;
  169. FIROptions *currentOptions = nil;
  170. NSString *currentNamespace = nil;
  171. switch (i) {
  172. case RCNTestRCInstanceSecondNamespace:
  173. currentAppName = RCNTestsDefaultFIRAppName;
  174. currentOptions = [self firstAppOptions];
  175. currentNamespace = RCNTestsPerfNamespace;
  176. break;
  177. case RCNTestRCInstanceSecondApp:
  178. currentAppName = RCNTestsSecondFIRAppName;
  179. currentOptions = [self secondAppOptions];
  180. currentNamespace = FIRNamespaceGoogleMobilePlatform;
  181. break;
  182. case RCNTestRCInstanceDefault:
  183. default:
  184. currentAppName = RCNTestsDefaultFIRAppName;
  185. currentOptions = [self firstAppOptions];
  186. currentNamespace = RCNTestsFIRNamespace;
  187. break;
  188. }
  189. _fullyQualifiedNamespace =
  190. [NSString stringWithFormat:@"%@:%@", currentNamespace, currentAppName];
  191. FIRRemoteConfig *config =
  192. OCMPartialMock([[FIRRemoteConfig alloc] initWithAppName:currentAppName
  193. FIROptions:currentOptions
  194. namespace:currentNamespace
  195. DBManager:_DBManager
  196. configContent:configContent
  197. analytics:nil]);
  198. _configInstances[i] = config;
  199. _settings = [[RCNConfigSettings alloc] initWithDatabaseManager:_DBManager
  200. namespace:_fullyQualifiedNamespace
  201. firebaseAppName:currentAppName
  202. googleAppID:currentOptions.googleAppID];
  203. _queue = dispatch_queue_create(
  204. [[NSString stringWithFormat:@"testqueue: %d", i] cStringUsingEncoding:NSUTF8StringEncoding],
  205. DISPATCH_QUEUE_SERIAL);
  206. _configFetch[i] =
  207. OCMPartialMock([[RCNConfigFetch alloc] initWithContent:configContent
  208. DBManager:_DBManager
  209. settings:_settings
  210. analytics:nil
  211. experiment:_experimentMock
  212. queue:_queue
  213. namespace:_fullyQualifiedNamespace
  214. options:currentOptions]);
  215. _configRealtime[i] = OCMPartialMock([[RCNConfigRealtime alloc] init:_configFetch[i]
  216. settings:_settings
  217. namespace:_fullyQualifiedNamespace
  218. options:currentOptions]);
  219. _settings.configInstallationsIdentifier = @"iid";
  220. OCMStubRecorder *mock = OCMStub([_configFetch[i] fetchConfigWithExpirationDuration:0
  221. completionHandler:OCMOCK_ANY]);
  222. mock = [mock ignoringNonObjectArgs];
  223. mock.andDo(^(NSInvocation *invocation) {
  224. __unsafe_unretained void (^handler)(FIRRemoteConfigFetchStatus status,
  225. NSError *_Nullable error) = nil;
  226. [invocation getArgument:&handler atIndex:3];
  227. [self->_configFetch[i] fetchWithUserProperties:[[NSDictionary alloc] init]
  228. fetchTypeHeader:@"Base/1"
  229. completionHandler:handler
  230. updateCompletionHandler:nil];
  231. });
  232. _response[i] = @{@"state" : @"UPDATE", @"entries" : _entries[i]};
  233. _responseData[i] = [NSJSONSerialization dataWithJSONObject:_response[i] options:0 error:nil];
  234. _URLResponse[i] = [[NSHTTPURLResponse alloc]
  235. initWithURL:[NSURL URLWithString:@"https://firebase.com"]
  236. statusCode:200
  237. HTTPVersion:nil
  238. headerFields:@{@"etag" : [NSString stringWithFormat:@"etag1-%d", i]}];
  239. id completionBlock =
  240. [OCMArg invokeBlockWithArgs:_responseData[i], _URLResponse[i], [NSNull null], nil];
  241. OCMStub([_configFetch[i] URLSessionDataTaskWithContent:[OCMArg any]
  242. fetchTypeHeader:[OCMArg any]
  243. completionHandler:completionBlock])
  244. .andReturn(nil);
  245. [_configInstances[i] updateWithNewInstancesForConfigFetch:_configFetch[i]
  246. configContent:configContent
  247. configSettings:_settings
  248. configExperiment:_experimentMock];
  249. [_configInstances[i] updateWithNewInstancesForConfigRealtime:_configRealtime[i]];
  250. }
  251. }
  252. - (void)tearDown {
  253. [_DBManager removeDatabaseOnDatabaseQueueAtPath:_DBPath];
  254. [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:_userDefaultsSuiteName];
  255. [_DBManagerMock stopMocking];
  256. _DBManagerMock = nil;
  257. [_userDefaultsMock stopMocking];
  258. _userDefaultsMock = nil;
  259. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  260. [(id)_configInstances[i] stopMocking];
  261. [(id)_configFetch[i] stopMocking];
  262. }
  263. [_configInstances removeAllObjects];
  264. [_configFetch removeAllObjects];
  265. [_configRealtime removeAllObjects];
  266. _configInstances = nil;
  267. _configFetch = nil;
  268. [super tearDown];
  269. }
  270. - (void)testFetchConfigWithNilCallback {
  271. NSMutableArray<XCTestExpectation *> *expectations =
  272. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  273. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  274. expectations[i] = [self
  275. expectationWithDescription:
  276. [NSString stringWithFormat:@"Set defaults no callback expectation - instance %d", i]];
  277. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  278. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:nil];
  279. dispatch_after(
  280. dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_checkCompletionTimeout * NSEC_PER_SEC)),
  281. dispatch_get_main_queue(), ^{
  282. XCTAssertEqual(self->_configInstances[i].lastFetchStatus,
  283. FIRRemoteConfigFetchStatusSuccess);
  284. [expectations[i] fulfill];
  285. });
  286. }
  287. [self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
  288. }
  289. - (void)testFetchConfigsSuccessfully {
  290. NSMutableArray<XCTestExpectation *> *expectations =
  291. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  292. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  293. expectations[i] =
  294. [self expectationWithDescription:
  295. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  296. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  297. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  298. NSError *error) {
  299. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  300. XCTAssertNil(error);
  301. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  302. XCTAssertTrue(changed);
  303. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  304. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  305. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  306. NSString *value2 = [NSString stringWithFormat:@"value2-%d", i];
  307. XCTAssertEqualObjects(self->_configInstances[i][key1].stringValue, value1);
  308. XCTAssertEqualObjects(self->_configInstances[i][key2].stringValue, value2);
  309. OCMVerify([self->_configInstances[i] objectForKeyedSubscript:key1]);
  310. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  311. @"Callback of first successful config "
  312. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccessFresh.");
  313. XCTAssertNotNil(self->_configInstances[i].lastFetchTime);
  314. XCTAssertGreaterThan(self->_configInstances[i].lastFetchTime.timeIntervalSince1970, 0,
  315. @"last fetch time interval should be set.");
  316. [expectations[i] fulfill];
  317. }];
  318. };
  319. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  320. }
  321. [self waitForExpectationsWithTimeout:_expectationTimeout
  322. handler:^(NSError *error) {
  323. XCTAssertNil(error);
  324. }];
  325. }
  326. - (void)testFetchAndActivate {
  327. NSMutableArray<XCTestExpectation *> *expectations =
  328. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  329. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  330. expectations[i] =
  331. [self expectationWithDescription:
  332. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  333. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  334. FIRRemoteConfigFetchAndActivateCompletion fetchAndActivateCompletion = ^void(
  335. FIRRemoteConfigFetchAndActivateStatus status, NSError *error) {
  336. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  337. XCTAssertNil(error);
  338. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  339. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  340. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  341. NSString *value2 = [NSString stringWithFormat:@"value2-%d", i];
  342. XCTAssertEqualObjects(self->_configInstances[i][key1].stringValue, value1);
  343. XCTAssertEqualObjects(self->_configInstances[i][key2].stringValue, value2);
  344. OCMVerify([self->_configInstances[i] objectForKeyedSubscript:key1]);
  345. XCTAssertEqual(
  346. status, FIRRemoteConfigFetchAndActivateStatusSuccessFetchedFromRemote,
  347. @"Callback of first successful config "
  348. @"fetchAndActivate status must equal to FIRRemoteConfigFetchAndStatusSuccessFromRemote.");
  349. XCTAssertNotNil(self->_configInstances[i].lastFetchTime);
  350. XCTAssertGreaterThan(self->_configInstances[i].lastFetchTime.timeIntervalSince1970, 0,
  351. @"last fetch time interval should be set.");
  352. [expectations[i] fulfill];
  353. };
  354. // Update the minimum fetch interval to 0. This disables the cache and forces a remote fetch
  355. // request.
  356. FIRRemoteConfigSettings *settings = [[FIRRemoteConfigSettings alloc] init];
  357. settings.minimumFetchInterval = 0;
  358. [_configInstances[i] setConfigSettings:settings];
  359. [_configInstances[i] fetchAndActivateWithCompletionHandler:fetchAndActivateCompletion];
  360. }
  361. [self waitForExpectationsWithTimeout:_expectationTimeout
  362. handler:^(NSError *error) {
  363. XCTAssertNil(error);
  364. }];
  365. }
  366. // TODO: Try splitting into smaller tests.
  367. - (void)testFetchConfigsSuccessfullyWithNewActivateMethodSignature {
  368. NSMutableArray<XCTestExpectation *> *expectations =
  369. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  370. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  371. expectations[i] =
  372. [self expectationWithDescription:
  373. [NSString stringWithFormat:@"Test fetch configs successfully - instance %d", i]];
  374. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  375. FIRRemoteConfigFetchCompletion fetchCompletion = ^(FIRRemoteConfigFetchStatus status,
  376. NSError *error) {
  377. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  378. XCTAssertNil(error);
  379. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  380. XCTAssertTrue(changed);
  381. XCTAssertNil(error);
  382. NSString *key1 = [NSString stringWithFormat:@"key1-%d", i];
  383. NSString *key2 = [NSString stringWithFormat:@"key2-%d", i];
  384. NSString *value1 = [NSString stringWithFormat:@"value1-%d", i];
  385. NSString *value2 = [NSString stringWithFormat:@"value2-%d", i];
  386. XCTAssertEqualObjects(self->_configInstances[i][key1].stringValue, value1);
  387. XCTAssertEqualObjects(self->_configInstances[i][key2].stringValue, value2);
  388. OCMVerify([self->_configInstances[i] objectForKeyedSubscript:key1]);
  389. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  390. @"Callback of first successful config "
  391. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccessFresh.");
  392. XCTAssertNotNil(self->_configInstances[i].lastFetchTime);
  393. XCTAssertGreaterThan(self->_configInstances[i].lastFetchTime.timeIntervalSince1970, 0,
  394. @"last fetch time interval should be set.");
  395. // A second activate should have no effect.
  396. [self->_configInstances[i]
  397. activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  398. XCTAssertFalse(changed);
  399. XCTAssertNil(error);
  400. }];
  401. [expectations[i] fulfill];
  402. }];
  403. };
  404. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  405. }
  406. [self waitForExpectationsWithTimeout:_expectationTimeout
  407. handler:^(NSError *error) {
  408. XCTAssertNil(error);
  409. }];
  410. }
  411. - (void)testEnumeratingConfigResults {
  412. NSMutableArray<XCTestExpectation *> *expectations =
  413. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  414. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  415. expectations[i] = [self
  416. expectationWithDescription:
  417. [NSString stringWithFormat:@"Test enumerating configs successfully - instance %d", i]];
  418. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  419. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  420. NSError *error) {
  421. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusSuccess);
  422. XCTAssertNil(error);
  423. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  424. XCTAssertTrue(changed);
  425. NSString *key5 = [NSString stringWithFormat:@"key5-%d", i];
  426. NSString *key19 = [NSString stringWithFormat:@"key19-%d", i];
  427. NSString *value5 = [NSString stringWithFormat:@"value5-%d", i];
  428. NSString *value19 = [NSString stringWithFormat:@"value19-%d", i];
  429. XCTAssertEqualObjects(self->_configInstances[i][key5].stringValue, value5);
  430. XCTAssertEqualObjects(self->_configInstances[i][key19].stringValue, value19);
  431. // Test enumerating config values.
  432. for (NSString *key in self->_configInstances[i]) {
  433. if ([key isEqualToString:key5]) {
  434. XCTAssertEqualObjects(self->_configInstances[i][key5].stringValue, value5);
  435. }
  436. if ([key isEqualToString:key19]) {
  437. XCTAssertEqualObjects(self->_configInstances[i][key19].stringValue, value19);
  438. }
  439. }
  440. XCTAssertEqual(status, FIRRemoteConfigFetchStatusSuccess,
  441. @"Callback of first successful config "
  442. @"fetch. Status must equal to FIRRemoteConfigFetchStatusSuccessFresh.");
  443. XCTAssertNotNil(self->_configInstances[i].lastFetchTime);
  444. XCTAssertGreaterThan(self->_configInstances[i].lastFetchTime.timeIntervalSince1970, 0,
  445. @"last fetch time interval should be set.");
  446. [expectations[i] fulfill];
  447. }];
  448. };
  449. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  450. }
  451. [self waitForExpectationsWithTimeout:_expectationTimeout
  452. handler:^(NSError *error) {
  453. XCTAssertNil(error);
  454. }];
  455. }
  456. - (void)testFetchAndActivate3pNamespaceUpdatesExperiments {
  457. [[_experimentMock expect] updateExperimentsWithResponse:[OCMArg any]];
  458. XCTestExpectation *expectation = [self
  459. expectationWithDescription:[NSString stringWithFormat:@"FetchAndActivate call for 'firebase' "
  460. @"namespace updates experiment data"]];
  461. XCTAssertEqual(_configInstances[RCNTestRCInstanceDefault].lastFetchStatus,
  462. FIRRemoteConfigFetchStatusNoFetchYet);
  463. FIRRemoteConfigFetchAndActivateCompletion fetchAndActivateCompletion =
  464. ^void(FIRRemoteConfigFetchAndActivateStatus status, NSError *error) {
  465. XCTAssertEqual(status, FIRRemoteConfigFetchAndActivateStatusSuccessFetchedFromRemote);
  466. XCTAssertNil(error);
  467. XCTAssertEqual(self->_configInstances[RCNTestRCInstanceDefault].lastFetchStatus,
  468. FIRRemoteConfigFetchStatusSuccess);
  469. XCTAssertNotNil(self->_configInstances[RCNTestRCInstanceDefault].lastFetchTime);
  470. XCTAssertGreaterThan(
  471. self->_configInstances[RCNTestRCInstanceDefault].lastFetchTime.timeIntervalSince1970, 0,
  472. @"last fetch time interval should be set.");
  473. [expectation fulfill];
  474. };
  475. [_configInstances[RCNTestRCInstanceDefault]
  476. fetchAndActivateWithCompletionHandler:fetchAndActivateCompletion];
  477. [self waitForExpectationsWithTimeout:_expectationTimeout
  478. handler:^(NSError *error) {
  479. XCTAssertNil(error);
  480. }];
  481. }
  482. - (void)testFetchAndActivateOtherNamespaceDoesntUpdateExperiments {
  483. [[_experimentMock reject] updateExperimentsWithResponse:[OCMArg any]];
  484. XCTestExpectation *expectation = [self
  485. expectationWithDescription:
  486. [NSString stringWithFormat:@"FetchAndActivate call for namespace other than 'firebase' "
  487. @"doesn't update experiment data"]];
  488. XCTAssertEqual(_configInstances[RCNTestRCInstanceSecondNamespace].lastFetchStatus,
  489. FIRRemoteConfigFetchStatusNoFetchYet);
  490. FIRRemoteConfigFetchAndActivateCompletion fetchAndActivateCompletion =
  491. ^void(FIRRemoteConfigFetchAndActivateStatus status, NSError *error) {
  492. XCTAssertEqual(status, FIRRemoteConfigFetchAndActivateStatusSuccessFetchedFromRemote);
  493. XCTAssertNil(error);
  494. XCTAssertEqual(self->_configInstances[RCNTestRCInstanceSecondNamespace].lastFetchStatus,
  495. FIRRemoteConfigFetchStatusSuccess);
  496. XCTAssertNotNil(self->_configInstances[RCNTestRCInstanceSecondNamespace].lastFetchTime);
  497. XCTAssertGreaterThan(self->_configInstances[RCNTestRCInstanceSecondNamespace]
  498. .lastFetchTime.timeIntervalSince1970,
  499. 0, @"last fetch time interval should be set.");
  500. [expectation fulfill];
  501. };
  502. [_configInstances[RCNTestRCInstanceSecondNamespace]
  503. fetchAndActivateWithCompletionHandler:fetchAndActivateCompletion];
  504. [self waitForExpectationsWithTimeout:_expectationTimeout
  505. handler:^(NSError *error) {
  506. XCTAssertNil(error);
  507. }];
  508. }
  509. - (void)testFetchConfigsFailed {
  510. // Override the setup values to return back an error status.
  511. RCNConfigContent *configContent = [[RCNConfigContent alloc] initWithDBManager:_DBManager];
  512. // Populate the default, second app, second namespace instances.
  513. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  514. NSString *currentAppName = nil;
  515. FIROptions *currentOptions = nil;
  516. NSString *currentNamespace = nil;
  517. switch (i) {
  518. case RCNTestRCInstanceSecondNamespace:
  519. currentAppName = RCNTestsDefaultFIRAppName;
  520. currentOptions = [self firstAppOptions];
  521. currentNamespace = RCNTestsPerfNamespace;
  522. break;
  523. case RCNTestRCInstanceSecondApp:
  524. currentAppName = RCNTestsSecondFIRAppName;
  525. currentOptions = [self secondAppOptions];
  526. currentNamespace = FIRNamespaceGoogleMobilePlatform;
  527. break;
  528. case RCNTestRCInstanceDefault:
  529. default:
  530. currentAppName = RCNTestsDefaultFIRAppName;
  531. currentOptions = [self firstAppOptions];
  532. currentNamespace = RCNTestsFIRNamespace;
  533. break;
  534. }
  535. RCNUserDefaultsManager *userDefaultsManager =
  536. [[RCNUserDefaultsManager alloc] initWithAppName:currentAppName
  537. bundleID:[NSBundle mainBundle].bundleIdentifier
  538. namespace:_fullyQualifiedNamespace];
  539. userDefaultsManager.lastFetchTime = 0;
  540. FIRRemoteConfig *config =
  541. OCMPartialMock([[FIRRemoteConfig alloc] initWithAppName:currentAppName
  542. FIROptions:currentOptions
  543. namespace:currentNamespace
  544. DBManager:_DBManager
  545. configContent:configContent
  546. analytics:nil]);
  547. _configInstances[i] = config;
  548. _configFetch[i] =
  549. OCMPartialMock([[RCNConfigFetch alloc] initWithContent:configContent
  550. DBManager:_DBManager
  551. settings:_settings
  552. analytics:nil
  553. experiment:nil
  554. queue:_queue
  555. namespace:_fullyQualifiedNamespace
  556. options:currentOptions]);
  557. _configRealtime[i] = OCMPartialMock([[RCNConfigRealtime alloc] init:_configFetch[i]
  558. settings:_settings
  559. namespace:_fullyQualifiedNamespace
  560. options:currentOptions]);
  561. OCMStub([_configFetch[i] fetchConfigWithExpirationDuration:43200 completionHandler:OCMOCK_ANY])
  562. .andDo(^(NSInvocation *invocation) {
  563. __unsafe_unretained void (^handler)(FIRRemoteConfigFetchStatus status,
  564. NSError *_Nullable error) = nil;
  565. [invocation getArgument:&handler atIndex:3];
  566. [self->_configFetch[i] fetchWithUserProperties:[[NSDictionary alloc] init]
  567. fetchTypeHeader:@"Base/1"
  568. completionHandler:handler
  569. updateCompletionHandler:nil];
  570. });
  571. _response[i] = @{};
  572. _responseData[i] = [NSJSONSerialization dataWithJSONObject:_response[i] options:0 error:nil];
  573. _URLResponse[i] =
  574. [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"https://firebase.com"]
  575. statusCode:500
  576. HTTPVersion:nil
  577. headerFields:@{@"etag" : @"etag1"}];
  578. [_configInstances[i] updateWithNewInstancesForConfigFetch:_configFetch[i]
  579. configContent:configContent
  580. configSettings:_settings
  581. configExperiment:nil];
  582. }
  583. // Make the fetch calls for all instances.
  584. NSMutableArray<XCTestExpectation *> *expectations =
  585. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  586. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  587. expectations[i] = [self
  588. expectationWithDescription:
  589. [NSString stringWithFormat:@"Test enumerating configs successfully - instance %d", i]];
  590. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  591. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  592. NSError *error) {
  593. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusFailure);
  594. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  595. XCTAssertFalse(changed);
  596. XCTAssertNil(error);
  597. FIRRemoteConfigValue *value = self->_configInstances[RCNTestRCInstanceDefault][@"key1"];
  598. XCTAssertEqual((int)value.source, (int)FIRRemoteConfigSourceStatic);
  599. XCTAssertEqualObjects(value.stringValue, @"");
  600. XCTAssertEqual(value.boolValue, NO);
  601. [expectations[i] fulfill];
  602. }];
  603. };
  604. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  605. }
  606. [self waitForExpectationsWithTimeout:_expectationTimeout
  607. handler:^(NSError *error) {
  608. XCTAssertNil(error);
  609. }];
  610. }
  611. // TODO(mandard): Break up test with helper methods.
  612. - (void)testFetchConfigsFailedErrorNoNetwork {
  613. // Override the setup values to return back an error status.
  614. RCNConfigContent *configContent = [[RCNConfigContent alloc] initWithDBManager:_DBManager];
  615. // Populate the default, second app, second namespace instances.
  616. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  617. NSString *currentAppName = nil;
  618. FIROptions *currentOptions = nil;
  619. NSString *currentNamespace = nil;
  620. switch (i) {
  621. case RCNTestRCInstanceSecondNamespace:
  622. currentAppName = RCNTestsDefaultFIRAppName;
  623. currentOptions = [self firstAppOptions];
  624. currentNamespace = RCNTestsPerfNamespace;
  625. break;
  626. case RCNTestRCInstanceSecondApp:
  627. currentAppName = RCNTestsSecondFIRAppName;
  628. currentOptions = [self secondAppOptions];
  629. currentNamespace = FIRNamespaceGoogleMobilePlatform;
  630. break;
  631. case RCNTestRCInstanceDefault:
  632. default:
  633. currentAppName = RCNTestsDefaultFIRAppName;
  634. currentOptions = [self firstAppOptions];
  635. currentNamespace = RCNTestsFIRNamespace;
  636. break;
  637. }
  638. NSString *fullyQualifiedNamespace =
  639. [NSString stringWithFormat:@"%@:%@", currentNamespace, currentAppName];
  640. RCNUserDefaultsManager *userDefaultsManager =
  641. [[RCNUserDefaultsManager alloc] initWithAppName:currentAppName
  642. bundleID:[NSBundle mainBundle].bundleIdentifier
  643. namespace:fullyQualifiedNamespace];
  644. userDefaultsManager.lastFetchTime = 0;
  645. FIRRemoteConfig *config =
  646. OCMPartialMock([[FIRRemoteConfig alloc] initWithAppName:currentAppName
  647. FIROptions:currentOptions
  648. namespace:currentNamespace
  649. DBManager:_DBManager
  650. configContent:configContent
  651. analytics:nil]);
  652. _configInstances[i] = config;
  653. RCNConfigSettings *settings =
  654. [[RCNConfigSettings alloc] initWithDatabaseManager:_DBManager
  655. namespace:fullyQualifiedNamespace
  656. firebaseAppName:currentAppName
  657. googleAppID:currentOptions.googleAppID];
  658. dispatch_queue_t queue = dispatch_queue_create(
  659. [[NSString stringWithFormat:@"testqueue: %d", i] cStringUsingEncoding:NSUTF8StringEncoding],
  660. DISPATCH_QUEUE_SERIAL);
  661. _configFetch[i] = OCMPartialMock([[RCNConfigFetch alloc] initWithContent:configContent
  662. DBManager:_DBManager
  663. settings:settings
  664. analytics:nil
  665. experiment:nil
  666. queue:queue
  667. namespace:fullyQualifiedNamespace
  668. options:currentOptions]);
  669. _configRealtime[i] = OCMPartialMock([[RCNConfigRealtime alloc] init:_configFetch[i]
  670. settings:settings
  671. namespace:fullyQualifiedNamespace
  672. options:currentOptions]);
  673. OCMStub([_configFetch[i] fetchConfigWithExpirationDuration:43200 completionHandler:OCMOCK_ANY])
  674. .andDo(^(NSInvocation *invocation) {
  675. __unsafe_unretained void (^handler)(FIRRemoteConfigFetchStatus status,
  676. NSError *_Nullable error) = nil;
  677. [invocation getArgument:&handler atIndex:3];
  678. [self->_configFetch[i] fetchWithUserProperties:[[NSDictionary alloc] init]
  679. fetchTypeHeader:@"Base/1"
  680. completionHandler:handler
  681. updateCompletionHandler:nil];
  682. });
  683. _response[i] = @{};
  684. _responseData[i] = [NSJSONSerialization dataWithJSONObject:_response[i] options:0 error:nil];
  685. // A no network error is accompanied with an HTTP status code of 0.
  686. _URLResponse[i] =
  687. [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"https://firebase.com"]
  688. statusCode:0
  689. HTTPVersion:nil
  690. headerFields:@{@"etag" : @"etag1"}];
  691. [_configInstances[i] updateWithNewInstancesForConfigFetch:_configFetch[i]
  692. configContent:configContent
  693. configSettings:settings
  694. configExperiment:nil];
  695. }
  696. // Make the fetch calls for all instances.
  697. NSMutableArray<XCTestExpectation *> *expectations =
  698. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  699. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  700. expectations[i] = [self
  701. expectationWithDescription:
  702. [NSString stringWithFormat:@"Test enumerating configs successfully - instance %d", i]];
  703. XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  704. FIRRemoteConfigFetchCompletion fetchCompletion = ^void(FIRRemoteConfigFetchStatus status,
  705. NSError *error) {
  706. XCTAssertEqual(self->_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusFailure);
  707. [self->_configInstances[i] activateWithCompletion:^(BOOL changed, NSError *_Nullable error) {
  708. XCTAssertFalse(changed);
  709. XCTAssertNil(error);
  710. // No such key, still return a static value.
  711. FIRRemoteConfigValue *value = self->_configInstances[RCNTestRCInstanceDefault][@"key1"];
  712. XCTAssertEqual((int)value.source, (int)FIRRemoteConfigSourceStatic);
  713. XCTAssertEqualObjects(value.stringValue, @"");
  714. XCTAssertEqual(value.boolValue, NO);
  715. [expectations[i] fulfill];
  716. }];
  717. };
  718. [_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  719. }
  720. [self waitForExpectationsWithTimeout:_expectationTimeout
  721. handler:^(NSError *error) {
  722. XCTAssertNil(error);
  723. }];
  724. }
  725. - (void)testFetchFailedNoNetworkErrorDoesNotThrottle {
  726. RCNConfigContent *configContent = [[RCNConfigContent alloc] initWithDBManager:_DBManager];
  727. NSString *currentAppName = RCNTestsDefaultFIRAppName;
  728. FIROptions *currentOptions = [self firstAppOptions];
  729. NSString *currentNamespace = RCNTestsFIRNamespace;
  730. NSString *fullyQualifiedNamespace =
  731. [NSString stringWithFormat:@"%@:%@", currentNamespace, currentAppName];
  732. RCNUserDefaultsManager *userDefaultsManager =
  733. [[RCNUserDefaultsManager alloc] initWithAppName:currentAppName
  734. bundleID:[NSBundle mainBundle].bundleIdentifier
  735. namespace:fullyQualifiedNamespace];
  736. userDefaultsManager.lastFetchTime = 0;
  737. FIRRemoteConfig *config = OCMPartialMock([[FIRRemoteConfig alloc] initWithAppName:currentAppName
  738. FIROptions:currentOptions
  739. namespace:currentNamespace
  740. DBManager:_DBManager
  741. configContent:configContent
  742. analytics:nil]);
  743. RCNConfigSettings *settings =
  744. [[RCNConfigSettings alloc] initWithDatabaseManager:_DBManager
  745. namespace:fullyQualifiedNamespace
  746. firebaseAppName:currentAppName
  747. googleAppID:currentOptions.googleAppID];
  748. dispatch_queue_t queue = dispatch_queue_create(
  749. [[NSString stringWithFormat:@"testqueue"] cStringUsingEncoding:NSUTF8StringEncoding],
  750. DISPATCH_QUEUE_SERIAL);
  751. RCNConfigFetch *configFetch =
  752. OCMPartialMock([[RCNConfigFetch alloc] initWithContent:configContent
  753. DBManager:_DBManager
  754. settings:settings
  755. analytics:nil
  756. experiment:nil
  757. queue:queue
  758. namespace:fullyQualifiedNamespace
  759. options:currentOptions]);
  760. OCMStub([configFetch fetchConfigWithExpirationDuration:43200 completionHandler:OCMOCK_ANY])
  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. fetchTypeHeader:@"Base/1"
  767. completionHandler:handler
  768. updateCompletionHandler:nil];
  769. });
  770. _responseData[0] = [NSJSONSerialization dataWithJSONObject:@{} options:0 error:nil];
  771. // A no network error is accompanied with an HTTP status code of 0.
  772. _URLResponse[0] =
  773. [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"https://firebase.com"]
  774. statusCode:0
  775. HTTPVersion:nil
  776. headerFields:@{@"etag" : @"etag1"}];
  777. [config updateWithNewInstancesForConfigFetch:configFetch
  778. configContent:configContent
  779. configSettings:settings
  780. configExperiment:nil];
  781. XCTestExpectation *expectation =
  782. [self expectationWithDescription:@"Network error doesn't increase throttle interval"];
  783. XCTAssertEqual(config.lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
  784. FIRRemoteConfigFetchCompletion fetchCompletion =
  785. ^void(FIRRemoteConfigFetchStatus status, NSError *error) {
  786. XCTAssertEqual(config.lastFetchStatus, FIRRemoteConfigFetchStatusFailure);
  787. XCTAssertEqual(settings.exponentialBackoffRetryInterval, 0);
  788. [expectation fulfill];
  789. };
  790. [config fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
  791. [self waitForExpectationsWithTimeout:_expectationTimeout
  792. handler:^(NSError *error) {
  793. XCTAssertNil(error);
  794. }];
  795. }
  796. // Activate should return false if a fetch response returns 200 with NO_CHANGE as the response body.
  797. - (void)testActivateOnFetchNoChangeStatus {
  798. // Override the setup values to return back an error status.
  799. RCNConfigContent *configContent = [[RCNConfigContent alloc] initWithDBManager:_DBManager];
  800. // Populate the default, second app, second namespace instances.
  801. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  802. NSString *currentAppName = nil;
  803. FIROptions *currentOptions = nil;
  804. NSString *currentNamespace = nil;
  805. switch (i) {
  806. case RCNTestRCInstanceSecondNamespace:
  807. currentAppName = RCNTestsDefaultFIRAppName;
  808. currentOptions = [self firstAppOptions];
  809. currentNamespace = RCNTestsPerfNamespace;
  810. break;
  811. case RCNTestRCInstanceSecondApp:
  812. currentAppName = RCNTestsSecondFIRAppName;
  813. currentOptions = [self secondAppOptions];
  814. currentNamespace = FIRNamespaceGoogleMobilePlatform;
  815. break;
  816. case RCNTestRCInstanceDefault:
  817. default:
  818. currentAppName = RCNTestsDefaultFIRAppName;
  819. currentOptions = [self firstAppOptions];
  820. currentNamespace = RCNTestsFIRNamespace;
  821. break;
  822. }
  823. NSString *fullyQualifiedNamespace =
  824. [NSString stringWithFormat:@"%@:%@", currentNamespace, currentAppName];
  825. RCNUserDefaultsManager *userDefaultsManager =
  826. [[RCNUserDefaultsManager alloc] initWithAppName:currentAppName
  827. bundleID:[NSBundle mainBundle].bundleIdentifier
  828. namespace:fullyQualifiedNamespace];
  829. userDefaultsManager.lastFetchTime = 10;
  830. FIRRemoteConfig *config =
  831. OCMPartialMock([[FIRRemoteConfig alloc] initWithAppName:currentAppName
  832. FIROptions:currentOptions
  833. namespace:currentNamespace
  834. DBManager:_DBManager
  835. configContent:configContent
  836. analytics:nil]);
  837. _configInstances[i] = config;
  838. RCNConfigSettings *settings =
  839. [[RCNConfigSettings alloc] initWithDatabaseManager:_DBManager
  840. namespace:fullyQualifiedNamespace
  841. firebaseAppName:currentAppName
  842. googleAppID:currentOptions.googleAppID];
  843. // Start the test with the assumption that we have some data that was fetched and activated.
  844. settings.lastETag = @"etag1";
  845. settings.lastETagUpdateTime = 100;
  846. settings.lastApplyTimeInterval = 101;
  847. dispatch_queue_t queue =
  848. dispatch_queue_create([[NSString stringWithFormat:@"testNoStatusFetchQueue: %d", i]
  849. cStringUsingEncoding:NSUTF8StringEncoding],
  850. DISPATCH_QUEUE_SERIAL);
  851. _configFetch[i] = OCMPartialMock([[RCNConfigFetch alloc] initWithContent:configContent
  852. DBManager:_DBManager
  853. settings:settings
  854. analytics:nil
  855. experiment:nil
  856. queue:queue
  857. namespace:fullyQualifiedNamespace
  858. options:currentOptions]);
  859. _configRealtime[i] = OCMPartialMock([[RCNConfigRealtime alloc] init:_configFetch[i]
  860. settings:settings
  861. namespace:fullyQualifiedNamespace
  862. options:currentOptions]);
  863. OCMStub([_configFetch[i] fetchConfigWithExpirationDuration:43200 completionHandler:OCMOCK_ANY])
  864. .andDo(^(NSInvocation *invocation) {
  865. __unsafe_unretained void (^handler)(FIRRemoteConfigFetchStatus status,
  866. NSError *_Nullable error) = nil;
  867. [invocation getArgument:&handler atIndex:3];
  868. [self->_configFetch[i] fetchWithUserProperties:[[NSDictionary alloc] init]
  869. fetchTypeHeader:@"Base/1"
  870. completionHandler:handler
  871. updateCompletionHandler:nil];
  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. fetchTypeHeader:@"Base/1"
  884. completionHandler:completionBlock])
  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 = 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(FIRRemoteConfigUpdate *_Nullable configUpdate, 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(FIRRemoteConfigUpdate *_Nullable configUpdate, 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)testRealtimeFetch {
  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([_configFetch[i] realtimeFetchConfigWithNoExpirationDuration:1
  1469. completionHandler:OCMOCK_ANY])
  1470. .andDo(nil);
  1471. OCMStub([_configRealtime[i] scheduleFetch:1 targetVersion:1]).andDo(nil);
  1472. [_configRealtime[i] fetchLatestConfig:3 targetVersion:1];
  1473. dispatch_after(
  1474. dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_checkCompletionTimeout * NSEC_PER_SEC)),
  1475. dispatch_get_main_queue(), ^{
  1476. OCMVerify([self->_configFetch[i] realtimeFetchConfigWithNoExpirationDuration:1
  1477. completionHandler:OCMOCK_ANY]);
  1478. [expectations[i] fulfill];
  1479. });
  1480. [self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
  1481. }
  1482. }
  1483. - (void)testAutofetch {
  1484. NSMutableArray<XCTestExpectation *> *expectations =
  1485. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  1486. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1487. expectations[i] = [self
  1488. expectationWithDescription:
  1489. [NSString stringWithFormat:@"Test Realtime Autofetch successfully - instance %d", i]];
  1490. OCMStub([_configRealtime[i] scheduleFetch:1 targetVersion:1]).andDo(nil);
  1491. [_configRealtime[i] autoFetch:1 targetVersion:1];
  1492. dispatch_after(
  1493. dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_checkCompletionTimeout * NSEC_PER_SEC)),
  1494. dispatch_get_main_queue(), ^{
  1495. OCMVerify([self->_configRealtime[i] scheduleFetch:1 targetVersion:1]);
  1496. [expectations[i] fulfill];
  1497. });
  1498. [self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
  1499. }
  1500. }
  1501. - (void)testAddOnConfigUpdateMethodSuccess {
  1502. NSMutableArray<XCTestExpectation *> *expectations =
  1503. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  1504. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1505. expectations[i] = [self
  1506. expectationWithDescription:
  1507. [NSString
  1508. stringWithFormat:@"Test public realtime method successfully - instance %d", i]];
  1509. OCMStub([_configRealtime[i] beginRealtimeStream]).andDo(nil);
  1510. id completion = ^void(FIRRemoteConfigUpdate *_Nullable configUpdate, NSError *_Nullable error) {
  1511. if (error != nil) {
  1512. NSLog(@"Callback");
  1513. }
  1514. };
  1515. [_configInstances[i] addOnConfigUpdateListener:completion];
  1516. dispatch_after(
  1517. dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_checkCompletionTimeout * NSEC_PER_SEC)),
  1518. dispatch_get_main_queue(), ^{
  1519. OCMVerify([self->_configRealtime[i] addConfigUpdateListener:completion]);
  1520. [expectations[i] fulfill];
  1521. });
  1522. [self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
  1523. }
  1524. }
  1525. - (void)testAddOnConfigUpdateMethodFail {
  1526. NSMutableArray<XCTestExpectation *> *expectations =
  1527. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  1528. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1529. expectations[i] = [self
  1530. expectationWithDescription:
  1531. [NSString stringWithFormat:@"Test public realtime method and fails - instance %d", i]];
  1532. id completion = nil;
  1533. [_configInstances[i] addOnConfigUpdateListener:completion];
  1534. dispatch_after(
  1535. dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_checkCompletionTimeout * NSEC_PER_SEC)),
  1536. dispatch_get_main_queue(), ^{
  1537. OCMVerify(never(), [self->_configRealtime[i] beginRealtimeStream]);
  1538. [expectations[i] fulfill];
  1539. });
  1540. [self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
  1541. }
  1542. }
  1543. - (void)testRealtimeDisabled {
  1544. NSMutableArray<XCTestExpectation *> *expectations =
  1545. [[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
  1546. for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
  1547. expectations[i] = [self
  1548. expectationWithDescription:
  1549. [NSString
  1550. stringWithFormat:@"Test isRealtimeDisabled flag and makes it true - instance %d",
  1551. i]];
  1552. OCMStub([_configRealtime[i] pauseRealtimeStream]).andDo(nil);
  1553. NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
  1554. [dictionary setValue:@"true" forKey:@"featureDisabled"];
  1555. [dictionary setValue:@"1" forKey:@"latestTemplateVersionNumber"];
  1556. [_configRealtime[i] evaluateStreamResponse:dictionary error:nil];
  1557. dispatch_after(
  1558. dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_checkCompletionTimeout * NSEC_PER_SEC)),
  1559. dispatch_get_main_queue(), ^{
  1560. OCMVerify([self->_configRealtime[i] pauseRealtimeStream]);
  1561. OCMVerify(never(), [self->_configRealtime[i] autoFetch:5 targetVersion:1]);
  1562. [expectations[i] fulfill];
  1563. });
  1564. [self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
  1565. }
  1566. }
  1567. - (void)testRealtimeStreamRequestBody {
  1568. NSError *error;
  1569. NSData *requestBody = [_configRealtime[0] createRequestBody];
  1570. NSData *uncompressedRequest = [NSData gul_dataByInflatingGzippedData:requestBody error:&error];
  1571. NSString *strData = [[NSString alloc] initWithData:uncompressedRequest
  1572. encoding:NSUTF8StringEncoding];
  1573. XCTAssertTrue([strData containsString:@"project:'correct_gcm_sender_id'"]);
  1574. XCTAssertTrue([strData containsString:@"namespace:'firebase'"]);
  1575. XCTAssertTrue([strData containsString:@"lastKnownVersionNumber:'0'"]);
  1576. XCTAssertTrue([strData containsString:@"appId:'1:123:ios:123abc'"]);
  1577. XCTAssertTrue([strData containsString:@"sdkVersion:"]);
  1578. XCTAssertTrue([strData containsString:@"appInstanceId:'iid'"]);
  1579. }
  1580. #pragma mark - Test Helpers
  1581. - (FIROptions *)firstAppOptions {
  1582. // TODO: Evaluate if we want to hardcode things here instead.
  1583. FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"1:123:ios:123abc"
  1584. GCMSenderID:@"correct_gcm_sender_id"];
  1585. options.APIKey = @"AIzaSy-ApiKeyWithValidFormat_0123456789";
  1586. options.projectID = @"abc-xyz-123";
  1587. return options;
  1588. }
  1589. - (FIROptions *)secondAppOptions {
  1590. NSBundle *bundle = [NSBundle bundleForClass:[self class]];
  1591. #if SWIFT_PACKAGE
  1592. bundle = SWIFTPM_MODULE_BUNDLE;
  1593. #endif
  1594. NSString *plistPath = [bundle pathForResource:@"SecondApp-GoogleService-Info" ofType:@"plist"];
  1595. FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:plistPath];
  1596. XCTAssertNotNil(options);
  1597. return options;
  1598. }
  1599. @end