GDTCORFlatFileStorageTest.m 65 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380
  1. /*
  2. * Copyright 2018 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 "GoogleDataTransport/GDTCORTests/Unit/GDTCORTestCase.h"
  17. #import "GoogleDataTransport/GDTCORLibrary/Private/GDTCORFlatFileStorage.h"
  18. #import "GoogleDataTransport/GDTCORLibrary/Private/GDTCORRegistrar_Private.h"
  19. #import "GoogleDataTransport/GDTCORLibrary/Internal/GDTCORPlatform.h"
  20. #import "GoogleDataTransport/GDTCORLibrary/Internal/GDTCORRegistrar.h"
  21. #import "GoogleDataTransport/GDTCORLibrary/Public/GoogleDataTransport/GDTCOREvent.h"
  22. #import "GoogleDataTransport/GDTCORTests/Unit/Helpers/GDTCORAssertHelper.h"
  23. #import "GoogleDataTransport/GDTCORTests/Unit/Helpers/GDTCORDataObjectTesterClasses.h"
  24. #import "GoogleDataTransport/GDTCORTests/Unit/Helpers/GDTCOREventGenerator.h"
  25. #import "GoogleDataTransport/GDTCORTests/Unit/Helpers/GDTCORTestUploader.h"
  26. #import "GoogleDataTransport/GDTCORTests/Common/Fakes/GDTCORUploadCoordinatorFake.h"
  27. #import "GoogleDataTransport/GDTCORTests/Common/Categories/GDTCORFlatFileStorage+Testing.h"
  28. #import "GoogleDataTransport/GDTCORTests/Common/Categories/GDTCORRegistrar+Testing.h"
  29. #import "GoogleDataTransport/GDTCORLibrary/Internal/GDTCORDirectorySizeTracker.h"
  30. /** A category that adds finding a random element to NSSet. NSSet's -anyObject isn't random. */
  31. @interface NSSet (GDTCORRandomElement)
  32. /** Returns a random element of the set.
  33. *
  34. * @return A random element of the set.
  35. */
  36. - (id)randomElement;
  37. @end
  38. @implementation NSSet (GDTCORRandomElement)
  39. - (id)randomElement {
  40. if (self.count) {
  41. NSArray *elements = [self allObjects];
  42. return elements[arc4random_uniform((uint32_t)self.count)];
  43. }
  44. return nil;
  45. }
  46. @end
  47. @interface GDTCORFlatFileStorageTest : GDTCORTestCase
  48. /** The uploader fake. */
  49. @property(nonatomic) GDTCORUploadCoordinatorFake *uploaderFake;
  50. @end
  51. @implementation GDTCORFlatFileStorageTest
  52. - (void)setUp {
  53. [super setUp];
  54. [[GDTCORRegistrar sharedInstance] reset];
  55. [[GDTCORFlatFileStorage sharedInstance] reset];
  56. self.uploaderFake = [[GDTCORUploadCoordinatorFake alloc] init];
  57. [GDTCORFlatFileStorage sharedInstance].uploadCoordinator = self.uploaderFake;
  58. [[GDTCORFlatFileStorage sharedInstance] reset];
  59. [[NSFileManager defaultManager] fileExistsAtPath:[GDTCORFlatFileStorage eventDataStoragePath]];
  60. }
  61. - (void)tearDown {
  62. dispatch_sync([GDTCORFlatFileStorage sharedInstance].storageQueue, ^{
  63. });
  64. // Destroy these objects before the next test begins.
  65. [GDTCORFlatFileStorage sharedInstance].uploadCoordinator =
  66. [GDTCORUploadCoordinator sharedInstance];
  67. self.uploaderFake = nil;
  68. [super tearDown];
  69. }
  70. /** Tests the singleton pattern. */
  71. - (void)testInit {
  72. XCTAssertEqual([GDTCORFlatFileStorage sharedInstance], [GDTCORFlatFileStorage sharedInstance]);
  73. }
  74. /** Tests storing an event. */
  75. - (void)testStoreEvent {
  76. GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
  77. GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:kGDTCORTargetTest];
  78. event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString"];
  79. event.clockSnapshot = [GDTCORClock snapshot];
  80. XCTestExpectation *expectation = [self expectationWithDescription:@"hasEvents completion called"];
  81. [storage hasEventsForTarget:kGDTCORTargetTest
  82. onComplete:^(BOOL hasEvents) {
  83. XCTAssertFalse(hasEvents);
  84. [expectation fulfill];
  85. }];
  86. [self waitForExpectations:@[ expectation ] timeout:10];
  87. XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
  88. XCTAssertNoThrow([storage storeEvent:event
  89. onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
  90. XCTAssertTrue(wasWritten);
  91. XCTAssertNotEqualObjects(event.eventID, @0);
  92. XCTAssertNil(error);
  93. [writtenExpectation fulfill];
  94. }]);
  95. [self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
  96. expectation = [self expectationWithDescription:@"hasEvents completion called"];
  97. [storage hasEventsForTarget:kGDTCORTargetTest
  98. onComplete:^(BOOL hasEvents) {
  99. XCTAssertTrue(hasEvents);
  100. [expectation fulfill];
  101. }];
  102. [self waitForExpectations:@[ expectation ] timeout:10];
  103. GDTCORStorageEventSelector *eventSelector =
  104. [GDTCORStorageEventSelector eventSelectorForTarget:kGDTCORTargetTest];
  105. expectation = [self expectationWithDescription:@"batch fetched"];
  106. [storage batchWithEventSelector:eventSelector
  107. batchExpiration:[NSDate dateWithTimeIntervalSinceNow:60]
  108. onComplete:^(NSNumber *_Nullable batchID,
  109. NSSet<GDTCOREvent *> *_Nullable events) {
  110. XCTAssertEqual(events.count, 1);
  111. XCTAssertEqualObjects(event.eventID, [events anyObject].eventID);
  112. [expectation fulfill];
  113. }];
  114. [self waitForExpectations:@[ expectation ] timeout:10];
  115. }
  116. /** Tests storing an event whose mappingID contains path components. */
  117. - (void)testStoreEventWithPathComponentsInMappingID {
  118. GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
  119. GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"this/messes/up/things"
  120. target:kGDTCORTargetTest];
  121. event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString"];
  122. event.clockSnapshot = [GDTCORClock snapshot];
  123. XCTestExpectation *expectation = [self expectationWithDescription:@"hasEvents completion called"];
  124. [storage hasEventsForTarget:kGDTCORTargetTest
  125. onComplete:^(BOOL hasEvents) {
  126. XCTAssertFalse(hasEvents);
  127. [expectation fulfill];
  128. }];
  129. [self waitForExpectations:@[ expectation ] timeout:10];
  130. XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
  131. XCTAssertNoThrow([storage storeEvent:event
  132. onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
  133. XCTAssertTrue(wasWritten);
  134. XCTAssertNotEqualObjects(event.eventID, @0);
  135. XCTAssertNil(error);
  136. [writtenExpectation fulfill];
  137. }]);
  138. [self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
  139. expectation = [self expectationWithDescription:@"hasEvents completion called"];
  140. [storage hasEventsForTarget:kGDTCORTargetTest
  141. onComplete:^(BOOL hasEvents) {
  142. XCTAssertTrue(hasEvents);
  143. [expectation fulfill];
  144. }];
  145. [self waitForExpectations:@[ expectation ] timeout:10];
  146. GDTCORStorageEventSelector *eventSelector =
  147. [GDTCORStorageEventSelector eventSelectorForTarget:kGDTCORTargetTest];
  148. expectation = [self expectationWithDescription:@"batch fetched"];
  149. [storage batchWithEventSelector:eventSelector
  150. batchExpiration:[NSDate dateWithTimeIntervalSinceNow:60]
  151. onComplete:^(NSNumber *_Nullable batchID,
  152. NSSet<GDTCOREvent *> *_Nullable events) {
  153. XCTAssertEqual(events.count, 1);
  154. XCTAssertEqualObjects(event.eventID, [events anyObject].eventID);
  155. [expectation fulfill];
  156. }];
  157. [self waitForExpectations:@[ expectation ] timeout:10];
  158. }
  159. /** Tests storing a few different events. */
  160. - (void)testStoreMultipleEvents {
  161. GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
  162. GDTCOREvent *event1 = [[GDTCOREvent alloc] initWithMappingID:@"404" target:kGDTCORTargetTest];
  163. event1.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString1"];
  164. XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
  165. XCTAssertNoThrow([storage storeEvent:event1
  166. onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
  167. XCTAssertNotEqualObjects(event1.eventID, @0);
  168. XCTAssertNil(error);
  169. [writtenExpectation fulfill];
  170. }]);
  171. [self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
  172. GDTCOREvent *event2 = [[GDTCOREvent alloc] initWithMappingID:@"100" target:kGDTCORTargetTest];
  173. event2.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString2"];
  174. writtenExpectation = [self expectationWithDescription:@"event written"];
  175. XCTAssertNoThrow([storage storeEvent:event2
  176. onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
  177. XCTAssertNotEqualObjects(event2.eventID, @0);
  178. XCTAssertNil(error);
  179. [writtenExpectation fulfill];
  180. }]);
  181. [self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
  182. GDTCOREvent *event3 = [[GDTCOREvent alloc] initWithMappingID:@"404" target:kGDTCORTargetTest];
  183. event3.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString3"];
  184. writtenExpectation = [self expectationWithDescription:@"event written"];
  185. XCTAssertNoThrow([storage storeEvent:event3
  186. onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
  187. XCTAssertNotEqualObjects(event3.eventID, @0);
  188. XCTAssertNil(error);
  189. [writtenExpectation fulfill];
  190. }]);
  191. [self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
  192. XCTestExpectation *expectation = [self expectationWithDescription:@"batch created"];
  193. GDTCORStorageEventSelector *eventSelector =
  194. [GDTCORStorageEventSelector eventSelectorForTarget:kGDTCORTargetTest];
  195. [storage batchWithEventSelector:eventSelector
  196. batchExpiration:[NSDate dateWithTimeIntervalSinceNow:60]
  197. onComplete:^(NSNumber *_Nullable batchID,
  198. NSSet<GDTCOREvent *> *_Nullable events) {
  199. XCTAssertEqual(events.count, 3);
  200. [expectation fulfill];
  201. }];
  202. [self waitForExpectations:@[ expectation ] timeout:10];
  203. }
  204. /** Tests sending a fast priority event causes an upload attempt. */
  205. - (void)testQoSTierFast {
  206. GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
  207. GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:kGDTCORTargetTest];
  208. event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString"];
  209. event.qosTier = GDTCOREventQoSFast;
  210. event.clockSnapshot = [GDTCORClock snapshot];
  211. XCTAssertFalse(self.uploaderFake.forceUploadCalled);
  212. XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
  213. XCTAssertNoThrow([storage storeEvent:event
  214. onComplete:^(BOOL wasWritten, NSError *error) {
  215. XCTAssertNotEqualObjects(event.eventID, @0);
  216. XCTAssertNil(error);
  217. [writtenExpectation fulfill];
  218. }]);
  219. [self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
  220. dispatch_sync(storage.storageQueue, ^{
  221. XCTAssertTrue(self.uploaderFake.forceUploadCalled);
  222. });
  223. }
  224. /** Fuzz tests the storing of events at the same time as a terminate lifecycle notification. This
  225. * test can fail if there's simultaneous access to ivars of GDTCORFlatFileStorage with one access
  226. * being off the storage's queue. The terminate lifecycle event should operate on and flush the
  227. * queue.
  228. */
  229. - (void)testStoringEventsDuringTerminate {
  230. BOOL originalValueOfContinueAfterFailure = self.continueAfterFailure;
  231. self.continueAfterFailure = NO;
  232. int numberOfIterations = 1000;
  233. GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
  234. for (int i = 0; i < numberOfIterations; i++) {
  235. NSString *testString = [NSString stringWithFormat:@"testString %d", i];
  236. GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:kGDTCORTargetTest];
  237. event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:testString];
  238. event.clockSnapshot = [GDTCORClock snapshot];
  239. XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
  240. XCTAssertNoThrow([storage storeEvent:event
  241. onComplete:^(BOOL wasWritten, NSError *error) {
  242. XCTAssertNotEqualObjects(event.eventID, @0);
  243. [writtenExpectation fulfill];
  244. }]);
  245. [self waitForExpectationsWithTimeout:10 handler:nil];
  246. if (i % 5 == 0) {
  247. GDTCORStorageEventSelector *eventSelector =
  248. [GDTCORStorageEventSelector eventSelectorForTarget:kGDTCORTargetTest];
  249. [storage batchWithEventSelector:eventSelector
  250. batchExpiration:[NSDate dateWithTimeIntervalSinceNow:60]
  251. onComplete:^(NSNumber *_Nullable batchID,
  252. NSSet<GDTCOREvent *> *_Nullable events) {
  253. [storage removeBatchWithID:batchID deleteEvents:YES onComplete:nil];
  254. }];
  255. }
  256. [NSNotificationCenter.defaultCenter
  257. postNotificationName:kGDTCORApplicationWillTerminateNotification
  258. object:nil];
  259. }
  260. self.continueAfterFailure = originalValueOfContinueAfterFailure;
  261. }
  262. - (void)testSaveAndLoadLibraryData {
  263. __weak NSData *weakData;
  264. NSString *dataKey = NSStringFromSelector(_cmd);
  265. @autoreleasepool {
  266. NSData *data = [@"test data" dataUsingEncoding:NSUTF8StringEncoding];
  267. weakData = data;
  268. XCTestExpectation *expectation = [self expectationWithDescription:@"storage completion called"];
  269. [[GDTCORFlatFileStorage sharedInstance] storeLibraryData:data
  270. forKey:dataKey
  271. onComplete:^(NSError *_Nullable error) {
  272. XCTAssertNil(error);
  273. [expectation fulfill];
  274. }];
  275. [self waitForExpectations:@[ expectation ] timeout:10.0];
  276. }
  277. XCTAssertNil(weakData);
  278. XCTestExpectation *expectation = [self expectationWithDescription:@"retrieval completion called"];
  279. [[GDTCORFlatFileStorage sharedInstance]
  280. libraryDataForKey:dataKey
  281. onFetchComplete:^(NSData *_Nullable data, NSError *_Nullable error) {
  282. [expectation fulfill];
  283. XCTAssertNil(error);
  284. XCTAssertEqualObjects(@"test data", [[NSString alloc] initWithData:data
  285. encoding:NSUTF8StringEncoding]);
  286. }
  287. setNewValue:nil];
  288. [self waitForExpectations:@[ expectation ] timeout:10.0];
  289. }
  290. - (void)testSavingNilLibraryData {
  291. XCTestExpectation *expectation = [self expectationWithDescription:@"storage completion called"];
  292. [[GDTCORFlatFileStorage sharedInstance] storeLibraryData:[NSData data]
  293. forKey:@"test data key"
  294. onComplete:^(NSError *_Nullable error) {
  295. XCTAssertNotNil(error);
  296. [expectation fulfill];
  297. }];
  298. [self waitForExpectations:@[ expectation ] timeout:10.0];
  299. }
  300. - (void)testSaveAndRemoveLibraryData {
  301. NSString *dataKey = NSStringFromSelector(_cmd);
  302. NSData *data = [@"test data" dataUsingEncoding:NSUTF8StringEncoding];
  303. XCTestExpectation *expectation = [self expectationWithDescription:@"storage completion called"];
  304. [[GDTCORFlatFileStorage sharedInstance] storeLibraryData:data
  305. forKey:dataKey
  306. onComplete:^(NSError *_Nullable error) {
  307. XCTAssertNil(error);
  308. [expectation fulfill];
  309. }];
  310. [self waitForExpectations:@[ expectation ] timeout:10.0];
  311. expectation = [self expectationWithDescription:@"retrieval completion called"];
  312. [[GDTCORFlatFileStorage sharedInstance] libraryDataForKey:dataKey
  313. onFetchComplete:^(NSData *_Nullable data, NSError *_Nullable error) {
  314. XCTAssertNil(error);
  315. XCTAssertEqualObjects(@"test data", [[NSString alloc] initWithData:data
  316. encoding:NSUTF8StringEncoding]);
  317. [expectation fulfill];
  318. }
  319. setNewValue:^NSData *_Nullable {
  320. return nil;
  321. }];
  322. [self waitForExpectations:@[ expectation ] timeout:10.0];
  323. expectation = [self expectationWithDescription:@"removal completion called"];
  324. [[GDTCORFlatFileStorage sharedInstance] removeLibraryDataForKey:dataKey
  325. onComplete:^(NSError *error) {
  326. [expectation fulfill];
  327. XCTAssertNil(error);
  328. }];
  329. [self waitForExpectations:@[ expectation ] timeout:10.0];
  330. expectation = [self expectationWithDescription:@"retrieval completion called"];
  331. [[GDTCORFlatFileStorage sharedInstance]
  332. libraryDataForKey:dataKey
  333. onFetchComplete:^(NSData *_Nullable data, NSError *_Nullable error) {
  334. XCTAssertNotNil(error);
  335. XCTAssertNil(data);
  336. [expectation fulfill];
  337. }
  338. setNewValue:nil];
  339. [self waitForExpectations:@[ expectation ] timeout:10.0];
  340. }
  341. /** Tests -pathForTarget:qosTier:mappingID: searching by target. */
  342. - (void)testSearchingPathsByTarget {
  343. GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
  344. NSSet<GDTCOREvent *> *generatedEvents = [self generateEventsForStorageTesting];
  345. NSSet<GDTCOREvent *> *expectedEvents = [generatedEvents
  346. filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
  347. GDTCOREvent *_Nullable event,
  348. NSDictionary<NSString *, id> *_Nullable bindings) {
  349. return event.target == kGDTCORTargetTest;
  350. }]];
  351. XCTestExpectation *expectation = [self expectationWithDescription:@"paths found"];
  352. [storage pathsForTarget:kGDTCORTargetTest
  353. eventIDs:nil
  354. qosTiers:nil
  355. mappingIDs:nil
  356. onComplete:^(NSSet<NSString *> *paths) {
  357. XCTAssertEqual(paths.count, expectedEvents.count);
  358. [expectation fulfill];
  359. }];
  360. [self waitForExpectations:@[ expectation ] timeout:1.0];
  361. }
  362. /** Tests -pathForTarget:qosTier:mappingID: searching by eventID. */
  363. - (void)testSearchingPathWithEventID {
  364. GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
  365. NSSet<GDTCOREvent *> *generatedEvents = [self generateEventsForStorageTesting];
  366. GDTCOREvent *anyEvent = [generatedEvents randomElement];
  367. NSSet<GDTCOREvent *> *expectedEvents = [generatedEvents
  368. filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
  369. GDTCOREvent *_Nullable event,
  370. NSDictionary<NSString *, id> *_Nullable bindings) {
  371. return anyEvent.target == event.target && [event.eventID isEqualToString:anyEvent.eventID];
  372. }]];
  373. XCTestExpectation *expectation = [self expectationWithDescription:@"paths found"];
  374. [storage pathsForTarget:anyEvent.target
  375. eventIDs:[NSSet setWithObject:anyEvent.eventID]
  376. qosTiers:nil
  377. mappingIDs:nil
  378. onComplete:^(NSSet<NSString *> *paths) {
  379. XCTAssertEqual(paths.count, expectedEvents.count);
  380. [expectation fulfill];
  381. }];
  382. [self waitForExpectations:@[ expectation ] timeout:1.0];
  383. GDTCOREvent *anotherEvent;
  384. do {
  385. anotherEvent = [generatedEvents randomElement];
  386. } while (anotherEvent == anyEvent || anotherEvent.target != anyEvent.target);
  387. expectedEvents = [generatedEvents
  388. filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
  389. GDTCOREvent *_Nullable event,
  390. NSDictionary<NSString *, id> *_Nullable bindings) {
  391. return (anyEvent.target == event.target &&
  392. [event.eventID isEqualToString:anyEvent.eventID]) ||
  393. (anotherEvent.target == event.target &&
  394. [event.eventID isEqualToString:anotherEvent.eventID]);
  395. }]];
  396. expectation = [self expectationWithDescription:@"paths found"];
  397. [storage pathsForTarget:anyEvent.target
  398. eventIDs:[NSSet setWithObjects:anyEvent.eventID, anotherEvent.eventID, nil]
  399. qosTiers:nil
  400. mappingIDs:nil
  401. onComplete:^(NSSet<NSString *> *paths) {
  402. XCTAssertEqual(paths.count, expectedEvents.count);
  403. [expectation fulfill];
  404. }];
  405. [self waitForExpectations:@[ expectation ] timeout:1.0];
  406. }
  407. /** Tests -pathForTarget:qosTier:mappingID: searching by qosTier. */
  408. - (void)testSearchingPathWithQoSTier {
  409. GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
  410. NSSet<GDTCOREvent *> *generatedEvents = [self generateEventsForStorageTesting];
  411. GDTCOREvent *anyEvent = [generatedEvents randomElement];
  412. NSSet<GDTCOREvent *> *expectedEvents = [generatedEvents
  413. filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
  414. GDTCOREvent *_Nullable event,
  415. NSDictionary<NSString *, id> *_Nullable bindings) {
  416. return event.target == anyEvent.target && event.qosTier == anyEvent.qosTier;
  417. }]];
  418. XCTestExpectation *expectation = [self expectationWithDescription:@"paths found"];
  419. [storage pathsForTarget:anyEvent.target
  420. eventIDs:nil
  421. qosTiers:[NSSet setWithObject:@(anyEvent.qosTier)]
  422. mappingIDs:nil
  423. onComplete:^(NSSet<NSString *> *paths) {
  424. XCTAssertEqual(paths.count, expectedEvents.count);
  425. [expectation fulfill];
  426. }];
  427. [self waitForExpectations:@[ expectation ] timeout:1.0];
  428. }
  429. /** Tests -pathForTarget:qosTier:mappingID: searching by mappingID. */
  430. - (void)testSearchingPathWithMappingID {
  431. GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
  432. NSSet<GDTCOREvent *> *generatedEvents = [self generateEventsForStorageTesting];
  433. GDTCOREvent *anyEvent = [generatedEvents randomElement];
  434. NSSet<GDTCOREvent *> *expectedEvents = [generatedEvents
  435. filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
  436. GDTCOREvent *_Nullable event,
  437. NSDictionary<NSString *, id> *_Nullable bindings) {
  438. return event.target == anyEvent.target &&
  439. [event.mappingID isEqualToString:anyEvent.mappingID];
  440. }]];
  441. XCTestExpectation *expectation = [self expectationWithDescription:@"paths found"];
  442. [storage pathsForTarget:anyEvent.target
  443. eventIDs:nil
  444. qosTiers:nil
  445. mappingIDs:[NSSet setWithObject:anyEvent.mappingID]
  446. onComplete:^(NSSet<NSString *> *paths) {
  447. XCTAssertEqual(paths.count, expectedEvents.count);
  448. [expectation fulfill];
  449. }];
  450. [self waitForExpectations:@[ expectation ] timeout:1.0];
  451. }
  452. /** Tests -pathForTarget:qosTier:mappingID: searching by mappingID that contains path components. */
  453. - (void)testSearchingPathWithMappingIDThatHasPathComponents {
  454. GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
  455. GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"this/messes/up/things"
  456. target:kGDTCORTargetTest];
  457. event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString"];
  458. event.clockSnapshot = [GDTCORClock snapshot];
  459. [storage storeEvent:event onComplete:nil];
  460. NSSet<GDTCOREvent *> *expectedEvents = [NSSet setWithObject:event];
  461. XCTestExpectation *expectation = [self expectationWithDescription:@"paths found"];
  462. [storage pathsForTarget:event.target
  463. eventIDs:nil
  464. qosTiers:nil
  465. mappingIDs:[NSSet setWithObject:event.mappingID]
  466. onComplete:^(NSSet<NSString *> *paths) {
  467. XCTAssertEqual(paths.count, expectedEvents.count);
  468. [expectation fulfill];
  469. }];
  470. [self waitForExpectations:@[ expectation ] timeout:1.0];
  471. }
  472. /** Tests -pathForTarget:qosTier:mappingID: searching by eventID and qosTier. */
  473. - (void)testSearchingPathWithEventIDAndQoSTier {
  474. GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
  475. NSSet<GDTCOREvent *> *generatedEvents = [self generateEventsForStorageTesting];
  476. GDTCOREvent *anyEvent = [generatedEvents randomElement];
  477. NSSet<GDTCOREvent *> *expectedEvents = [generatedEvents
  478. filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
  479. GDTCOREvent *_Nullable event,
  480. NSDictionary<NSString *, id> *_Nullable bindings) {
  481. return event.target == anyEvent.target &&
  482. [event.eventID isEqualToString:anyEvent.eventID] &&
  483. event.qosTier == anyEvent.qosTier;
  484. }]];
  485. XCTestExpectation *expectation = [self expectationWithDescription:@"paths found"];
  486. [storage pathsForTarget:anyEvent.target
  487. eventIDs:[NSSet setWithObject:anyEvent.eventID]
  488. qosTiers:[NSSet setWithObject:@(anyEvent.qosTier)]
  489. mappingIDs:nil
  490. onComplete:^(NSSet<NSString *> *paths) {
  491. XCTAssertEqual(paths.count, expectedEvents.count);
  492. [expectation fulfill];
  493. }];
  494. [self waitForExpectations:@[ expectation ] timeout:1.0];
  495. }
  496. /** Tests -pathForTarget:qosTier:mappingID: searching by eventID and qosTier without results. */
  497. - (void)testSearchingPathWithEventIDAndQoSTierNoResults {
  498. GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
  499. NSSet<GDTCOREvent *> *generatedEvents = [self generateEventsForStorageTesting];
  500. XCTAssertGreaterThan(generatedEvents.count, 0);
  501. XCTestExpectation *expectation = [self expectationWithDescription:@"paths found"];
  502. [storage pathsForTarget:kGDTCORTargetFLL
  503. eventIDs:[NSSet setWithObject:@"made up"]
  504. qosTiers:nil
  505. mappingIDs:nil
  506. onComplete:^(NSSet<NSString *> *paths) {
  507. XCTAssertEqual(paths.count, 0);
  508. [expectation fulfill];
  509. }];
  510. [self waitForExpectations:@[ expectation ] timeout:1.0];
  511. }
  512. /** Tests -pathForTarget:qosTier:mappingID: searching by qosTier and mappingID. */
  513. - (void)testSearchingPathWithQoSTierAndMappingID {
  514. GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
  515. NSSet<GDTCOREvent *> *generatedEvents = [self generateEventsForStorageTesting];
  516. GDTCOREvent *anyEvent = [generatedEvents randomElement];
  517. NSSet<GDTCOREvent *> *expectedEvents = [generatedEvents
  518. filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
  519. GDTCOREvent *_Nullable event,
  520. NSDictionary<NSString *, id> *_Nullable bindings) {
  521. return event.target == anyEvent.target && event.qosTier == anyEvent.qosTier &&
  522. [event.mappingID isEqualToString:anyEvent.mappingID];
  523. }]];
  524. XCTestExpectation *expectation = [self expectationWithDescription:@"paths found"];
  525. [storage pathsForTarget:anyEvent.target
  526. eventIDs:nil
  527. qosTiers:[NSSet setWithObject:@(anyEvent.qosTier)]
  528. mappingIDs:[NSSet setWithObject:anyEvent.mappingID]
  529. onComplete:^(NSSet<NSString *> *paths) {
  530. XCTAssertEqual(paths.count, expectedEvents.count);
  531. [expectation fulfill];
  532. }];
  533. [self waitForExpectations:@[ expectation ] timeout:1.0];
  534. }
  535. /** Tests hasEventsForTarget: returns YES when events are stored and NO otherwise. */
  536. - (void)testHasEventsForTarget {
  537. XCTestExpectation *expectation = [self expectationWithDescription:@"hasEvent completion called"];
  538. [[GDTCORFlatFileStorage sharedInstance] hasEventsForTarget:kGDTCORTargetTest
  539. onComplete:^(BOOL hasEvents) {
  540. XCTAssertFalse(hasEvents);
  541. [expectation fulfill];
  542. }];
  543. [self waitForExpectations:@[ expectation ] timeout:10];
  544. GDTCOREvent *event = [GDTCOREventGenerator generateEventForTarget:kGDTCORTargetTest
  545. qosTier:nil
  546. mappingID:nil];
  547. [[GDTCORFlatFileStorage sharedInstance] storeEvent:event onComplete:nil];
  548. expectation = [self expectationWithDescription:@"hasEvent completion called"];
  549. [[GDTCORFlatFileStorage sharedInstance] hasEventsForTarget:kGDTCORTargetTest
  550. onComplete:^(BOOL hasEvents) {
  551. XCTAssertTrue(hasEvents);
  552. [expectation fulfill];
  553. }];
  554. [self waitForExpectations:@[ expectation ] timeout:10];
  555. }
  556. /** Tests generating the next batchID. */
  557. - (void)testNextBatchID {
  558. BOOL originalContinueAfterFailure = self.continueAfterFailure;
  559. self.continueAfterFailure = NO;
  560. NSNumber *expectedBatchID = @0;
  561. XCTestExpectation *expectation = [self expectationWithDescription:@"nextBatchID completion"];
  562. [[GDTCORFlatFileStorage sharedInstance] nextBatchID:^(NSNumber *_Nonnull batchID) {
  563. XCTAssertNotNil(batchID);
  564. XCTAssertEqualObjects(batchID, expectedBatchID);
  565. [expectation fulfill];
  566. }];
  567. [self waitForExpectations:@[ expectation ] timeout:10.0];
  568. expectedBatchID = @1;
  569. expectation = [self expectationWithDescription:@"nextBatchID completion"];
  570. [[GDTCORFlatFileStorage sharedInstance] nextBatchID:^(NSNumber *_Nonnull batchID) {
  571. XCTAssertNotNil(batchID);
  572. XCTAssertEqualObjects(batchID, expectedBatchID);
  573. [expectation fulfill];
  574. }];
  575. [self waitForExpectations:@[ expectation ] timeout:10.0];
  576. for (int i = 0; i < 1000; i++) {
  577. XCTestExpectation *expectation = [self expectationWithDescription:@"nextBatchID completion"];
  578. [[GDTCORFlatFileStorage sharedInstance] nextBatchID:^(NSNumber *_Nonnull batchID) {
  579. NSNumber *expectedBatchID = @(i + 2); // 2 because of the 2 we generated.
  580. XCTAssertEqualObjects(batchID, expectedBatchID);
  581. [expectation fulfill];
  582. }];
  583. [self waitForExpectations:@[ expectation ] timeout:10.0];
  584. }
  585. self.continueAfterFailure = originalContinueAfterFailure;
  586. }
  587. /** Tests the thread safety of nextBatchID by making a lot of simultaneous calls to it. */
  588. - (void)testNextBatchIDThreadSafety {
  589. NSUInteger numberOfIterations = 1000;
  590. NSUInteger expectedBatchID = 2 * numberOfIterations - 1;
  591. __block NSNumber *batchID;
  592. NSMutableArray *expectations = [[NSMutableArray alloc] init];
  593. for (NSUInteger i = 0; i < numberOfIterations; i++) {
  594. XCTestExpectation *firstExpectation = [self expectationWithDescription:@"first block run"];
  595. [expectations addObject:firstExpectation];
  596. dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
  597. [[GDTCORFlatFileStorage sharedInstance] nextBatchID:^(NSNumber *_Nonnull newBatchID) {
  598. batchID = newBatchID;
  599. [firstExpectation fulfill];
  600. }];
  601. });
  602. XCTestExpectation *secondExpectation = [self expectationWithDescription:@"first block run"];
  603. [expectations addObject:secondExpectation];
  604. dispatch_async(dispatch_get_global_queue(QOS_CLASS_UNSPECIFIED, 0), ^{
  605. [[GDTCORFlatFileStorage sharedInstance] nextBatchID:^(NSNumber *_Nonnull newBatchID) {
  606. batchID = newBatchID;
  607. [secondExpectation fulfill];
  608. }];
  609. });
  610. }
  611. [self waitForExpectations:expectations timeout:30];
  612. XCTAssertEqualObjects(batchID, @(expectedBatchID));
  613. }
  614. /** Tests basic batch creation and removal. */
  615. - (void)testBatchIDWithTarget {
  616. GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
  617. NSSet<GDTCOREvent *> *generatedEvents = [self generateEventsForStorageTesting];
  618. NSSet<GDTCOREvent *> *testTargetEvents = [generatedEvents
  619. filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
  620. GDTCOREvent *_Nullable event,
  621. NSDictionary<NSString *, id> *_Nullable bindings) {
  622. return event.target == kGDTCORTargetTest;
  623. }]];
  624. XCTAssertNotNil(testTargetEvents);
  625. XCTestExpectation *expectation = [self expectationWithDescription:@"batch callback invoked"];
  626. GDTCORStorageEventSelector *eventSelector =
  627. [GDTCORStorageEventSelector eventSelectorForTarget:kGDTCORTargetTest];
  628. __block NSNumber *batchID;
  629. [storage batchWithEventSelector:eventSelector
  630. batchExpiration:[NSDate dateWithTimeIntervalSinceNow:600]
  631. onComplete:^(NSNumber *_Nullable newBatchID,
  632. NSSet<GDTCOREvent *> *_Nullable events) {
  633. batchID = newBatchID;
  634. XCTAssertNotNil(batchID);
  635. XCTAssertEqual(events.count, testTargetEvents.count);
  636. [expectation fulfill];
  637. }];
  638. [self waitForExpectations:@[ expectation ] timeout:10];
  639. XCTAssertNotNil(batchID);
  640. expectation = [self expectationWithDescription:@"pathsForTarget completion invoked"];
  641. [storage pathsForTarget:kGDTCORTargetTest
  642. eventIDs:nil
  643. qosTiers:nil
  644. mappingIDs:nil
  645. onComplete:^(NSSet<NSString *> *_Nonnull paths) {
  646. XCTAssertEqual(paths.count, 0);
  647. [expectation fulfill];
  648. }];
  649. [self waitForExpectations:@[ expectation ] timeout:10];
  650. }
  651. - (void)testBatchIDsForTarget {
  652. __auto_type expectedBatch = [self generateAndBatchEvents];
  653. XCTestExpectation *batchIDsExpectation = [self expectationWithDescription:@"batchIDsExpectation"];
  654. [[GDTCORFlatFileStorage sharedInstance]
  655. batchIDsForTarget:kGDTCORTargetTest
  656. onComplete:^(NSSet<NSNumber *> *_Nullable batchIDs) {
  657. [batchIDsExpectation fulfill];
  658. XCTAssertEqual(batchIDs.count, 1);
  659. XCTAssertEqualObjects([expectedBatch.allKeys firstObject], [batchIDs anyObject]);
  660. }];
  661. [self waitForExpectations:@[ batchIDsExpectation ] timeout:5];
  662. }
  663. #pragma mark - Expiration tests
  664. /** Tests events expiring at a given time. */
  665. - (void)testCheckForExpirations_WhenEventsExpire {
  666. NSTimeInterval delay = 10.0;
  667. XCTestExpectation *expectation = [self expectationWithDescription:@"hasEvent completion called"];
  668. [[GDTCORFlatFileStorage sharedInstance] hasEventsForTarget:kGDTCORTargetTest
  669. onComplete:^(BOOL hasEvents) {
  670. XCTAssertFalse(hasEvents);
  671. [expectation fulfill];
  672. }];
  673. [self waitForExpectations:@[ expectation ] timeout:10];
  674. GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"testing" target:kGDTCORTargetTest];
  675. event.expirationDate = [NSDate dateWithTimeIntervalSinceNow:delay];
  676. event.clockSnapshot = [GDTCORClock snapshot];
  677. event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString"];
  678. expectation = [self expectationWithDescription:@"storeEvent completion"];
  679. [[GDTCORFlatFileStorage sharedInstance] storeEvent:event
  680. onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
  681. XCTAssertTrue(wasWritten);
  682. XCTAssertNil(error);
  683. [expectation fulfill];
  684. }];
  685. [self waitForExpectations:@[ expectation ] timeout:5.0];
  686. expectation = [self expectationWithDescription:@"hasEvent completion called"];
  687. [[GDTCORFlatFileStorage sharedInstance] hasEventsForTarget:kGDTCORTargetTest
  688. onComplete:^(BOOL hasEvents) {
  689. XCTAssertTrue(hasEvents);
  690. [expectation fulfill];
  691. }];
  692. [self waitForExpectations:@[ expectation ] timeout:10];
  693. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:delay]];
  694. [[GDTCORFlatFileStorage sharedInstance] checkForExpirations];
  695. expectation = [self expectationWithDescription:@"hasEvent completion called"];
  696. [[GDTCORFlatFileStorage sharedInstance] hasEventsForTarget:kGDTCORTargetTest
  697. onComplete:^(BOOL hasEvents) {
  698. XCTAssertFalse(hasEvents);
  699. [expectation fulfill];
  700. }];
  701. [self waitForExpectations:@[ expectation ] timeout:10];
  702. }
  703. - (void)testCheckForExpirations_WhenBatchWithNotExpiredEventsExpires {
  704. NSTimeInterval batchExpiresIn = 0.5;
  705. // 0.1. Generate and batch events
  706. __auto_type generatedBatch = [self generateAndBatchEventsExpiringIn:1000
  707. batchExpiringIn:batchExpiresIn];
  708. NSNumber *generatedBatchID = [[generatedBatch allKeys] firstObject];
  709. NSSet<GDTCOREvent *> *generatedEvents = generatedBatch[generatedBatchID];
  710. // 0.2. Wait for batch expiration.
  711. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:batchExpiresIn]];
  712. // 1. Check for expiration.
  713. [[GDTCORFlatFileStorage sharedInstance] checkForExpirations];
  714. // 2. Check events.
  715. // 2.1. Expect no batches left.
  716. XCTestExpectation *getBatchesExpectation =
  717. [self expectationWithDescription:@"getBatchesExpectation"];
  718. [[GDTCORFlatFileStorage sharedInstance]
  719. batchIDsForTarget:kGDTCORTargetTest
  720. onComplete:^(NSSet<NSNumber *> *_Nullable batchIDs) {
  721. [getBatchesExpectation fulfill];
  722. XCTAssertEqual(batchIDs.count, 0);
  723. }];
  724. // 2.2. Expect the events back in the main storage.
  725. XCTestExpectation *getEventsExpectation =
  726. [self expectationWithDescription:@"getEventsExpectation"];
  727. [[GDTCORFlatFileStorage sharedInstance]
  728. batchWithEventSelector:[GDTCORStorageEventSelector eventSelectorForTarget:kGDTCORTargetTest]
  729. batchExpiration:[NSDate dateWithTimeIntervalSinceNow:1000]
  730. onComplete:^(NSNumber *_Nullable newBatchID,
  731. NSSet<GDTCOREvent *> *_Nullable batchEvents) {
  732. [getEventsExpectation fulfill];
  733. XCTAssertNotNil(newBatchID);
  734. NSSet<NSString *> *batchEventsIDs = [batchEvents valueForKeyPath:@"eventID"];
  735. NSSet<NSString *> *generatedEventsIDs =
  736. [generatedEvents valueForKeyPath:@"eventID"];
  737. XCTAssertEqualObjects(batchEventsIDs, generatedEventsIDs);
  738. }];
  739. [self waitForExpectations:@[ getBatchesExpectation, getEventsExpectation ] timeout:0.5];
  740. }
  741. - (void)testCheckForExpirations_WhenBatchWithExpiredEventsExpires {
  742. NSTimeInterval batchExpiresIn = 0.5;
  743. NSTimeInterval eventsExpireIn = 0.5;
  744. // 0.1. Generate and batch events
  745. __unused __auto_type generatedBatch = [self generateAndBatchEventsExpiringIn:eventsExpireIn
  746. batchExpiringIn:batchExpiresIn];
  747. // 0.2. Wait for batch expiration.
  748. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:batchExpiresIn]];
  749. // 1. Check for expiration.
  750. [[GDTCORFlatFileStorage sharedInstance] checkForExpirations];
  751. // 2. Check events.
  752. // 2.1. Expect no batches left.
  753. XCTestExpectation *getBatchesExpectation =
  754. [self expectationWithDescription:@"getBatchesExpectation"];
  755. [[GDTCORFlatFileStorage sharedInstance]
  756. batchIDsForTarget:kGDTCORTargetTest
  757. onComplete:^(NSSet<NSNumber *> *_Nullable batchIDs) {
  758. [getBatchesExpectation fulfill];
  759. XCTAssertEqual(batchIDs.count, 0);
  760. }];
  761. // 2.2. Expect events to be deleted.
  762. XCTestExpectation *getEventsExpectation =
  763. [self expectationWithDescription:@"getEventsExpectation"];
  764. [[GDTCORFlatFileStorage sharedInstance]
  765. batchWithEventSelector:[GDTCORStorageEventSelector eventSelectorForTarget:kGDTCORTargetTest]
  766. batchExpiration:[NSDate dateWithTimeIntervalSinceNow:1000]
  767. onComplete:^(NSNumber *_Nullable newBatchID,
  768. NSSet<GDTCOREvent *> *_Nullable batchEvents) {
  769. [getEventsExpectation fulfill];
  770. XCTAssertNil(newBatchID);
  771. XCTAssertEqual(batchEvents.count, 0);
  772. }];
  773. [self waitForExpectations:@[ getBatchesExpectation, getEventsExpectation ] timeout:0.5];
  774. }
  775. #pragma mark - Remove Batch tests
  776. - (void)testRemoveBatchWithIDWithNoDeletingEvents {
  777. GDTCORFlatFileStorage *storage = [[GDTCORFlatFileStorage alloc] init];
  778. // 0. Prepare a batch to remove.
  779. __auto_type generatedBatch = [self generateAndBatchEvents];
  780. NSNumber *batchIDToRemove = [generatedBatch.allKeys firstObject];
  781. NSSet<GDTCOREvent *> *generatedEvents = generatedBatch[batchIDToRemove];
  782. // 2. Remove batch.
  783. XCTestExpectation *batchRemovedExpectation =
  784. [self expectationWithDescription:@"batchRemovedExpectation"];
  785. [storage removeBatchWithID:batchIDToRemove
  786. deleteEvents:NO
  787. onComplete:^{
  788. [batchRemovedExpectation fulfill];
  789. }];
  790. [self waitForExpectations:@[ batchRemovedExpectation ] timeout:0.5];
  791. // 3. Validate no batches.
  792. [self assertBatchIDs:nil inStorage:storage];
  793. // 4. Validate events.
  794. GDTCORStorageEventSelector *testEventsSelector =
  795. [[GDTCORStorageEventSelector alloc] initWithTarget:kGDTCORTargetTest
  796. eventIDs:nil
  797. mappingIDs:nil
  798. qosTiers:nil];
  799. XCTestExpectation *eventsBatchedExpectation2 =
  800. [self expectationWithDescription:@"eventsBatchedExpectation1"];
  801. [storage batchWithEventSelector:testEventsSelector
  802. batchExpiration:[NSDate distantFuture]
  803. onComplete:^(NSNumber *_Nullable newBatchID,
  804. NSSet<GDTCOREvent *> *_Nullable batchEvents) {
  805. [eventsBatchedExpectation2 fulfill];
  806. XCTAssertNotNil(newBatchID);
  807. XCTAssertEqual(generatedEvents.count, batchEvents.count);
  808. NSSet<NSString *> *batchEventsIDs =
  809. [batchEvents valueForKeyPath:@"eventID"];
  810. NSSet<NSString *> *generatedEventsIDs =
  811. [generatedEvents valueForKeyPath:@"eventID"];
  812. XCTAssertEqualObjects(batchEventsIDs, generatedEventsIDs);
  813. }];
  814. [self waitForExpectations:@[ eventsBatchedExpectation2 ] timeout:0.5];
  815. }
  816. - (void)testRemoveBatchWithIDWithNoDeletingEventsConflictingEvents {
  817. GDTCORFlatFileStorage *storage = [[GDTCORFlatFileStorage alloc] init];
  818. // 0.1. Prepare a batch to remove.
  819. __auto_type generatedBatch = [self generateAndBatchEvents];
  820. NSNumber *batchIDToRemove = [generatedBatch.allKeys firstObject];
  821. NSSet<GDTCOREvent *> *generatedEvents = generatedBatch[batchIDToRemove];
  822. // 0.2. Store an event with conflicting ID.
  823. [self storeEvent:[generatedEvents anyObject] inStorage:storage];
  824. // 0.3. Store another event.
  825. GDTCOREvent *differentEvent = [GDTCOREventGenerator generateEventForTarget:kGDTCORTargetTest
  826. qosTier:nil
  827. mappingID:nil];
  828. [self storeEvent:differentEvent inStorage:storage];
  829. NSMutableSet<GDTCOREvent *> *expectedEvents = [generatedEvents mutableCopy];
  830. [expectedEvents addObject:differentEvent];
  831. // 2. Remove batch.
  832. XCTestExpectation *batchRemovedExpectation =
  833. [self expectationWithDescription:@"batchRemovedExpectation"];
  834. [storage removeBatchWithID:batchIDToRemove
  835. deleteEvents:NO
  836. onComplete:^{
  837. [batchRemovedExpectation fulfill];
  838. }];
  839. [self waitForExpectations:@[ batchRemovedExpectation ] timeout:0.5];
  840. // 3. Validate no batches.
  841. [self assertBatchIDs:nil inStorage:storage];
  842. // 4. Validate events.
  843. XCTestExpectation *eventsBatchedExpectation2 =
  844. [self expectationWithDescription:@"eventsBatchedExpectation1"];
  845. GDTCORStorageEventSelector *testEventsSelector =
  846. [[GDTCORStorageEventSelector alloc] initWithTarget:kGDTCORTargetTest
  847. eventIDs:nil
  848. mappingIDs:nil
  849. qosTiers:nil];
  850. [storage batchWithEventSelector:testEventsSelector
  851. batchExpiration:[NSDate distantFuture]
  852. onComplete:^(NSNumber *_Nullable newBatchID,
  853. NSSet<GDTCOREvent *> *_Nullable batchEvents) {
  854. [eventsBatchedExpectation2 fulfill];
  855. XCTAssertNotNil(newBatchID);
  856. XCTAssertEqual(expectedEvents.count, batchEvents.count);
  857. NSSet<NSString *> *batchEventsIDs =
  858. [batchEvents valueForKeyPath:@"eventID"];
  859. NSSet<NSString *> *expectedEventsIDs =
  860. [expectedEvents valueForKeyPath:@"eventID"];
  861. XCTAssertEqualObjects(batchEventsIDs, expectedEventsIDs);
  862. }];
  863. [self waitForExpectations:@[ eventsBatchedExpectation2 ] timeout:0.5];
  864. }
  865. - (void)testRemoveBatchWithIDDeletingEvents {
  866. GDTCORFlatFileStorage *storage = [[GDTCORFlatFileStorage alloc] init];
  867. // 0. Prepare a batch to remove.
  868. __auto_type generatedBatch = [self generateAndBatchEvents];
  869. NSNumber *batchIDToRemove = [generatedBatch.allKeys firstObject];
  870. // 2. Remove batch.
  871. XCTestExpectation *batchRemovedExpectation =
  872. [self expectationWithDescription:@"batchRemovedExpectation"];
  873. [storage removeBatchWithID:batchIDToRemove
  874. deleteEvents:YES
  875. onComplete:^{
  876. [batchRemovedExpectation fulfill];
  877. }];
  878. [self waitForExpectations:@[ batchRemovedExpectation ] timeout:0.5];
  879. // 3. Validate no batches.
  880. [self assertBatchIDs:nil inStorage:storage];
  881. // 4. Validate events.
  882. XCTestExpectation *eventsBatchedExpectation2 =
  883. [self expectationWithDescription:@"eventsBatchedExpectation1"];
  884. GDTCORStorageEventSelector *testEventsSelector =
  885. [[GDTCORStorageEventSelector alloc] initWithTarget:kGDTCORTargetTest
  886. eventIDs:nil
  887. mappingIDs:nil
  888. qosTiers:nil];
  889. [storage batchWithEventSelector:testEventsSelector
  890. batchExpiration:[NSDate distantFuture]
  891. onComplete:^(NSNumber *_Nullable newBatchID,
  892. NSSet<GDTCOREvent *> *_Nullable batchEvents) {
  893. [eventsBatchedExpectation2 fulfill];
  894. XCTAssertNil(newBatchID);
  895. XCTAssertEqual(batchEvents.count, 0);
  896. }];
  897. [self waitForExpectations:@[ eventsBatchedExpectation2 ] timeout:500];
  898. }
  899. /** Tests creating a batch and then deleting the files. */
  900. - (void)testRemoveBatchWithIDDeletingEventsStorageSize {
  901. GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
  902. NSSet<GDTCOREvent *> *generatedEvents = [self generateEventsForStorageTesting];
  903. __block NSUInteger testTargetSize = 0;
  904. NSSet<GDTCOREvent *> *testTargetEvents = [generatedEvents
  905. filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
  906. GDTCOREvent *_Nullable event,
  907. NSDictionary<NSString *, id> *_Nullable bindings) {
  908. NSError *error;
  909. testTargetSize +=
  910. event.target == kGDTCORTargetTest ? GDTCOREncodeArchive(event, nil, &error).length : 0;
  911. XCTAssertNil(error);
  912. return event.target == kGDTCORTargetTest;
  913. }]];
  914. XCTAssertNotNil(testTargetEvents);
  915. __block uint64_t totalSize;
  916. [storage storageSizeWithCallback:^(uint64_t storageSize) {
  917. totalSize = storageSize;
  918. }];
  919. XCTestExpectation *expectation = [self expectationWithDescription:@"batch callback invoked"];
  920. GDTCORStorageEventSelector *eventSelector =
  921. [GDTCORStorageEventSelector eventSelectorForTarget:kGDTCORTargetTest];
  922. __block NSNumber *batchID;
  923. [storage batchWithEventSelector:eventSelector
  924. batchExpiration:[NSDate dateWithTimeIntervalSinceNow:600]
  925. onComplete:^(NSNumber *_Nullable newBatchID,
  926. NSSet<GDTCOREvent *> *_Nullable events) {
  927. batchID = newBatchID;
  928. XCTAssertNotNil(batchID);
  929. XCTAssertEqual(events.count, testTargetEvents.count);
  930. [expectation fulfill];
  931. }];
  932. [self waitForExpectations:@[ expectation ] timeout:10];
  933. expectation = [self expectationWithDescription:@"batch removal completion invoked"];
  934. [storage removeBatchWithID:batchID
  935. deleteEvents:YES
  936. onComplete:^{
  937. [expectation fulfill];
  938. }];
  939. [self waitForExpectations:@[ expectation ] timeout:10];
  940. expectation = [self expectationWithDescription:@"storageSize callback invoked"];
  941. [storage storageSizeWithCallback:^(uint64_t storageSize) {
  942. XCTAssertLessThan(storageSize * .95, totalSize - testTargetSize); // .95 to allow overhead
  943. [expectation fulfill];
  944. }];
  945. [self waitForExpectations:@[ expectation ] timeout:10];
  946. }
  947. #pragma mark - Storage Size Limit
  948. /** Tests that the size of the storage is returned accurately. */
  949. - (void)testStorageSizeWithCallback {
  950. GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
  951. NSUInteger ongoingSize = 0;
  952. XCTAssertEqual([self storageSize], 0);
  953. // 1. Check add library data.
  954. NSData *libData = [@"this is a test" dataUsingEncoding:NSUTF8StringEncoding];
  955. ongoingSize += libData.length;
  956. [storage storeLibraryData:libData forKey:@"testKey" onComplete:nil];
  957. XCTAssertEqual([self storageSize], ongoingSize);
  958. // 2. Check update library data.
  959. NSData *updatedLibData = [@"updated" dataUsingEncoding:NSUTF8StringEncoding];
  960. ongoingSize -= libData.length;
  961. ongoingSize += updatedLibData.length;
  962. [storage libraryDataForKey:@"testKey"
  963. onFetchComplete:^(NSData *_Nullable data, NSError *_Nullable error) {
  964. }
  965. setNewValue:^NSData *_Nullable {
  966. return updatedLibData;
  967. }];
  968. XCTAssertEqual([self storageSize], ongoingSize);
  969. // 3. Check store events.
  970. NSSet<GDTCOREvent *> *generatedEvents = [self generateEventsForStorageTesting];
  971. ongoingSize += [self storageSizeOfEvents:generatedEvents];
  972. XCTAssertEqual([self storageSize], ongoingSize);
  973. // 4. Check remove lib data.
  974. ongoingSize -= updatedLibData.length;
  975. [storage removeLibraryDataForKey:@"testKey"
  976. onComplete:^(NSError *_Nullable error){
  977. }];
  978. XCTAssertEqual([self storageSize], ongoingSize);
  979. // 5. Check batch.
  980. XCTestExpectation *batchCreatedExpectation =
  981. [self expectationWithDescription:@"batchCreatedExpectation"];
  982. __block NSNumber *batchID;
  983. __block uint64_t batchedEventSize = 0;
  984. [storage
  985. batchWithEventSelector:[GDTCORStorageEventSelector eventSelectorForTarget:kGDTCORTargetTest]
  986. batchExpiration:[NSDate dateWithTimeIntervalSinceNow:1000]
  987. onComplete:^(NSNumber *_Nullable newBatchID,
  988. NSSet<GDTCOREvent *> *_Nullable events) {
  989. batchID = newBatchID;
  990. batchedEventSize = [self storageSizeOfEvents:events];
  991. // 100 - kGDTCORTargetTest generated events count.
  992. XCTAssertEqual(events.count, 100);
  993. [batchCreatedExpectation fulfill];
  994. }];
  995. [self waitForExpectations:@[ batchCreatedExpectation ] timeout:5];
  996. // Expect size increase due to the batch counter stored in lib data.
  997. ongoingSize += sizeof(int32_t);
  998. XCTAssertEqual([self storageSize], ongoingSize);
  999. // 6. Batch remove.
  1000. [storage removeBatchWithID:batchID
  1001. deleteEvents:YES
  1002. onComplete:^{
  1003. }];
  1004. ongoingSize -= batchedEventSize;
  1005. XCTAssertEqual([self storageSize], ongoingSize);
  1006. }
  1007. - (void)testStoreEvent_WhenSizeLimitReached_ThenNewEventIsSkipped {
  1008. GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
  1009. // 1. Generate and store maximum allowed amount of events.
  1010. __auto_type generatedEvents =
  1011. [self generateAndStoreEventsWithTotalSizeUpTo:kGDTCORFlatFileStorageSizeLimit];
  1012. XCTAssertGreaterThan([self storageSizeOfEvents:generatedEvents] +
  1013. [self storageEventSize:[generatedEvents anyObject]],
  1014. kGDTCORFlatFileStorageSizeLimit);
  1015. // 2. Check storage size.
  1016. uint64_t storageSize = [self storageSize];
  1017. XCTAssertEqual(storageSize, [self storageSizeOfEvents:generatedEvents]);
  1018. // 3. Try to add another event.
  1019. GDTCOREvent *event = [GDTCOREventGenerator generateEventForTarget:kGDTCORTargetTest
  1020. qosTier:nil
  1021. mappingID:nil];
  1022. event.expirationDate = [NSDate dateWithTimeIntervalSinceNow:1000];
  1023. XCTestExpectation *storeExpectation1 = [self expectationWithDescription:@"storeExpectation1"];
  1024. [storage storeEvent:event
  1025. onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
  1026. XCTAssertFalse(wasWritten);
  1027. XCTAssertNotNil(error);
  1028. XCTAssertEqualObjects(error.domain, GDTCORFlatFileStorageErrorDomain);
  1029. XCTAssertEqual(error.code, GDTCORFlatFileStorageErrorSizeLimitReached);
  1030. [storeExpectation1 fulfill];
  1031. }];
  1032. [self waitForExpectations:@[ storeExpectation1 ] timeout:5];
  1033. // 4. Check the storage size didn't change.
  1034. XCTAssertEqual([self storageSize], storageSize);
  1035. // 5. Batch and remove events
  1036. XCTestExpectation *batchCreatedExpectation =
  1037. [self expectationWithDescription:@"batchCreatedExpectation"];
  1038. __block NSNumber *batchID;
  1039. [storage
  1040. batchWithEventSelector:[GDTCORStorageEventSelector eventSelectorForTarget:kGDTCORTargetTest]
  1041. batchExpiration:[NSDate dateWithTimeIntervalSinceNow:1000]
  1042. onComplete:^(NSNumber *_Nullable newBatchID,
  1043. NSSet<GDTCOREvent *> *_Nullable events) {
  1044. batchID = newBatchID;
  1045. XCTAssertGreaterThan(events.count, 0);
  1046. [batchCreatedExpectation fulfill];
  1047. }];
  1048. [self waitForExpectations:@[ batchCreatedExpectation ] timeout:generatedEvents.count * 0.1];
  1049. XCTestExpectation *removeBatchExpectation = [self expectationWithDescription:@"removeBatch"];
  1050. [storage removeBatchWithID:batchID
  1051. deleteEvents:YES
  1052. onComplete:^{
  1053. [removeBatchExpectation fulfill];
  1054. }];
  1055. [self waitForExpectations:@[ removeBatchExpectation ] timeout:generatedEvents.count * 0.1];
  1056. // 6. Try to add another event.
  1057. XCTestExpectation *storeExpectation2 = [self expectationWithDescription:@"storeExpectation2"];
  1058. [storage storeEvent:event
  1059. onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
  1060. XCTAssertTrue(wasWritten);
  1061. XCTAssertNil(error);
  1062. [storeExpectation2 fulfill];
  1063. }];
  1064. [self waitForExpectations:@[ storeExpectation2 ] timeout:5];
  1065. GDTCORStorageSizeBytes lastBatchIDSize = sizeof(int32_t);
  1066. XCTAssertEqual([self storageSize], [self storageEventSize:event] + lastBatchIDSize);
  1067. }
  1068. #pragma mark - Helpers
  1069. /** Generates and returns a set of events that are generated randomly and stored.
  1070. *
  1071. * @return A set of randomly generated and stored events.
  1072. */
  1073. - (NSSet<GDTCOREvent *> *)generateEventsForStorageTesting {
  1074. NSMutableSet<GDTCOREvent *> *generatedEvents = [[NSMutableSet alloc] init];
  1075. // Generate 100 test target events
  1076. [generatedEvents unionSet:[self generateEventsForTarget:kGDTCORTargetTest
  1077. expiringIn:1000
  1078. count:100]];
  1079. // Generate 50 FLL target events.
  1080. [generatedEvents unionSet:[self generateEventsForTarget:kGDTCORTargetFLL
  1081. expiringIn:1000
  1082. count:50]];
  1083. return generatedEvents;
  1084. }
  1085. /** Generates and stores events with specified parameters.
  1086. * @return Generated events.
  1087. */
  1088. - (NSSet<GDTCOREvent *> *)generateEventsForTarget:(GDTCORTarget)target
  1089. expiringIn:(NSTimeInterval)eventsExpireIn
  1090. count:(NSInteger)count {
  1091. GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
  1092. NSMutableSet<GDTCOREvent *> *generatedEvents = [[NSMutableSet alloc] init];
  1093. XCTestExpectation *generatedEventsStoredExpectation =
  1094. [self expectationWithDescription:@"generatedEventsStoredExpectation"];
  1095. generatedEventsStoredExpectation.expectedFulfillmentCount = count;
  1096. for (int i = 0; i < count; i++) {
  1097. GDTCOREvent *event = [GDTCOREventGenerator generateEventForTarget:target
  1098. qosTier:nil
  1099. mappingID:nil];
  1100. event.expirationDate = [NSDate dateWithTimeIntervalSinceNow:eventsExpireIn];
  1101. [generatedEvents addObject:event];
  1102. [storage storeEvent:event
  1103. onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
  1104. XCTAssertTrue(wasWritten);
  1105. XCTAssertNil(error);
  1106. [generatedEventsStoredExpectation fulfill];
  1107. }];
  1108. }
  1109. [self waitForExpectations:@[ generatedEventsStoredExpectation ] timeout:1 * count];
  1110. return generatedEvents;
  1111. }
  1112. /** Generates and stores events to fill up the storage up the the specified size.
  1113. * @return Generated events.
  1114. */
  1115. - (NSSet<GDTCOREvent *> *)generateAndStoreEventsWithTotalSizeUpTo:
  1116. (GDTCORStorageSizeBytes)totalSize {
  1117. GDTCORTarget target = kGDTCORTargetTest;
  1118. GDTCORStorageSizeBytes eventsSize = 0;
  1119. NSMutableSet<GDTCOREvent *> *generatedEvents = [[NSMutableSet alloc] init];
  1120. GDTCOREvent *generatedEvent = [GDTCOREventGenerator generateEventForTarget:target
  1121. qosTier:nil
  1122. mappingID:nil];
  1123. do {
  1124. XCTestExpectation *eventStoredExpectation = [self expectationWithDescription:@"eventStored"];
  1125. [[GDTCORFlatFileStorage sharedInstance]
  1126. storeEvent:generatedEvent
  1127. onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
  1128. XCTAssertTrue(wasWritten);
  1129. XCTAssertNil(error);
  1130. [eventStoredExpectation fulfill];
  1131. }];
  1132. [self waitForExpectations:@[ eventStoredExpectation ] timeout:1];
  1133. [generatedEvents addObject:generatedEvent];
  1134. eventsSize += [self storageEventSize:generatedEvent];
  1135. generatedEvent = [GDTCOREventGenerator generateEventForTarget:target qosTier:nil mappingID:nil];
  1136. } while (eventsSize + [self storageEventSize:generatedEvent] <= totalSize);
  1137. return generatedEvents;
  1138. }
  1139. /** Generates, stores and batches 100 events.
  1140. * @return A dictionary with the generated events by the batch ID.
  1141. */
  1142. - (NSDictionary<NSNumber *, NSSet<GDTCOREvent *> *> *)generateAndBatchEvents {
  1143. return [self generateAndBatchEventsExpiringIn:1000 batchExpiringIn:1000];
  1144. }
  1145. /** Generates, stores and batches 100 events with specified parameters.
  1146. * @return A dictionary with the generated events by the batch ID.
  1147. */
  1148. - (NSDictionary<NSNumber *, NSSet<GDTCOREvent *> *> *)
  1149. generateAndBatchEventsExpiringIn:(NSTimeInterval)eventsExpireIn
  1150. batchExpiringIn:(NSTimeInterval)batchExpiresIn {
  1151. GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
  1152. NSSet<GDTCOREvent *> *events = [self generateEventsForTarget:kGDTCORTargetTest
  1153. expiringIn:eventsExpireIn
  1154. count:100];
  1155. XCTestExpectation *eventsGeneratedExpectation =
  1156. [self expectationWithDescription:@"eventsGeneratedExpectation"];
  1157. [storage hasEventsForTarget:kGDTCORTargetTest
  1158. onComplete:^(BOOL hasEvents) {
  1159. XCTAssertTrue(hasEvents);
  1160. [eventsGeneratedExpectation fulfill];
  1161. }];
  1162. [self waitForExpectations:@[ eventsGeneratedExpectation ] timeout:5];
  1163. // Batch generated events.
  1164. XCTestExpectation *batchCreatedExpectation =
  1165. [self expectationWithDescription:@"batchCreatedExpectation"];
  1166. __block NSNumber *batchID;
  1167. [storage
  1168. batchWithEventSelector:[GDTCORStorageEventSelector eventSelectorForTarget:kGDTCORTargetTest]
  1169. batchExpiration:[NSDate dateWithTimeIntervalSinceNow:batchExpiresIn]
  1170. onComplete:^(NSNumber *_Nullable newBatchID,
  1171. NSSet<GDTCOREvent *> *_Nullable events) {
  1172. batchID = newBatchID;
  1173. XCTAssertGreaterThan(events.count, 0);
  1174. [batchCreatedExpectation fulfill];
  1175. }];
  1176. [self waitForExpectations:@[ batchCreatedExpectation ] timeout:5];
  1177. return @{batchID : events};
  1178. }
  1179. /** Calls `[GDTCORFlatFileStorage batchIDsForTarget:onComplete:]`, waits for the completion and
  1180. * asserts the result. */
  1181. - (void)assertBatchIDs:(NSSet<NSNumber *> *)expectedBatchIDs
  1182. inStorage:(GDTCORFlatFileStorage *)storage {
  1183. XCTestExpectation *batchIDsFetchedExpectation =
  1184. [self expectationWithDescription:@"batchIDsFetchedExpectation"];
  1185. [storage batchIDsForTarget:kGDTCORTargetTest
  1186. onComplete:^(NSSet<NSNumber *> *_Nullable batchIDs) {
  1187. [batchIDsFetchedExpectation fulfill];
  1188. XCTAssertEqualObjects(batchIDs, expectedBatchIDs);
  1189. }];
  1190. [self waitForExpectations:@[ batchIDsFetchedExpectation ] timeout:0.5];
  1191. }
  1192. /** Calls `[GDTCORFlatFileStorage storeEvent:onComplete:]` and waits for the completion. */
  1193. - (void)storeEvent:(GDTCOREvent *)event inStorage:(GDTCORFlatFileStorage *)storage {
  1194. XCTestExpectation *eventStoredExpectation =
  1195. [self expectationWithDescription:@"eventStoredExpectation"];
  1196. [storage storeEvent:event
  1197. onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
  1198. [eventStoredExpectation fulfill];
  1199. XCTAssertTrue(wasWritten);
  1200. XCTAssertNil(error);
  1201. }];
  1202. [self waitForExpectations:@[ eventStoredExpectation ] timeout:0.5];
  1203. }
  1204. /** Calls `[GDTCORFlatFileStorage storageSizeWithCallback]`, waits for completion and returns the
  1205. * result. */
  1206. - (uint64_t)storageSize {
  1207. __block uint64_t storageSize = 0;
  1208. XCTestExpectation *expectation = [self expectationWithDescription:@"storageSize complete"];
  1209. [[GDTCORFlatFileStorage sharedInstance] storageSizeWithCallback:^(uint64_t aStorageSize) {
  1210. storageSize = aStorageSize;
  1211. [expectation fulfill];
  1212. }];
  1213. [self waitForExpectations:@[ expectation ] timeout:1.0];
  1214. return storageSize;
  1215. }
  1216. /** Returns an expected size taken by the events in the storage. */
  1217. - (GDTCORStorageSizeBytes)storageSizeOfEvents:(NSSet<GDTCOREvent *> *)events {
  1218. uint64_t eventsSize = 0;
  1219. for (GDTCOREvent *event in events) {
  1220. eventsSize += [self storageEventSize:event];
  1221. }
  1222. return eventsSize;
  1223. }
  1224. /** Returns an expected size taken by the event in the storage. */
  1225. - (GDTCORStorageSizeBytes)storageEventSize:(GDTCOREvent *)event {
  1226. NSError *error;
  1227. NSData *serializedEventData = GDTCOREncodeArchive(event, nil, &error);
  1228. XCTAssertNil(error);
  1229. return serializedEventData.length;
  1230. }
  1231. @end