GDTCORStorageTest.m 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  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 "GDTCORTests/Unit/GDTCORTestCase.h"
  17. #import "GDTCORLibrary/Private/GDTCORRegistrar_Private.h"
  18. #import "GDTCORLibrary/Private/GDTCORStorage.h"
  19. #import "GDTCORLibrary/Private/GDTCORStorage_Private.h"
  20. #import "GDTCORLibrary/Public/GDTCOREvent.h"
  21. #import "GDTCORLibrary/Public/GDTCORRegistrar.h"
  22. #import "GDTCORTests/Unit/Helpers/GDTCORAssertHelper.h"
  23. #import "GDTCORTests/Unit/Helpers/GDTCORDataObjectTesterClasses.h"
  24. #import "GDTCORTests/Unit/Helpers/GDTCORTestPrioritizer.h"
  25. #import "GDTCORTests/Unit/Helpers/GDTCORTestUploader.h"
  26. #import "GDTCORTests/Common/Fakes/GDTCORUploadCoordinatorFake.h"
  27. #import "GDTCORTests/Common/Categories/GDTCORRegistrar+Testing.h"
  28. #import "GDTCORTests/Common/Categories/GDTCORStorage+Testing.h"
  29. static NSInteger target = kGDTCORTargetCCT;
  30. @interface GDTCORStorageTest : GDTCORTestCase
  31. /** The test backend implementation. */
  32. @property(nullable, nonatomic) GDTCORTestUploader *testBackend;
  33. /** The test prioritizer implementation. */
  34. @property(nullable, nonatomic) GDTCORTestPrioritizer *testPrioritizer;
  35. /** The uploader fake. */
  36. @property(nonatomic) GDTCORUploadCoordinatorFake *uploaderFake;
  37. @end
  38. @implementation GDTCORStorageTest
  39. - (void)setUp {
  40. [super setUp];
  41. self.testBackend = [[GDTCORTestUploader alloc] init];
  42. self.testPrioritizer = [[GDTCORTestPrioritizer alloc] init];
  43. [[GDTCORRegistrar sharedInstance] registerUploader:_testBackend target:target];
  44. [[GDTCORRegistrar sharedInstance] registerPrioritizer:_testPrioritizer target:target];
  45. self.uploaderFake = [[GDTCORUploadCoordinatorFake alloc] init];
  46. [GDTCORStorage sharedInstance].uploadCoordinator = self.uploaderFake;
  47. }
  48. - (void)tearDown {
  49. [super tearDown];
  50. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  51. });
  52. // Destroy these objects before the next test begins.
  53. self.testBackend = nil;
  54. self.testPrioritizer = nil;
  55. [[GDTCORRegistrar sharedInstance] reset];
  56. [[GDTCORStorage sharedInstance] reset];
  57. [GDTCORStorage sharedInstance].uploadCoordinator = [GDTCORUploadCoordinator sharedInstance];
  58. self.uploaderFake = nil;
  59. }
  60. /** Tests the singleton pattern. */
  61. - (void)testInit {
  62. XCTAssertEqual([GDTCORStorage sharedInstance], [GDTCORStorage sharedInstance]);
  63. }
  64. /** Tests storing an event. */
  65. - (void)testStoreEvent {
  66. // event is autoreleased, and the pool needs to drain.
  67. @autoreleasepool {
  68. GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
  69. event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString"];
  70. event.clockSnapshot = [GDTCORClock snapshot];
  71. XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
  72. XCTAssertNoThrow([[GDTCORStorage sharedInstance] storeEvent:event
  73. onComplete:^(BOOL wasWritten, NSError *error) {
  74. XCTAssertTrue(wasWritten);
  75. XCTAssertNil(error);
  76. [writtenExpectation fulfill];
  77. }]);
  78. [self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
  79. }
  80. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  81. XCTAssertEqual([GDTCORStorage sharedInstance].storedEvents.count, 1);
  82. XCTAssertEqual([GDTCORStorage sharedInstance].targetToEventSet[@(target)].count, 1);
  83. NSURL *eventFile = [[GDTCORStorage sharedInstance].storedEvents lastObject].fileURL;
  84. XCTAssertNotNil(eventFile);
  85. XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:eventFile.path]);
  86. NSError *error;
  87. XCTAssertTrue([[NSFileManager defaultManager] removeItemAtURL:eventFile error:&error]);
  88. XCTAssertNil(error, @"There was an error deleting the eventFile: %@", error);
  89. });
  90. }
  91. /** Tests removing an event. */
  92. - (void)testRemoveEvent {
  93. // event is autoreleased, and the pool needs to drain.
  94. @autoreleasepool {
  95. GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
  96. event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString"];
  97. event.clockSnapshot = [GDTCORClock snapshot];
  98. XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
  99. XCTAssertNoThrow([[GDTCORStorage sharedInstance] storeEvent:event
  100. onComplete:^(BOOL wasWritten, NSError *error) {
  101. XCTAssertTrue(wasWritten);
  102. XCTAssertNil(error);
  103. [writtenExpectation fulfill];
  104. }]);
  105. [self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
  106. }
  107. __block NSURL *eventFile;
  108. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  109. eventFile = [[GDTCORStorage sharedInstance].storedEvents lastObject].fileURL;
  110. XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:eventFile.path]);
  111. });
  112. [[GDTCORStorage sharedInstance] removeEvents:[GDTCORStorage sharedInstance].storedEvents.set];
  113. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  114. XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:eventFile.path]);
  115. XCTAssertEqual([GDTCORStorage sharedInstance].storedEvents.count, 0);
  116. XCTAssertEqual([GDTCORStorage sharedInstance].targetToEventSet[@(target)].count, 0);
  117. });
  118. }
  119. /** Tests removing a set of events. */
  120. - (void)testRemoveEvents {
  121. GDTCORStorage *storage = [GDTCORStorage sharedInstance];
  122. __block GDTCOREvent *storedEvent1, *storedEvent2, *storedEvent3;
  123. // events are autoreleased, and the pool needs to drain.
  124. @autoreleasepool {
  125. GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
  126. event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString1"];
  127. XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
  128. XCTAssertNoThrow([[GDTCORStorage sharedInstance] storeEvent:event
  129. onComplete:^(BOOL wasWritten, NSError *error) {
  130. XCTAssertTrue(wasWritten);
  131. XCTAssertNil(error);
  132. [writtenExpectation fulfill];
  133. }]);
  134. [self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
  135. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  136. storedEvent1 = [storage.storedEvents lastObject];
  137. });
  138. event = [[GDTCOREvent alloc] initWithMappingID:@"100" target:target];
  139. event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString2"];
  140. writtenExpectation = [self expectationWithDescription:@"event written"];
  141. XCTAssertNoThrow([[GDTCORStorage sharedInstance] storeEvent:event
  142. onComplete:^(BOOL wasWritten, NSError *error) {
  143. XCTAssertTrue(wasWritten);
  144. XCTAssertNil(error);
  145. [writtenExpectation fulfill];
  146. }]);
  147. [self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
  148. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  149. storedEvent2 = [storage.storedEvents lastObject];
  150. });
  151. event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
  152. event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString3"];
  153. writtenExpectation = [self expectationWithDescription:@"event written"];
  154. XCTAssertNoThrow([[GDTCORStorage sharedInstance] storeEvent:event
  155. onComplete:^(BOOL wasWritten, NSError *error) {
  156. XCTAssertTrue(wasWritten);
  157. XCTAssertNil(error);
  158. [writtenExpectation fulfill];
  159. }]);
  160. [self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
  161. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  162. storedEvent3 = [storage.storedEvents lastObject];
  163. });
  164. }
  165. NSSet<GDTCOREvent *> *eventSet =
  166. [NSSet setWithObjects:storedEvent1, storedEvent2, storedEvent3, nil];
  167. [storage removeEvents:eventSet];
  168. dispatch_sync(storage.storageQueue, ^{
  169. XCTAssertFalse([storage.storedEvents containsObject:storedEvent1]);
  170. XCTAssertFalse([storage.storedEvents containsObject:storedEvent2]);
  171. XCTAssertFalse([storage.storedEvents containsObject:storedEvent3]);
  172. XCTAssertEqual(storage.targetToEventSet[@(target)].count, 0);
  173. for (GDTCOREvent *event in eventSet) {
  174. XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:event.fileURL.path]);
  175. }
  176. });
  177. }
  178. /** Tests storing a few different events. */
  179. - (void)testStoreMultipleEvents {
  180. __block GDTCOREvent *storedEvent1, *storedEvent2, *storedEvent3;
  181. // events are autoreleased, and the pool needs to drain.
  182. @autoreleasepool {
  183. GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
  184. event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString1"];
  185. XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
  186. XCTAssertNoThrow([[GDTCORStorage sharedInstance] storeEvent:event
  187. onComplete:^(BOOL wasWritten, NSError *error) {
  188. XCTAssertTrue(wasWritten);
  189. XCTAssertNil(error);
  190. [writtenExpectation fulfill];
  191. }]);
  192. [self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
  193. XCTAssertNotNil(event.fileURL);
  194. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  195. storedEvent1 = [[GDTCORStorage sharedInstance].storedEvents lastObject];
  196. });
  197. event = [[GDTCOREvent alloc] initWithMappingID:@"100" target:target];
  198. event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString2"];
  199. writtenExpectation = [self expectationWithDescription:@"event written"];
  200. XCTAssertNoThrow([[GDTCORStorage sharedInstance] storeEvent:event
  201. onComplete:^(BOOL wasWritten, NSError *error) {
  202. XCTAssertTrue(wasWritten);
  203. XCTAssertNil(error);
  204. [writtenExpectation fulfill];
  205. }]);
  206. [self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
  207. XCTAssertNotNil(event.fileURL);
  208. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  209. storedEvent2 = [[GDTCORStorage sharedInstance].storedEvents lastObject];
  210. });
  211. event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
  212. event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString3"];
  213. writtenExpectation = [self expectationWithDescription:@"event written"];
  214. XCTAssertNoThrow([[GDTCORStorage sharedInstance] storeEvent:event
  215. onComplete:^(BOOL wasWritten, NSError *error) {
  216. XCTAssertTrue(wasWritten);
  217. XCTAssertNil(error);
  218. [writtenExpectation fulfill];
  219. }]);
  220. [self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
  221. XCTAssertNotNil(event.fileURL);
  222. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  223. storedEvent3 = [[GDTCORStorage sharedInstance].storedEvents lastObject];
  224. });
  225. }
  226. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  227. XCTAssertEqual([GDTCORStorage sharedInstance].storedEvents.count, 3);
  228. XCTAssertEqual([GDTCORStorage sharedInstance].targetToEventSet[@(target)].count, 3);
  229. NSURL *event1File = storedEvent1.fileURL;
  230. XCTAssertNotNil(event1File);
  231. XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:event1File.path]);
  232. NSError *error;
  233. XCTAssertTrue([[NSFileManager defaultManager] removeItemAtURL:event1File error:&error]);
  234. XCTAssertNil(error, @"There was an error deleting the eventFile: %@", error);
  235. NSURL *event2File = storedEvent2.fileURL;
  236. XCTAssertNotNil(event2File);
  237. XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:event2File.path]);
  238. error = nil;
  239. XCTAssertTrue([[NSFileManager defaultManager] removeItemAtURL:event2File error:&error]);
  240. XCTAssertNil(error, @"There was an error deleting the eventFile: %@", error);
  241. NSURL *event3File = storedEvent3.fileURL;
  242. XCTAssertNotNil(event3File);
  243. XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:event3File.path]);
  244. error = nil;
  245. XCTAssertTrue([[NSFileManager defaultManager] removeItemAtURL:event3File error:&error]);
  246. XCTAssertNil(error, @"There was an error deleting the eventFile: %@", error);
  247. });
  248. }
  249. /** Tests enforcing that a prioritizer does not retain the DataObjectTransportBytes of an event in
  250. * memory.
  251. */
  252. - (void)testEventDeallocationIsEnforced {
  253. __weak NSData *weakDataObjectTransportBytes;
  254. GDTCOREvent *event;
  255. @autoreleasepool {
  256. event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
  257. weakDataObjectTransportBytes = [event.dataObject transportBytes];
  258. event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString"];
  259. event.clockSnapshot = [GDTCORClock snapshot];
  260. // Store the event and wait for the expectation.
  261. XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
  262. XCTAssertNoThrow([[GDTCORStorage sharedInstance] storeEvent:event
  263. onComplete:^(BOOL wasWritten, NSError *error) {
  264. XCTAssertTrue(wasWritten);
  265. XCTAssertNil(error);
  266. [writtenExpectation fulfill];
  267. }]);
  268. [self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
  269. }
  270. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  271. XCTAssertNil(weakDataObjectTransportBytes);
  272. XCTAssertNotNil(event);
  273. });
  274. NSURL *eventFile;
  275. eventFile = [[GDTCORStorage sharedInstance].storedEvents lastObject].fileURL;
  276. // This isn't strictly necessary because of the -waitForExpectations above.
  277. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  278. XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:eventFile.path]);
  279. });
  280. // Ensure event was removed.
  281. [[GDTCORStorage sharedInstance] removeEvents:[GDTCORStorage sharedInstance].storedEvents.set];
  282. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  283. XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:eventFile.path]);
  284. XCTAssertEqual([GDTCORStorage sharedInstance].storedEvents.count, 0);
  285. XCTAssertEqual([GDTCORStorage sharedInstance].targetToEventSet[@(target)].count, 0);
  286. });
  287. }
  288. /** Tests encoding and decoding the storage singleton correctly. */
  289. - (void)testNSSecureCoding {
  290. XCTAssertTrue([GDTCORStorage supportsSecureCoding]);
  291. GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
  292. event.clockSnapshot = [GDTCORClock snapshot];
  293. event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString"];
  294. XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
  295. XCTAssertNoThrow([[GDTCORStorage sharedInstance] storeEvent:event
  296. onComplete:^(BOOL wasWritten, NSError *error) {
  297. XCTAssertTrue(wasWritten);
  298. [writtenExpectation fulfill];
  299. }]);
  300. [self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
  301. event = nil;
  302. __block NSData *storageData;
  303. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  304. if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
  305. storageData = [NSKeyedArchiver archivedDataWithRootObject:[GDTCORStorage sharedInstance]
  306. requiringSecureCoding:YES
  307. error:nil];
  308. } else {
  309. #if !TARGET_OS_MACCATALYST
  310. storageData = [NSKeyedArchiver archivedDataWithRootObject:[GDTCORStorage sharedInstance]];
  311. #endif
  312. }
  313. });
  314. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  315. XCTAssertNotNil([[GDTCORStorage sharedInstance].storedEvents lastObject]);
  316. });
  317. [[GDTCORStorage sharedInstance] removeEvents:[GDTCORStorage sharedInstance].storedEvents.set];
  318. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  319. XCTAssertNil([[GDTCORStorage sharedInstance].storedEvents lastObject]);
  320. });
  321. GDTCORStorage *unarchivedStorage;
  322. NSError *error;
  323. if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
  324. unarchivedStorage = [NSKeyedUnarchiver unarchivedObjectOfClass:[GDTCORStorage class]
  325. fromData:storageData
  326. error:&error];
  327. } else {
  328. #if !TARGET_OS_MACCATALYST
  329. unarchivedStorage = [NSKeyedUnarchiver unarchiveObjectWithData:storageData];
  330. #endif
  331. }
  332. XCTAssertNotNil([unarchivedStorage.storedEvents lastObject]);
  333. }
  334. /** Tests encoding and decoding the storage singleton when calling -sharedInstance. */
  335. - (void)testNSSecureCodingWithSharedInstance {
  336. GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
  337. event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString"];
  338. event.clockSnapshot = [GDTCORClock snapshot];
  339. XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
  340. XCTAssertNoThrow([[GDTCORStorage sharedInstance] storeEvent:event
  341. onComplete:^(BOOL wasWritten, NSError *error) {
  342. XCTAssertTrue(wasWritten);
  343. [writtenExpectation fulfill];
  344. }]);
  345. [self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
  346. event = nil;
  347. __block NSData *storageData;
  348. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  349. if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
  350. storageData = [NSKeyedArchiver archivedDataWithRootObject:[GDTCORStorage sharedInstance]
  351. requiringSecureCoding:YES
  352. error:nil];
  353. } else {
  354. #if !TARGET_OS_MACCATALYST
  355. storageData = [NSKeyedArchiver archivedDataWithRootObject:[GDTCORStorage sharedInstance]];
  356. #endif
  357. }
  358. });
  359. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  360. XCTAssertNotNil([[GDTCORStorage sharedInstance].storedEvents lastObject]);
  361. });
  362. [[GDTCORStorage sharedInstance] removeEvents:[GDTCORStorage sharedInstance].storedEvents.set];
  363. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  364. XCTAssertNil([[GDTCORStorage sharedInstance].storedEvents lastObject]);
  365. });
  366. GDTCORStorage *unarchivedStorage;
  367. if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
  368. unarchivedStorage = [NSKeyedUnarchiver unarchivedObjectOfClass:[GDTCORStorage class]
  369. fromData:storageData
  370. error:nil];
  371. } else {
  372. #if !TARGET_OS_MACCATALYST
  373. unarchivedStorage = [NSKeyedUnarchiver unarchiveObjectWithData:storageData];
  374. #endif
  375. }
  376. XCTAssertNotNil([unarchivedStorage.storedEvents lastObject]);
  377. }
  378. /** Tests sending a fast priority event causes an upload attempt. */
  379. - (void)testQoSTierFast {
  380. // event is autoreleased, and the pool needs to drain.
  381. @autoreleasepool {
  382. GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
  383. event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString"];
  384. event.qosTier = GDTCOREventQoSFast;
  385. event.clockSnapshot = [GDTCORClock snapshot];
  386. XCTAssertFalse(self.uploaderFake.forceUploadCalled);
  387. XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
  388. XCTAssertNoThrow([[GDTCORStorage sharedInstance] storeEvent:event
  389. onComplete:^(BOOL wasWritten, NSError *error) {
  390. XCTAssertTrue(wasWritten);
  391. XCTAssertNil(error);
  392. [writtenExpectation fulfill];
  393. }]);
  394. [self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
  395. }
  396. dispatch_sync([GDTCORStorage sharedInstance].storageQueue, ^{
  397. XCTAssertTrue(self.uploaderFake.forceUploadCalled);
  398. XCTAssertEqual([GDTCORStorage sharedInstance].storedEvents.count, 1);
  399. XCTAssertEqual([GDTCORStorage sharedInstance].targetToEventSet[@(target)].count, 1);
  400. NSURL *eventFile = [[GDTCORStorage sharedInstance].storedEvents lastObject].fileURL;
  401. XCTAssertNotNil(eventFile);
  402. XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:eventFile.path]);
  403. NSError *error;
  404. XCTAssertTrue([[NSFileManager defaultManager] removeItemAtURL:eventFile error:&error]);
  405. XCTAssertNil(error, @"There was an error deleting the eventFile: %@", error);
  406. });
  407. }
  408. /** Fuzz tests the storing of events at the same time as a terminate lifecycle notification. This
  409. * test can fail if there's simultaneous access to ivars of GDTCORStorage with one access being
  410. * off the storage's queue. The terminate lifecycle event should operate on and flush the queue.
  411. */
  412. - (void)testStoringEventsDuringTerminate {
  413. int numberOfIterations = 1000;
  414. for (int i = 0; i < numberOfIterations; i++) {
  415. NSString *testString = [NSString stringWithFormat:@"testString %d", i];
  416. GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
  417. event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:testString];
  418. event.clockSnapshot = [GDTCORClock snapshot];
  419. XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
  420. XCTAssertNoThrow([[GDTCORStorage sharedInstance] storeEvent:event
  421. onComplete:^(BOOL wasWritten, NSError *error) {
  422. XCTAssertTrue(wasWritten);
  423. [writtenExpectation fulfill];
  424. }]);
  425. [self waitForExpectationsWithTimeout:10 handler:nil];
  426. if (i % 5 == 0) {
  427. [[GDTCORStorage sharedInstance] removeEvents:[GDTCORStorage sharedInstance].storedEvents.set];
  428. }
  429. [NSNotificationCenter.defaultCenter
  430. postNotificationName:kGDTCORApplicationWillTerminateNotification
  431. object:nil];
  432. }
  433. }
  434. /** Tests migration from v1 of the storage format to v2. */
  435. - (void)testMigrationFromOldVersion {
  436. static NSString *base64EncodedArchive =
  437. @"YnBsaXN0MDDUAAEAAgADAAQABQAGAAcAClgkdmVyc2lvblkkYXJjaGl2ZXJUJHRvcFgkb2JqZWN0cxIAAYagXxAPTlN"
  438. @"LZXllZEFyY2hpdmVy0QAIAAlUcm9vdIABrxBxAAsADAAVADsASQBPAFUAVgBcAGAAYQBiAGMAbQBxAHUAfQCBAIUAhg"
  439. @"CKAJIAlgCaAJsAnwCnAKsArwCwALQAvADAAMQAxQDGAMoA0gDWANoA2wDfAOcA6wDvAPAA8QD1AP0BAQEFAQYBCgESA"
  440. @"RYBGgEbAR8BJwErAS8BMAE0ATwBQAFEAUUBSQFRAVUBWQFaAV4BZgFqAW4BbwFwAXQBfAGAAYQBhQGJAZEBlQGZAZoB"
  441. @"ngGmAaoBrgGvAbABtAG8AcABxAHFAckB0QHVAdkB2gHeAeMB7QH2AfsCCgIPAhECFVUkbnVsbNQADQAOAA8AEAARABI"
  442. @"AEwAUXxAhR0RUQ09SU3RvcmFnZVVwbG9hZENvb3JkaW5hdG9yS2V5XxAgR0RUQ09SU3RvcmFnZVRhcmdldFRvRXZlbn"
  443. @"RTZXRLZXlfEBxHRFRDT1JTdG9yYWdlU3RvcmVkRXZlbnRzS2V5ViRjbGFzc4BugGmAAoBw3xATABYAFwAYABkAGgAbA"
  444. @"BwAHQAeAB8AIAAQACEAIgAjACQAJQAmACcAKAApACoAKwAsAC0ALgAvADAAMQAyADMANAA1ADYANwA4ADkAOltOUy5v"
  445. @"YmplY3QuNlxOUy5vYmplY3QuMTZbTlMub2JqZWN0LjdbTlMub2JqZWN0LjhcTlMub2JqZWN0LjE3W05TLm9iamVjdC4"
  446. @"5XE5TLm9iamVjdC4xMFxOUy5vYmplY3QuMTFbTlMub2JqZWN0LjBcTlMub2JqZWN0LjEyW05TLm9iamVjdC4xW05TLm"
  447. @"9iamVjdC4yXE5TLm9iamVjdC4xM1tOUy5vYmplY3QuM1xOUy5vYmplY3QuMTRbTlMub2JqZWN0LjRbTlMub2JqZWN0L"
  448. @"jVcTlMub2JqZWN0LjE1gCmAXoAvgDSAY4A5gD6AQ4ADgEiAD4BogBSAToAZgFOAHoAkgFjXABAAPAA9AD4APwBAAEEA"
  449. @"QgBDAEQARQBGAEcASF8QGkdEVENPUlN0b3JlZEV2ZW50VGFyZ2V0S2V5XxAeR0RUQ09SU3RvcmVkRXZlbnREYXRhRnV"
  450. @"0dXJlS2V5XxAbR0RUQ09SU3RvcmVkRXZlbnRRb3NUaWVyS2V5XxAlR0RUQ09SU3RvcmVkRXZlbnRjdXN0b21CeXRlc1"
  451. @"BhcmFtc0tleV8QIUdEVENPUlN0b3JlZEV2ZW50Q2xvY2tTbmFwc2hvdEtleV8QHUdEVENPUlN0b3JlZEV2ZW50TWFwc"
  452. @"GluZ0lES2V5gA6ACoAEgAuAAIAMgAnTABAASgBLAEwARgBOXxAXR0RUQ09SRGF0YUZ1dHVyZURhdGFLZXlfEBpHRFRD"
  453. @"T1JEYXRhRnV0dXJlRmlsZVVSTEtleYAIgACABdMAUAAQAFEARgBTAFRXTlMuYmFzZVtOUy5yZWxhdGl2ZYAAgAeABl8"
  454. @"Q5GZpbGU6Ly8vVXNlcnMvaGFuZXltL0xpYnJhcnkvRGV2ZWxvcGVyL0NvcmVTaW11bGF0b3IvRGV2aWNlcy83Q0ZDMD"
  455. @"BCMS05MDc0LTQ2NDgtQTRCMC1CNzk0RkYzRUM2OTEvZGF0YS9Db250YWluZXJzL0RhdGEvQXBwbGljYXRpb24vOEUyM"
  456. @"DcyQjQtNjYzQi00NTdFLTlGQUMtRjVBQUIwMDZBRkZBL0xpYnJhcnkvQ2FjaGVzL2dvb2dsZS1zZGtzLWV2ZW50cy9l"
  457. @"dmVudC0xODI5Nzg1NzIwNjcyMjc5OTkwONIAVwBYAFkAWlokY2xhc3NuYW1lWCRjbGFzc2VzVU5TVVJMogBZAFtYTlN"
  458. @"PYmplY3TSAFcAWABdAF5fEBBHRFRDT1JEYXRhRnV0dXJlogBfAFtfEBBHRFRDT1JEYXRhRnV0dXJlVDEwMTgRA+gQAd"
  459. @"UAEABkAGUAZgBnAGgAaQBqAGsAbF8QFUdEVENPUkNsb2NrVGltZU1pbGxpc18QEUdEVENPUkNsb2NrVXB0aW1lXxAgR"
  460. @"0RUQ09SQ2xvY2tUaW1lem9uZU9mZnNldFNlY29uZHNfEBlHRFRDT1JDbG9ja0tlcm5lbEJvb3RUaW1lgA0TAAABcEWS"
  461. @"uikTAAACb3DqLb8T////////j4ATAAWcIFQ9BZLSAFcAWABuAG9bR0RUQ09SQ2xvY2uiAHAAW1tHRFRDT1JDbG9ja9I"
  462. @"AVwBYAHIAc18QEUdEVENPUlN0b3JlZEV2ZW50ogB0AFtfEBFHRFRDT1JTdG9yZWRFdmVudNcAEAA8AD0APgA/AEAAQQ"
  463. @"BCAEMAeABFAEYAewBIgA6ACoAQgAuAAIATgAnTABAASgBLAEwARgCAgAiAAIAR0wBQABAAUQBGAFMAhIAAgAeAEl8Q5"
  464. @"GZpbGU6Ly8vVXNlcnMvaGFuZXltL0xpYnJhcnkvRGV2ZWxvcGVyL0NvcmVTaW11bGF0b3IvRGV2aWNlcy83Q0ZDMDBC"
  465. @"MS05MDc0LTQ2NDgtQTRCMC1CNzk0RkYzRUM2OTEvZGF0YS9Db250YWluZXJzL0RhdGEvQXBwbGljYXRpb24vOEUyMDc"
  466. @"yQjQtNjYzQi00NTdFLTlGQUMtRjVBQUIwMDZBRkZBL0xpYnJhcnkvQ2FjaGVzL2dvb2dsZS1zZGtzLWV2ZW50cy9ldm"
  467. @"VudC0xODI5NzcwODE2NDQwNDM0OTE4MdUAEABkAGUAZgBnAGgAiACJAGsAbIANEwAAAXBFkrrLEwAAAm9w7KYU1wAQA"
  468. @"DwAPQA+AD8AQABBAEIAQwCNAEUARgCQAEiADoAKgBWAC4AAgBiACdMAEABKAEsATABGAJWACIAAgBbTAFAAEABRAEYA"
  469. @"UwCZgACAB4AXXxDkZmlsZTovLy9Vc2Vycy9oYW5leW0vTGlicmFyeS9EZXZlbG9wZXIvQ29yZVNpbXVsYXRvci9EZXZ"
  470. @"pY2VzLzdDRkMwMEIxLTkwNzQtNDY0OC1BNEIwLUI3OTRGRjNFQzY5MS9kYXRhL0NvbnRhaW5lcnMvRGF0YS9BcHBsaW"
  471. @"NhdGlvbi84RTIwNzJCNC02NjNCLTQ1N0UtOUZBQy1GNUFBQjAwNkFGRkEvTGlicmFyeS9DYWNoZXMvZ29vZ2xlLXNka"
  472. @"3MtZXZlbnRzL2V2ZW50LTE4Mjk1OTM1ODIwNjI5NzUxODU41QAQAGQAZQBmAGcAaACdAJ4AawBsgA0TAAABcEWSu14T"
  473. @"AAACb3Du5R7XABAAPAA9AD4APwBAAEEAQgBDAKIARQBGAKUASIAOgAqAGoALgACAHYAJ0wAQAEoASwBMAEYAqoAIgAC"
  474. @"AG9MAUAAQAFEARgBTAK6AAIAHgBxfEORmaWxlOi8vL1VzZXJzL2hhbmV5bS9MaWJyYXJ5L0RldmVsb3Blci9Db3JlU2"
  475. @"ltdWxhdG9yL0RldmljZXMvN0NGQzAwQjEtOTA3NC00NjQ4LUE0QjAtQjc5NEZGM0VDNjkxL2RhdGEvQ29udGFpbmVyc"
  476. @"y9EYXRhL0FwcGxpY2F0aW9uLzhFMjA3MkI0LTY2M0ItNDU3RS05RkFDLUY1QUFCMDA2QUZGQS9MaWJyYXJ5L0NhY2hl"
  477. @"cy9nb29nbGUtc2Rrcy1ldmVudHMvZXZlbnQtMTgyOTY5NjUyNDM0ODIxNTA3NDPVABAAZABlAGYAZwBoALIAswBrAGy"
  478. @"ADRMAAAFwRZK8FhMAAAJvcPGzA9cAEAA8AD0APgA/AEAAQQBCAEMAtwC4AEYAugBIgA6ACoAfgCKAAIAjgAnTABAASg"
  479. @"BLAEwARgC/gAiAAIAg0wBQABAAUQBGAFMAw4AAgAeAIV8Q5GZpbGU6Ly8vVXNlcnMvaGFuZXltL0xpYnJhcnkvRGV2Z"
  480. @"WxvcGVyL0NvcmVTaW11bGF0b3IvRGV2aWNlcy83Q0ZDMDBCMS05MDc0LTQ2NDgtQTRCMC1CNzk0RkYzRUM2OTEvZGF0"
  481. @"YS9Db250YWluZXJzL0RhdGEvQXBwbGljYXRpb24vOEUyMDcyQjQtNjYzQi00NTdFLTlGQUMtRjVBQUIwMDZBRkZBL0x"
  482. @"pYnJhcnkvQ2FjaGVzL2dvb2dsZS1zZGtzLWV2ZW50cy9ldmVudC0xODI5OTgzMTA3OTY1MDA2NDQwMBAD1QAQAGQAZQ"
  483. @"BmAGcAaADIAMkAawBsgA0TAAABcEWSveMTAAACb3D4vMfXABAAPAA9AD4APwBAAEEAQgBDAM0AuABGANAASIAOgAqAJ"
  484. @"YAigACAKIAJ0wAQAEoASwBMAEYA1YAIgACAJtMAUAAQAFEARgBTANmAAIAHgCdfEORmaWxlOi8vL1VzZXJzL2hhbmV5"
  485. @"bS9MaWJyYXJ5L0RldmVsb3Blci9Db3JlU2ltdWxhdG9yL0RldmljZXMvN0NGQzAwQjEtOTA3NC00NjQ4LUE0QjAtQjc"
  486. @"5NEZGM0VDNjkxL2RhdGEvQ29udGFpbmVycy9EYXRhL0FwcGxpY2F0aW9uLzhFMjA3MkI0LTY2M0ItNDU3RS05RkFDLU"
  487. @"Y1QUFCMDA2QUZGQS9MaWJyYXJ5L0NhY2hlcy9nb29nbGUtc2Rrcy1ldmVudHMvZXZlbnQtMTgyOTg2ODA3Njk5NzU4M"
  488. @"DUxMjHVABAAZABlAGYAZwBoAN0A3gBrAGyADRMAAAFwRZK+lhMAAAJvcPt089cAEAA8AD0APgA/AEAAQQBCAOEA4gC4"
  489. @"AEYA5QBIgA6ALYAqgCKAAIAugAnTABAASgBLAEwARgDqgAiAAIAr0wBQABAAUQBGAFMA7oAAgAeALF8Q5GZpbGU6Ly8"
  490. @"vVXNlcnMvaGFuZXltL0xpYnJhcnkvRGV2ZWxvcGVyL0NvcmVTaW11bGF0b3IvRGV2aWNlcy83Q0ZDMDBCMS05MDc0LT"
  491. @"Q2NDgtQTRCMC1CNzk0RkYzRUM2OTEvZGF0YS9Db250YWluZXJzL0RhdGEvQXBwbGljYXRpb24vOEUyMDcyQjQtNjYzQ"
  492. @"i00NTdFLTlGQUMtRjVBQUIwMDZBRkZBL0xpYnJhcnkvQ2FjaGVzL2dvb2dsZS1zZGtzLWV2ZW50cy9ldmVudC0xODMw"
  493. @"MDUyNTEyMjAxMDQxNTY5NRED6dUAEABkAGUAZgBnAGgA8wD0AGsAbIANEwAAAXBFksI/EwAAAm9xCcJF1wAQADwAPQA"
  494. @"+AD8AQABBAEIA4QD4ALgARgD7AEiADoAtgDCAIoAAgDOACdMAEABKAEsATABGAQCACIAAgDHTAFAAEABRAEYAUwEEgA"
  495. @"CAB4AyXxDkZmlsZTovLy9Vc2Vycy9oYW5leW0vTGlicmFyeS9EZXZlbG9wZXIvQ29yZVNpbXVsYXRvci9EZXZpY2VzL"
  496. @"zdDRkMwMEIxLTkwNzQtNDY0OC1BNEIwLUI3OTRGRjNFQzY5MS9kYXRhL0NvbnRhaW5lcnMvRGF0YS9BcHBsaWNhdGlv"
  497. @"bi84RTIwNzJCNC02NjNCLTQ1N0UtOUZBQy1GNUFBQjAwNkFGRkEvTGlicmFyeS9DYWNoZXMvZ29vZ2xlLXNka3MtZXZ"
  498. @"lbnRzL2V2ZW50LTE4Mjg1ODc4MDYzMDU2ODM3MjUw1QAQAGQAZQBmAGcAaAEIAQkAawBsgA0TAAABcEWSw8MTAAACb3"
  499. @"EPrWTXABAAPAA9AD4APwBAAEEAQgDhAQ0AuABGARAASIAOgC2ANYAigACAOIAJ0wAQAEoASwBMAEYBFYAIgACANtMAU"
  500. @"AAQAFEARgBTARmAAIAHgDdfEORmaWxlOi8vL1VzZXJzL2hhbmV5bS9MaWJyYXJ5L0RldmVsb3Blci9Db3JlU2ltdWxh"
  501. @"dG9yL0RldmljZXMvN0NGQzAwQjEtOTA3NC00NjQ4LUE0QjAtQjc5NEZGM0VDNjkxL2RhdGEvQ29udGFpbmVycy9EYXR"
  502. @"hL0FwcGxpY2F0aW9uLzhFMjA3MkI0LTY2M0ItNDU3RS05RkFDLUY1QUFCMDA2QUZGQS9MaWJyYXJ5L0NhY2hlcy9nb2"
  503. @"9nbGUtc2Rrcy1ldmVudHMvZXZlbnQtMTgyODY0MTI4Njg1OTcwMzcwNzDVABAAZABlAGYAZwBoAR0BHgBrAGyADRMAA"
  504. @"AFwRZLElRMAAAJvcRLh3tcAEAA8AD0APgA/AEAAQQBCAOEBIgBFAEYBJQBIgA6ALYA6gAuAAIA9gAnTABAASgBLAEwA"
  505. @"RgEqgAiAAIA70wBQABAAUQBGAFMBLoAAgAeAPF8Q5GZpbGU6Ly8vVXNlcnMvaGFuZXltL0xpYnJhcnkvRGV2ZWxvcGV"
  506. @"yL0NvcmVTaW11bGF0b3IvRGV2aWNlcy83Q0ZDMDBCMS05MDc0LTQ2NDgtQTRCMC1CNzk0RkYzRUM2OTEvZGF0YS9Db2"
  507. @"50YWluZXJzL0RhdGEvQXBwbGljYXRpb24vOEUyMDcyQjQtNjYzQi00NTdFLTlGQUMtRjVBQUIwMDZBRkZBL0xpYnJhc"
  508. @"nkvQ2FjaGVzL2dvb2dsZS1zZGtzLWV2ZW50cy9ldmVudC0xODI4NTM5MTg3NjI3NTA4Mjk1MdUAEABkAGUAZgBnAGgB"
  509. @"MgEzAGsAbIANEwAAAXBFksZ7EwAAAm9xGktv1wAQADwAPQA+AD8AQABBAEIA4QE3AEUARgE6AEiADoAtgD+AC4AAgEK"
  510. @"ACdMAEABKAEsATABGAT+ACIAAgEDTAFAAEABRAEYAUwFDgACAB4BBXxDkZmlsZTovLy9Vc2Vycy9oYW5leW0vTGlicm"
  511. @"FyeS9EZXZlbG9wZXIvQ29yZVNpbXVsYXRvci9EZXZpY2VzLzdDRkMwMEIxLTkwNzQtNDY0OC1BNEIwLUI3OTRGRjNFQ"
  512. @"zY5MS9kYXRhL0NvbnRhaW5lcnMvRGF0YS9BcHBsaWNhdGlvbi84RTIwNzJCNC02NjNCLTQ1N0UtOUZBQy1GNUFBQjAw"
  513. @"NkFGRkEvTGlicmFyeS9DYWNoZXMvZ29vZ2xlLXNka3MtZXZlbnRzL2V2ZW50LTE4Mjg4MDcyODk3NDM3NDg0MDYy1QA"
  514. @"QAGQAZQBmAGcAaAFHAUgAawBsgA0TAAABcEWSxxwTAAACb3EcwoHXABAAPAA9AD4APwBAAEEAQgDhAUwARQBGAU8ASI"
  515. @"AOgC2ARIALgACAR4AJ0wAQAEoASwBMAEYBVIAIgACARdMAUAAQAFEARgBTAViAAIAHgEZfEORmaWxlOi8vL1VzZXJzL"
  516. @"2hhbmV5bS9MaWJyYXJ5L0RldmVsb3Blci9Db3JlU2ltdWxhdG9yL0RldmljZXMvN0NGQzAwQjEtOTA3NC00NjQ4LUE0"
  517. @"QjAtQjc5NEZGM0VDNjkxL2RhdGEvQ29udGFpbmVycy9EYXRhL0FwcGxpY2F0aW9uLzhFMjA3MkI0LTY2M0ItNDU3RS0"
  518. @"5RkFDLUY1QUFCMDA2QUZGQS9MaWJyYXJ5L0NhY2hlcy9nb29nbGUtc2Rrcy1ldmVudHMvZXZlbnQtMTgyODkwNDg0ND"
  519. @"AxMzk0MDIwNDbVABAAZABlAGYAZwBoAVwBXQBrAGyADRMAAAFwRZLHuBMAAAJvcR8jBdcAEAA8AD0APgA/AEAAQQBCA"
  520. @"OEBYQFiAEYBZABIgA6ALYBJgEyAAIBNgAnTABAASgBLAEwARgFpgAiAAIBK0wBQABAAUQBGAFMBbYAAgAeAS18Q5GZp"
  521. @"bGU6Ly8vVXNlcnMvaGFuZXltL0xpYnJhcnkvRGV2ZWxvcGVyL0NvcmVTaW11bGF0b3IvRGV2aWNlcy83Q0ZDMDBCMS0"
  522. @"5MDc0LTQ2NDgtQTRCMC1CNzk0RkYzRUM2OTEvZGF0YS9Db250YWluZXJzL0RhdGEvQXBwbGljYXRpb24vOEUyMDcyQj"
  523. @"QtNjYzQi00NTdFLTlGQUMtRjVBQUIwMDZBRkZBL0xpYnJhcnkvQ2FjaGVzL2dvb2dsZS1zZGtzLWV2ZW50cy9ldmVud"
  524. @"C0xODI5MDU2NDQ2MDYyMzE0NDA2NRAF1QAQAGQAZQBmAGcAaAFyAXMAawBsgA0TAAABcEWSydcTAAACb3EnbeLXABAA"
  525. @"PAA9AD4APwBAAEEAQgDhAXcBYgBGAXoASIAOgC2AT4BMgACAUoAJ0wAQAEoASwBMAEYBf4AIgACAUNMAUAAQAFEARgB"
  526. @"TAYOAAIAHgFFfEORmaWxlOi8vL1VzZXJzL2hhbmV5bS9MaWJyYXJ5L0RldmVsb3Blci9Db3JlU2ltdWxhdG9yL0Rldm"
  527. @"ljZXMvN0NGQzAwQjEtOTA3NC00NjQ4LUE0QjAtQjc5NEZGM0VDNjkxL2RhdGEvQ29udGFpbmVycy9EYXRhL0FwcGxpY"
  528. @"2F0aW9uLzhFMjA3MkI0LTY2M0ItNDU3RS05RkFDLUY1QUFCMDA2QUZGQS9MaWJyYXJ5L0NhY2hlcy9nb29nbGUtc2Rr"
  529. @"cy1ldmVudHMvZXZlbnQtMTgyOTAzMzI5MDkwMzM5NDQ3ODLVABAAZABlAGYAZwBoAYcBiABrAGyADRMAAAFwRZLKcRM"
  530. @"AAAJvcSnE+9cAEAA8AD0APgA/AEAAQQBCAOEBjAFiAEYBjwBIgA6ALYBUgEyAAIBXgAnTABAASgBLAEwARgGUgAiAAI"
  531. @"BV0wBQABAAUQBGAFMBmIAAgAeAVl8Q5GZpbGU6Ly8vVXNlcnMvaGFuZXltL0xpYnJhcnkvRGV2ZWxvcGVyL0NvcmVTa"
  532. @"W11bGF0b3IvRGV2aWNlcy83Q0ZDMDBCMS05MDc0LTQ2NDgtQTRCMC1CNzk0RkYzRUM2OTEvZGF0YS9Db250YWluZXJz"
  533. @"L0RhdGEvQXBwbGljYXRpb24vOEUyMDcyQjQtNjYzQi00NTdFLTlGQUMtRjVBQUIwMDZBRkZBL0xpYnJhcnkvQ2FjaGV"
  534. @"zL2dvb2dsZS1zZGtzLWV2ZW50cy9ldmVudC0xODI5MDg3MzY1MzgwODczNjM4NNUAEABkAGUAZgBnAGgBnAGdAGsAbI"
  535. @"ANEwAAAXBFkssiEwAAAm9xLHlG1wAQADwAPQA+AD8AQABBAEIA4QGhAaIARgGkAEiADoAtgFmAXIAAgF2ACdMAEABKA"
  536. @"EsATABGAamACIAAgFrTAFAAEABRAEYAUwGtgACAB4BbXxDkZmlsZTovLy9Vc2Vycy9oYW5leW0vTGlicmFyeS9EZXZl"
  537. @"bG9wZXIvQ29yZVNpbXVsYXRvci9EZXZpY2VzLzdDRkMwMEIxLTkwNzQtNDY0OC1BNEIwLUI3OTRGRjNFQzY5MS9kYXR"
  538. @"hL0NvbnRhaW5lcnMvRGF0YS9BcHBsaWNhdGlvbi84RTIwNzJCNC02NjNCLTQ1N0UtOUZBQy1GNUFBQjAwNkFGRkEvTG"
  539. @"licmFyeS9DYWNoZXMvZ29vZ2xlLXNka3MtZXZlbnRzL2V2ZW50LTE4MjkwMTg5OTgwODEwODcxNjk4EALVABAAZABlA"
  540. @"GYAZwBoAbIBswBrAGyADRMAAAFwRZLMtRMAAAJvcTKfNtcAEAA8AD0APgA/AEAAQQBCAOEBtwGiAEYBugBIgA6ALYBf"
  541. @"gFyAAIBigAnTABAASgBLAEwARgG/gAiAAIBg0wBQABAAUQBGAFMBw4AAgAeAYV8Q5GZpbGU6Ly8vVXNlcnMvaGFuZXl"
  542. @"tL0xpYnJhcnkvRGV2ZWxvcGVyL0NvcmVTaW11bGF0b3IvRGV2aWNlcy83Q0ZDMDBCMS05MDc0LTQ2NDgtQTRCMC1CNz"
  543. @"k0RkYzRUM2OTEvZGF0YS9Db250YWluZXJzL0RhdGEvQXBwbGljYXRpb24vOEUyMDcyQjQtNjYzQi00NTdFLTlGQUMtR"
  544. @"jVBQUIwMDZBRkZBL0xpYnJhcnkvQ2FjaGVzL2dvb2dsZS1zZGtzLWV2ZW50cy9ldmVudC0xODI5MzA1NjUzNjM1MjA0"
  545. @"NDYxOdUAEABkAGUAZgBnAGgBxwHIAGsAbIANEwAAAXBFks2BEwAAAm9xNbwL1wAQADwAPQA+AD8AQABBAEIA4QHMAaI"
  546. @"ARgHPAEiADoAtgGSAXIAAgGeACdMAEABKAEsATABGAdSACIAAgGXTAFAAEABRAEYAUwHYgACAB4BmXxDkZmlsZTovLy"
  547. @"9Vc2Vycy9oYW5leW0vTGlicmFyeS9EZXZlbG9wZXIvQ29yZVNpbXVsYXRvci9EZXZpY2VzLzdDRkMwMEIxLTkwNzQtN"
  548. @"DY0OC1BNEIwLUI3OTRGRjNFQzY5MS9kYXRhL0NvbnRhaW5lcnMvRGF0YS9BcHBsaWNhdGlvbi84RTIwNzJCNC02NjNC"
  549. @"LTQ1N0UtOUZBQy1GNUFBQjAwNkFGRkEvTGlicmFyeS9DYWNoZXMvZ29vZ2xlLXNka3MtZXZlbnRzL2V2ZW50LTE4Mjk"
  550. @"zNDI4OTExMjU2MzE1MjY21QAQAGQAZQBmAGcAaAHcAd0AawBsgA0TAAABcEWSziwTAAACb3E4V7/SAFcAWAHfAeBfEB"
  551. @"NOU011dGFibGVPcmRlcmVkU2V0owHhAeIAW18QE05TTXV0YWJsZU9yZGVyZWRTZXRcTlNPcmRlcmVkU2V00wHkAeUAE"
  552. @"AHmAekB7FdOUy5rZXlzWk5TLm9iamVjdHOiAEMA4YAKgC2iAeoB64BqgGyAbdIB5QAQAe4B9aYANAA4ADAAMgA2ADmA"
  553. @"FIAegAOAD4AZgCSAa9IAVwBYAfcB+FxOU011dGFibGVTZXSjAfkB+gBbXE5TTXV0YWJsZVNldFVOU1NldNIB5QAQAfw"
  554. @"B9awAOgApADUALgAvACwANwAoAC0AKwAqADGAWIBegE6APoBDgGOAU4ApgDmANIAvgEiAa9IAVwBYAgsCDF8QE05TTX"
  555. @"V0YWJsZURpY3Rpb25hcnmjAg0CDgBbXxATTlNNdXRhYmxlRGljdGlvbmFyeVxOU0RpY3Rpb25hcnnRABACEIBv0gBXA"
  556. @"FgCEgITXxAXR0RUQ09SVXBsb2FkQ29vcmRpbmF0b3KiAhQAW18QF0dEVENPUlVwbG9hZENvb3JkaW5hdG9y0gBXAFgC"
  557. @"FgIXXUdEVENPUlN0b3JhZ2WiAhgAW11HRFRDT1JTdG9yYWdlAAgAGQAiACwAMQA6AD8AUQBWAFsAXQFCAUgBWQF9AaA"
  558. @"BvwHGAcgBygHMAc4CHQIpAjYCQgJOAlsCZwJ0AoECjQKaAqYCsgK/AssC2ALkAvAC/QL/AwEDAwMFAwcDCQMLAw0DDw"
  559. @"MRAxMDFQMXAxkDGwMdAx8DIQMjA0ADXQN+A5wDxAPoBAgECgQMBA4EEAQSBBQEFgQjBD0EWgRcBF4EYARtBHUEgQSDB"
  560. @"IUEhwVuBXcFggWLBZEFlgWfBagFuwXABdMF2AXbBd0F8gYKBh4GQQZdBl8GaAZxBnoGgwaMBpgGnQapBrIGxgbLBt8G"
  561. @"/Ab+BwAHAgcEBwYHCAcKBxcHGQcbBx0HKgcsBy4HMAgXCCwILgg3CEAIXQhfCGEIYwhlCGcIaQhrCHgIegh8CH4Iiwi"
  562. @"NCI8IkQl4CY0JjwmYCaEJvgnACcIJxAnGCcgJygnMCdkJ2wndCd8J7AnuCfAJ8grZCu4K8Ar5CwILHwshCyMLJQsnCy"
  563. @"kLKwstCzoLPAs+C0ALTQtPC1ELUww6DDwMUQxTDFwMZQyCDIQMhgyIDIoMjAyODJAMnQyfDKEMowywDLIMtAy2DZ0Ns"
  564. @"g20Db0Nxg3jDeUN5w3pDesN7Q3vDfEN/g4ADgIOBA4RDhMOFQ4XDv4PAQ8WDxgPIQ8qD0cPSQ9LD00PTw9RD1MPVQ9i"
  565. @"D2QPZg9oD3UPdw95D3sQYhB3EHkQghCLEKgQqhCsEK4QsBCyELQQthDDEMUQxxDJENYQ2BDaENwRwxHYEdoR4xHsEgk"
  566. @"SCxINEg8SERITEhUSFxIkEiYSKBIqEjcSORI7Ej0TJBM5EzsTRBNNE2oTbBNuE3ATchN0E3YTeBOFE4cTiROLE5gTmh"
  567. @"OcE54UhRSaFJwUpRSuFMsUzRTPFNEU0xTVFNcU2RTmFOgU6hTsFPkU+xT9FP8V5hX7Ff0WBhYPFiwWLhYwFjIWNBY2F"
  568. @"jgWOhZHFkkWSxZNFloWXBZeFmAXRxdJF14XYBdpF3IXjxeRF5MXlReXF5kXmxedF6oXrBeuF7AXvRe/F8EXwxiqGL8Y"
  569. @"wRjKGNMY8BjyGPQY9hj4GPoY/Bj+GQsZDRkPGREZHhkgGSIZJBoLGiAaIhorGjQaURpTGlUaVxpZGlsaXRpfGmwabhp"
  570. @"wGnIafxqBGoMahRtsG24bgxuFG44blxu0G7YbuBu6G7wbvhvAG8IbzxvRG9Mb1RviG+Qb5hvoHM8c5BzmHO8c+B0VHR"
  571. @"cdGR0bHR0dHx0hHSMdMB0yHTQdNh1DHUUdRx1JHjAeRR5HHlAeWR5iHngefx6VHqIerx63HsIexx7JHsse0B7SHtQe1"
  572. @"h7fHuwe7h7wHvIe9B72Hvge+h8DHxAfFx8kHyofMx9MH04fUB9SH1QfVh9YH1ofXB9eH2AfYh9kH2Yfbx+FH4wfoh+v"
  573. @"H7Qfth+/H9kf3h/4IAEgDyAUAAAAAAAAAgIAAAAAAAACGQAAAAAAAAAAAAAAAAAAICI=";
  574. NSData *v1ArchiveData = [[NSData alloc] initWithBase64EncodedString:base64EncodedArchive
  575. options:0];
  576. XCTAssertNotNil(v1ArchiveData);
  577. GDTCORStorage *archiveStorage;
  578. XCTAssertNoThrow(archiveStorage = [NSKeyedUnarchiver unarchiveObjectWithData:v1ArchiveData]);
  579. XCTAssertEqual(archiveStorage.targetToEventSet[@(kGDTCORTargetCCT)].count, 6);
  580. XCTAssertEqual(archiveStorage.targetToEventSet[@(kGDTCORTargetFLL)].count, 12);
  581. XCTAssertEqual(archiveStorage.storedEvents.count, 18);
  582. for (GDTCOREvent *event in archiveStorage.storedEvents) {
  583. XCTAssertNotNil(event.fileURL);
  584. }
  585. XCTAssertNotNil(archiveStorage.uploadCoordinator);
  586. }
  587. @end