RCNRemoteConfigTest.m 97 KB

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