RCNRemoteConfigTest.m 97 KB

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