GDTCORStorageTest.m 37 KB

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