Files
firebase-ios-sdk/GoogleDataTransport/GDTCORTests/Unit/GDTCORFlatFileStorageTest.m
T
Michael Haney 93eb6d8c48 Revert GDTCORStorage changes for now (#5778)
* Revert "Refactor event saving and searching (#5773)"

This reverts commit a61786cbf1.

* Revert "Expand the storage protocol and provide an event iterator (#5742)"

This reverts commit 641d8531a3.

* Revert "Implement helpers for storing and finding events (#5738)"

This reverts commit 9a0aed81b0.
2020-06-09 13:55:15 -07:00

684 lines
39 KiB
Objective-C

/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "GDTCORTests/Unit/GDTCORTestCase.h"
#import "GDTCORLibrary/Private/GDTCORFlatFileStorage.h"
#import "GDTCORLibrary/Private/GDTCORRegistrar_Private.h"
#import "GDTCORLibrary/Public/GDTCOREvent.h"
#import "GDTCORLibrary/Public/GDTCORPlatform.h"
#import "GDTCORLibrary/Public/GDTCORRegistrar.h"
#import "GDTCORTests/Unit/Helpers/GDTCORAssertHelper.h"
#import "GDTCORTests/Unit/Helpers/GDTCORDataObjectTesterClasses.h"
#import "GDTCORTests/Unit/Helpers/GDTCORTestPrioritizer.h"
#import "GDTCORTests/Unit/Helpers/GDTCORTestUploader.h"
#import "GDTCORTests/Common/Fakes/GDTCORUploadCoordinatorFake.h"
#import "GDTCORTests/Common/Categories/GDTCORFlatFileStorage+Testing.h"
#import "GDTCORTests/Common/Categories/GDTCORRegistrar+Testing.h"
static NSInteger target = kGDTCORTargetCCT;
@interface GDTCORFlatFileStorageTest : GDTCORTestCase
/** The test backend implementation. */
@property(nullable, nonatomic) GDTCORTestUploader *testBackend;
/** The test prioritizer implementation. */
@property(nullable, nonatomic) GDTCORTestPrioritizer *testPrioritizer;
/** The uploader fake. */
@property(nonatomic) GDTCORUploadCoordinatorFake *uploaderFake;
@end
@implementation GDTCORFlatFileStorageTest
- (void)setUp {
[super setUp];
self.testBackend = [[GDTCORTestUploader alloc] init];
self.testPrioritizer = [[GDTCORTestPrioritizer alloc] init];
[[GDTCORRegistrar sharedInstance] registerUploader:_testBackend target:target];
[[GDTCORRegistrar sharedInstance] registerPrioritizer:_testPrioritizer target:target];
self.uploaderFake = [[GDTCORUploadCoordinatorFake alloc] init];
[GDTCORFlatFileStorage sharedInstance].uploadCoordinator = self.uploaderFake;
}
- (void)tearDown {
[super tearDown];
dispatch_sync([GDTCORFlatFileStorage sharedInstance].storageQueue, ^{
});
// Destroy these objects before the next test begins.
self.testBackend = nil;
self.testPrioritizer = nil;
[[GDTCORRegistrar sharedInstance] reset];
[[GDTCORFlatFileStorage sharedInstance] reset];
[GDTCORFlatFileStorage sharedInstance].uploadCoordinator =
[GDTCORUploadCoordinator sharedInstance];
self.uploaderFake = nil;
}
/** Tests the singleton pattern. */
- (void)testInit {
XCTAssertEqual([GDTCORFlatFileStorage sharedInstance], [GDTCORFlatFileStorage sharedInstance]);
}
/** Tests storing an event. */
- (void)testStoreEvent {
GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString"];
event.clockSnapshot = [GDTCORClock snapshot];
XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
XCTAssertNoThrow([[GDTCORFlatFileStorage sharedInstance]
storeEvent:event
onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
XCTAssertNotEqualObjects(event.eventID, @0);
XCTAssertNil(error);
[writtenExpectation fulfill];
}]);
[self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
dispatch_sync([GDTCORFlatFileStorage sharedInstance].storageQueue, ^{
XCTAssertEqual([GDTCORFlatFileStorage sharedInstance].storedEvents.count, 1);
XCTAssertEqual([GDTCORFlatFileStorage sharedInstance].targetToEventSet[@(target)].count, 1);
NSURL *eventFile = event.fileURL;
XCTAssertNotNil(eventFile);
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:eventFile.path]);
NSError *error;
XCTAssertTrue([[NSFileManager defaultManager] removeItemAtPath:eventFile.path error:&error]);
XCTAssertNil(error, @"There was an error deleting the eventFile: %@", error);
});
}
/** Tests removing an event. */
- (void)testRemoveEvent {
GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString"];
event.clockSnapshot = [GDTCORClock snapshot];
XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
XCTAssertNoThrow([[GDTCORFlatFileStorage sharedInstance]
storeEvent:event
onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
XCTAssertNotEqualObjects(event.eventID, @0);
XCTAssertNil(error);
[writtenExpectation fulfill];
}]);
[self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
__block NSURL *eventFile;
dispatch_sync([GDTCORFlatFileStorage sharedInstance].storageQueue, ^{
eventFile = event.fileURL;
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:eventFile.path]);
});
NSSet *eventIDs =
[NSSet setWithArray:[[GDTCORFlatFileStorage sharedInstance].storedEvents allKeys]];
[[GDTCORFlatFileStorage sharedInstance] removeEvents:eventIDs];
dispatch_sync([GDTCORFlatFileStorage sharedInstance].storageQueue, ^{
XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:eventFile.path]);
XCTAssertEqual([GDTCORFlatFileStorage sharedInstance].storedEvents.count, 0);
XCTAssertEqual([GDTCORFlatFileStorage sharedInstance].targetToEventSet[@(target)].count, 0);
});
}
/** Tests removing a set of events. */
- (void)testRemoveEvents {
GDTCORFlatFileStorage *storage = [GDTCORFlatFileStorage sharedInstance];
GDTCOREvent *event1 = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
event1.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString1"];
XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
XCTAssertNoThrow([[GDTCORFlatFileStorage sharedInstance]
storeEvent:event1
onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
XCTAssertNotEqualObjects(event1.eventID, @0);
XCTAssertNil(error);
[writtenExpectation fulfill];
}]);
[self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
GDTCOREvent *event2 = [[GDTCOREvent alloc] initWithMappingID:@"100" target:target];
event2.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString2"];
writtenExpectation = [self expectationWithDescription:@"event written"];
XCTAssertNoThrow([[GDTCORFlatFileStorage sharedInstance]
storeEvent:event2
onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
XCTAssertNotEqualObjects(event2.eventID, @0);
XCTAssertNil(error);
[writtenExpectation fulfill];
}]);
[self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
GDTCOREvent *event3 = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
event3.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString3"];
writtenExpectation = [self expectationWithDescription:@"event written"];
XCTAssertNoThrow([[GDTCORFlatFileStorage sharedInstance]
storeEvent:event3
onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
XCTAssertNotEqualObjects(event3.eventID, @0);
XCTAssertNil(error);
[writtenExpectation fulfill];
}]);
[self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
NSSet<NSNumber *> *eventIDSet =
[NSSet setWithObjects:event1.eventID, event2.eventID, event3.eventID, nil];
[storage removeEvents:eventIDSet];
NSSet<GDTCOREvent *> *eventSet = [NSSet setWithObjects:event1, event2, event3, nil];
dispatch_sync(storage.storageQueue, ^{
XCTAssertNil(storage.storedEvents[event1.eventID]);
XCTAssertNil(storage.storedEvents[event2.eventID]);
XCTAssertNil(storage.storedEvents[event3.eventID]);
XCTAssertEqual(storage.targetToEventSet[@(target)].count, 0);
for (GDTCOREvent *event in eventSet) {
XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:event.fileURL.path]);
}
});
}
/** Tests storing a few different events. */
- (void)testStoreMultipleEvents {
GDTCOREvent *event1 = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
event1.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString1"];
XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
XCTAssertNoThrow([[GDTCORFlatFileStorage sharedInstance]
storeEvent:event1
onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
XCTAssertNotEqualObjects(event1.eventID, @0);
XCTAssertNil(error);
[writtenExpectation fulfill];
}]);
[self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
XCTAssertNotNil(event1.fileURL);
GDTCOREvent *event2 = [[GDTCOREvent alloc] initWithMappingID:@"100" target:target];
event2.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString2"];
writtenExpectation = [self expectationWithDescription:@"event written"];
XCTAssertNoThrow([[GDTCORFlatFileStorage sharedInstance]
storeEvent:event2
onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
XCTAssertNotEqualObjects(event2.eventID, @0);
XCTAssertNil(error);
[writtenExpectation fulfill];
}]);
[self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
XCTAssertNotNil(event2.fileURL);
GDTCOREvent *event3 = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
event3.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString3"];
writtenExpectation = [self expectationWithDescription:@"event written"];
XCTAssertNoThrow([[GDTCORFlatFileStorage sharedInstance]
storeEvent:event3
onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
XCTAssertNotEqualObjects(event3.eventID, @0);
XCTAssertNil(error);
[writtenExpectation fulfill];
}]);
[self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
XCTAssertNotNil(event3.fileURL);
dispatch_sync([GDTCORFlatFileStorage sharedInstance].storageQueue, ^{
XCTAssertEqual([GDTCORFlatFileStorage sharedInstance].storedEvents.count, 3);
XCTAssertEqual([GDTCORFlatFileStorage sharedInstance].targetToEventSet[@(target)].count, 3);
NSURL *event1File = event1.fileURL;
XCTAssertNotNil(event1File);
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:event1File.path]);
NSError *error;
XCTAssertTrue([[NSFileManager defaultManager] removeItemAtPath:event1File.path error:&error]);
XCTAssertNil(error, @"There was an error deleting the eventFile: %@", error);
NSURL *event2File = event2.fileURL;
XCTAssertNotNil(event2File);
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:event2File.path]);
error = nil;
XCTAssertTrue([[NSFileManager defaultManager] removeItemAtPath:event2File.path error:&error]);
XCTAssertNil(error, @"There was an error deleting the eventFile: %@", error);
NSURL *event3File = event3.fileURL;
XCTAssertNotNil(event3File);
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:event3File.path]);
error = nil;
XCTAssertTrue([[NSFileManager defaultManager] removeItemAtPath:event3File.path error:&error]);
XCTAssertNil(error, @"There was an error deleting the eventFile: %@", error);
});
}
/** Tests enforcing that a prioritizer does not retain the DataObjectTransportBytes of an event in
* memory.
*/
- (void)testEventDeallocationIsEnforced {
__weak NSData *weakDataObjectTransportBytes;
GDTCOREvent *event;
@autoreleasepool {
event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
weakDataObjectTransportBytes = [event.dataObject transportBytes];
event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString"];
event.clockSnapshot = [GDTCORClock snapshot];
// Store the event and wait for the expectation.
XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
XCTAssertNoThrow([[GDTCORFlatFileStorage sharedInstance]
storeEvent:event
onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
XCTAssertNotEqualObjects(event.eventID, @0);
XCTAssertNil(error);
[writtenExpectation fulfill];
}]);
[self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
}
dispatch_sync([GDTCORFlatFileStorage sharedInstance].storageQueue, ^{
XCTAssertNil(weakDataObjectTransportBytes);
XCTAssertNotNil(event);
});
NSURL *eventFile = event.fileURL;
// This isn't strictly necessary because of the -waitForExpectations above.
dispatch_sync([GDTCORFlatFileStorage sharedInstance].storageQueue, ^{
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:eventFile.path]);
});
// Ensure event was removed.
NSSet *eventIDs =
[NSSet setWithArray:[[GDTCORFlatFileStorage sharedInstance].storedEvents allKeys]];
[[GDTCORFlatFileStorage sharedInstance] removeEvents:eventIDs];
dispatch_sync([GDTCORFlatFileStorage sharedInstance].storageQueue, ^{
XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:eventFile.path]);
XCTAssertEqual([GDTCORFlatFileStorage sharedInstance].storedEvents.count, 0);
XCTAssertEqual([GDTCORFlatFileStorage sharedInstance].targetToEventSet[@(target)].count, 0);
});
}
/** Tests encoding and decoding the storage singleton correctly. */
- (void)testNSSecureCoding {
XCTAssertTrue([GDTCORFlatFileStorage supportsSecureCoding]);
GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
event.clockSnapshot = [GDTCORClock snapshot];
event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString"];
XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
XCTAssertNoThrow([[GDTCORFlatFileStorage sharedInstance]
storeEvent:event
onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
XCTAssertNotEqualObjects(event.eventID, @0);
[writtenExpectation fulfill];
}]);
[self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
event = nil;
__block NSData *storageData;
dispatch_sync([GDTCORFlatFileStorage sharedInstance].storageQueue, ^{
NSError *error;
storageData = GDTCOREncodeArchive([GDTCORFlatFileStorage sharedInstance], nil, &error);
XCTAssertNil(error);
XCTAssertNotNil(storageData);
});
dispatch_sync([GDTCORFlatFileStorage sharedInstance].storageQueue, ^{
XCTAssertEqual([GDTCORFlatFileStorage sharedInstance].storedEvents.count, 1);
});
NSSet *eventIDs =
[NSSet setWithArray:[[GDTCORFlatFileStorage sharedInstance].storedEvents allKeys]];
[[GDTCORFlatFileStorage sharedInstance] removeEvents:eventIDs];
dispatch_sync([GDTCORFlatFileStorage sharedInstance].storageQueue, ^{
XCTAssertEqual([GDTCORFlatFileStorage sharedInstance].storedEvents.count, 0);
});
NSError *error;
GDTCORFlatFileStorage *unarchivedStorage = (GDTCORFlatFileStorage *)GDTCORDecodeArchive(
[GDTCORFlatFileStorage class], nil, storageData, &error);
XCTAssertNil(error);
XCTAssertNotNil(unarchivedStorage);
XCTAssertGreaterThan([unarchivedStorage storedEvents].count, 0);
}
/** Tests encoding and decoding the storage singleton when calling -sharedInstance. */
- (void)testNSSecureCodingWithSharedInstance {
GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString"];
event.clockSnapshot = [GDTCORClock snapshot];
XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
XCTAssertNoThrow([[GDTCORFlatFileStorage sharedInstance]
storeEvent:event
onComplete:^(BOOL wasWritten, NSError *_Nullable error) {
XCTAssertNotEqualObjects(event.eventID, @0);
[writtenExpectation fulfill];
}]);
[self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
event = nil;
__block NSData *storageData;
dispatch_sync([GDTCORFlatFileStorage sharedInstance].storageQueue, ^{
NSError *error;
storageData = GDTCOREncodeArchive([GDTCORFlatFileStorage sharedInstance], nil, &error);
XCTAssertNil(error);
XCTAssertNotNil(storageData);
});
dispatch_sync([GDTCORFlatFileStorage sharedInstance].storageQueue, ^{
XCTAssertNotNil([GDTCORFlatFileStorage sharedInstance].storedEvents);
XCTAssertGreaterThan([GDTCORFlatFileStorage sharedInstance].storedEvents.count, 0);
});
NSSet *eventIDs =
[NSSet setWithArray:[[GDTCORFlatFileStorage sharedInstance].storedEvents allKeys]];
[[GDTCORFlatFileStorage sharedInstance] removeEvents:eventIDs];
dispatch_sync([GDTCORFlatFileStorage sharedInstance].storageQueue, ^{
XCTAssertNotNil([GDTCORFlatFileStorage sharedInstance].storedEvents);
XCTAssertEqual([GDTCORFlatFileStorage sharedInstance].storedEvents.count, 0);
});
NSError *error;
GDTCORFlatFileStorage *unarchivedStorage = (GDTCORFlatFileStorage *)GDTCORDecodeArchive(
[GDTCORFlatFileStorage class], nil, storageData, &error);
XCTAssertNil(error);
XCTAssertNotNil(unarchivedStorage);
XCTAssertNotNil([unarchivedStorage storedEvents]);
XCTAssertGreaterThan([unarchivedStorage storedEvents].count, 0);
}
/** Tests sending a fast priority event causes an upload attempt. */
- (void)testQoSTierFast {
GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:@"testString"];
event.qosTier = GDTCOREventQoSFast;
event.clockSnapshot = [GDTCORClock snapshot];
XCTAssertFalse(self.uploaderFake.forceUploadCalled);
XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
XCTAssertNoThrow([[GDTCORFlatFileStorage sharedInstance]
storeEvent:event
onComplete:^(BOOL wasWritten, NSError *error) {
XCTAssertNotEqualObjects(event.eventID, @0);
XCTAssertNil(error);
[writtenExpectation fulfill];
}]);
[self waitForExpectations:@[ writtenExpectation ] timeout:10.0];
dispatch_sync([GDTCORFlatFileStorage sharedInstance].storageQueue, ^{
XCTAssertTrue(self.uploaderFake.forceUploadCalled);
XCTAssertEqual([GDTCORFlatFileStorage sharedInstance].storedEvents.count, 1);
XCTAssertEqual([GDTCORFlatFileStorage sharedInstance].targetToEventSet[@(target)].count, 1);
NSURL *eventFile = event.fileURL;
XCTAssertNotNil(eventFile);
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:eventFile.path]);
NSError *error;
XCTAssertTrue([[NSFileManager defaultManager] removeItemAtPath:eventFile.path error:&error]);
XCTAssertNil(error, @"There was an error deleting the eventFile: %@", error);
});
}
/** Fuzz tests the storing of events at the same time as a terminate lifecycle notification. This
* test can fail if there's simultaneous access to ivars of GDTCORFlatFileStorage with one access
* being off the storage's queue. The terminate lifecycle event should operate on and flush the
* queue.
*/
- (void)testStoringEventsDuringTerminate {
int numberOfIterations = 1000;
for (int i = 0; i < numberOfIterations; i++) {
NSString *testString = [NSString stringWithFormat:@"testString %d", i];
GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
event.dataObject = [[GDTCORDataObjectTesterSimple alloc] initWithString:testString];
event.clockSnapshot = [GDTCORClock snapshot];
XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
XCTAssertNoThrow([[GDTCORFlatFileStorage sharedInstance]
storeEvent:event
onComplete:^(BOOL wasWritten, NSError *error) {
XCTAssertNotEqualObjects(event.eventID, @0);
[writtenExpectation fulfill];
}]);
[self waitForExpectationsWithTimeout:10 handler:nil];
if (i % 5 == 0) {
NSSet *eventIDs =
[NSSet setWithArray:[[GDTCORFlatFileStorage sharedInstance].storedEvents allKeys]];
[[GDTCORFlatFileStorage sharedInstance] removeEvents:eventIDs];
}
[NSNotificationCenter.defaultCenter
postNotificationName:kGDTCORApplicationWillTerminateNotification
object:nil];
}
}
- (void)testSaveAndLoadLibraryData {
__weak NSData *weakData;
NSString *dataKey = NSStringFromSelector(_cmd);
@autoreleasepool {
NSData *data = [@"test data" dataUsingEncoding:NSUTF8StringEncoding];
weakData = data;
XCTestExpectation *expectation = [self expectationWithDescription:@"storage completion called"];
[[GDTCORFlatFileStorage sharedInstance] storeLibraryData:data
forKey:dataKey
onComplete:^(NSError *_Nullable error) {
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:10.0];
}
XCTAssertNil(weakData);
XCTestExpectation *expectation = [self expectationWithDescription:@"retrieval completion called"];
[[GDTCORFlatFileStorage sharedInstance]
libraryDataForKey:dataKey
onComplete:^(NSData *_Nullable data, NSError *_Nullable error) {
[expectation fulfill];
XCTAssertNil(error);
XCTAssertEqualObjects(@"test data",
[[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding]);
}];
[self waitForExpectations:@[ expectation ] timeout:10.0];
}
- (void)testSavingNilLibraryData {
XCTestExpectation *expectation = [self expectationWithDescription:@"storage completion called"];
[[GDTCORFlatFileStorage sharedInstance] storeLibraryData:[NSData data]
forKey:@"test data key"
onComplete:^(NSError *_Nullable error) {
XCTAssertNotNil(error);
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:10.0];
}
- (void)testSaveAndRemoveLibraryData {
NSString *dataKey = NSStringFromSelector(_cmd);
NSData *data = [@"test data" dataUsingEncoding:NSUTF8StringEncoding];
XCTestExpectation *expectation = [self expectationWithDescription:@"storage completion called"];
[[GDTCORFlatFileStorage sharedInstance] storeLibraryData:data
forKey:dataKey
onComplete:^(NSError *_Nullable error) {
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:10.0];
expectation = [self expectationWithDescription:@"retrieval completion called"];
[[GDTCORFlatFileStorage sharedInstance]
libraryDataForKey:dataKey
onComplete:^(NSData *_Nullable data, NSError *_Nullable error) {
[expectation fulfill];
XCTAssertNil(error);
XCTAssertEqualObjects(@"test data",
[[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding]);
}];
[self waitForExpectations:@[ expectation ] timeout:10.0];
expectation = [self expectationWithDescription:@"removal completion called"];
[[GDTCORFlatFileStorage sharedInstance] removeLibraryDataForKey:dataKey
onComplete:^(NSError *error) {
[expectation fulfill];
XCTAssertNil(error);
}];
[self waitForExpectations:@[ expectation ] timeout:10.0];
expectation = [self expectationWithDescription:@"retrieval completion called"];
[[GDTCORFlatFileStorage sharedInstance]
libraryDataForKey:dataKey
onComplete:^(NSData *_Nullable data, NSError *_Nullable error) {
[expectation fulfill];
XCTAssertNotNil(error);
XCTAssertNil(data);
}];
[self waitForExpectations:@[ expectation ] timeout:10.0];
}
/** Tests migration from v1 of the storage format to v2. */
- (void)testMigrationFromOldVersion {
static NSString *base64EncodedArchive =
@"YnBsaXN0MDDUAAEAAgADAAQABQAGAAcAClgkdmVyc2lvblkkYXJjaGl2ZXJUJHRvcFgkb2JqZWN0cxIAAYagXxAPTlN"
@"LZXllZEFyY2hpdmVy0QAIAAlUcm9vdIABrxBxAAsADAAVADsASQBPAFUAVgBcAGAAYQBiAGMAbQBxAHUAfQCBAIUAhg"
@"CKAJIAlgCaAJsAnwCnAKsArwCwALQAvADAAMQAxQDGAMoA0gDWANoA2wDfAOcA6wDvAPAA8QD1AP0BAQEFAQYBCgESA"
@"RYBGgEbAR8BJwErAS8BMAE0ATwBQAFEAUUBSQFRAVUBWQFaAV4BZgFqAW4BbwFwAXQBfAGAAYQBhQGJAZEBlQGZAZoB"
@"ngGmAaoBrgGvAbABtAG8AcABxAHFAckB0QHVAdkB2gHeAeMB7QH2AfsCCgIPAhECFVUkbnVsbNQADQAOAA8AEAARABI"
@"AEwAUXxAhR0RUQ09SU3RvcmFnZVVwbG9hZENvb3JkaW5hdG9yS2V5XxAgR0RUQ09SU3RvcmFnZVRhcmdldFRvRXZlbn"
@"RTZXRLZXlfEBxHRFRDT1JTdG9yYWdlU3RvcmVkRXZlbnRzS2V5ViRjbGFzc4BugGmAAoBw3xATABYAFwAYABkAGgAbA"
@"BwAHQAeAB8AIAAQACEAIgAjACQAJQAmACcAKAApACoAKwAsAC0ALgAvADAAMQAyADMANAA1ADYANwA4ADkAOltOUy5v"
@"YmplY3QuNlxOUy5vYmplY3QuMTZbTlMub2JqZWN0LjdbTlMub2JqZWN0LjhcTlMub2JqZWN0LjE3W05TLm9iamVjdC4"
@"5XE5TLm9iamVjdC4xMFxOUy5vYmplY3QuMTFbTlMub2JqZWN0LjBcTlMub2JqZWN0LjEyW05TLm9iamVjdC4xW05TLm"
@"9iamVjdC4yXE5TLm9iamVjdC4xM1tOUy5vYmplY3QuM1xOUy5vYmplY3QuMTRbTlMub2JqZWN0LjRbTlMub2JqZWN0L"
@"jVcTlMub2JqZWN0LjE1gCmAXoAvgDSAY4A5gD6AQ4ADgEiAD4BogBSAToAZgFOAHoAkgFjXABAAPAA9AD4APwBAAEEA"
@"QgBDAEQARQBGAEcASF8QGkdEVENPUlN0b3JlZEV2ZW50VGFyZ2V0S2V5XxAeR0RUQ09SU3RvcmVkRXZlbnREYXRhRnV"
@"0dXJlS2V5XxAbR0RUQ09SU3RvcmVkRXZlbnRRb3NUaWVyS2V5XxAlR0RUQ09SU3RvcmVkRXZlbnRjdXN0b21CeXRlc1"
@"BhcmFtc0tleV8QIUdEVENPUlN0b3JlZEV2ZW50Q2xvY2tTbmFwc2hvdEtleV8QHUdEVENPUlN0b3JlZEV2ZW50TWFwc"
@"GluZ0lES2V5gA6ACoAEgAuAAIAMgAnTABAASgBLAEwARgBOXxAXR0RUQ09SRGF0YUZ1dHVyZURhdGFLZXlfEBpHRFRD"
@"T1JEYXRhRnV0dXJlRmlsZVVSTEtleYAIgACABdMAUAAQAFEARgBTAFRXTlMuYmFzZVtOUy5yZWxhdGl2ZYAAgAeABl8"
@"Q5GZpbGU6Ly8vVXNlcnMvaGFuZXltL0xpYnJhcnkvRGV2ZWxvcGVyL0NvcmVTaW11bGF0b3IvRGV2aWNlcy83Q0ZDMD"
@"BCMS05MDc0LTQ2NDgtQTRCMC1CNzk0RkYzRUM2OTEvZGF0YS9Db250YWluZXJzL0RhdGEvQXBwbGljYXRpb24vOEUyM"
@"DcyQjQtNjYzQi00NTdFLTlGQUMtRjVBQUIwMDZBRkZBL0xpYnJhcnkvQ2FjaGVzL2dvb2dsZS1zZGtzLWV2ZW50cy9l"
@"dmVudC0xODI5Nzg1NzIwNjcyMjc5OTkwONIAVwBYAFkAWlokY2xhc3NuYW1lWCRjbGFzc2VzVU5TVVJMogBZAFtYTlN"
@"PYmplY3TSAFcAWABdAF5fEBBHRFRDT1JEYXRhRnV0dXJlogBfAFtfEBBHRFRDT1JEYXRhRnV0dXJlVDEwMTgRA+gQAd"
@"UAEABkAGUAZgBnAGgAaQBqAGsAbF8QFUdEVENPUkNsb2NrVGltZU1pbGxpc18QEUdEVENPUkNsb2NrVXB0aW1lXxAgR"
@"0RUQ09SQ2xvY2tUaW1lem9uZU9mZnNldFNlY29uZHNfEBlHRFRDT1JDbG9ja0tlcm5lbEJvb3RUaW1lgA0TAAABcEWS"
@"uikTAAACb3DqLb8T////////j4ATAAWcIFQ9BZLSAFcAWABuAG9bR0RUQ09SQ2xvY2uiAHAAW1tHRFRDT1JDbG9ja9I"
@"AVwBYAHIAc18QEUdEVENPUlN0b3JlZEV2ZW50ogB0AFtfEBFHRFRDT1JTdG9yZWRFdmVudNcAEAA8AD0APgA/AEAAQQ"
@"BCAEMAeABFAEYAewBIgA6ACoAQgAuAAIATgAnTABAASgBLAEwARgCAgAiAAIAR0wBQABAAUQBGAFMAhIAAgAeAEl8Q5"
@"GZpbGU6Ly8vVXNlcnMvaGFuZXltL0xpYnJhcnkvRGV2ZWxvcGVyL0NvcmVTaW11bGF0b3IvRGV2aWNlcy83Q0ZDMDBC"
@"MS05MDc0LTQ2NDgtQTRCMC1CNzk0RkYzRUM2OTEvZGF0YS9Db250YWluZXJzL0RhdGEvQXBwbGljYXRpb24vOEUyMDc"
@"yQjQtNjYzQi00NTdFLTlGQUMtRjVBQUIwMDZBRkZBL0xpYnJhcnkvQ2FjaGVzL2dvb2dsZS1zZGtzLWV2ZW50cy9ldm"
@"VudC0xODI5NzcwODE2NDQwNDM0OTE4MdUAEABkAGUAZgBnAGgAiACJAGsAbIANEwAAAXBFkrrLEwAAAm9w7KYU1wAQA"
@"DwAPQA+AD8AQABBAEIAQwCNAEUARgCQAEiADoAKgBWAC4AAgBiACdMAEABKAEsATABGAJWACIAAgBbTAFAAEABRAEYA"
@"UwCZgACAB4AXXxDkZmlsZTovLy9Vc2Vycy9oYW5leW0vTGlicmFyeS9EZXZlbG9wZXIvQ29yZVNpbXVsYXRvci9EZXZ"
@"pY2VzLzdDRkMwMEIxLTkwNzQtNDY0OC1BNEIwLUI3OTRGRjNFQzY5MS9kYXRhL0NvbnRhaW5lcnMvRGF0YS9BcHBsaW"
@"NhdGlvbi84RTIwNzJCNC02NjNCLTQ1N0UtOUZBQy1GNUFBQjAwNkFGRkEvTGlicmFyeS9DYWNoZXMvZ29vZ2xlLXNka"
@"3MtZXZlbnRzL2V2ZW50LTE4Mjk1OTM1ODIwNjI5NzUxODU41QAQAGQAZQBmAGcAaACdAJ4AawBsgA0TAAABcEWSu14T"
@"AAACb3Du5R7XABAAPAA9AD4APwBAAEEAQgBDAKIARQBGAKUASIAOgAqAGoALgACAHYAJ0wAQAEoASwBMAEYAqoAIgAC"
@"AG9MAUAAQAFEARgBTAK6AAIAHgBxfEORmaWxlOi8vL1VzZXJzL2hhbmV5bS9MaWJyYXJ5L0RldmVsb3Blci9Db3JlU2"
@"ltdWxhdG9yL0RldmljZXMvN0NGQzAwQjEtOTA3NC00NjQ4LUE0QjAtQjc5NEZGM0VDNjkxL2RhdGEvQ29udGFpbmVyc"
@"y9EYXRhL0FwcGxpY2F0aW9uLzhFMjA3MkI0LTY2M0ItNDU3RS05RkFDLUY1QUFCMDA2QUZGQS9MaWJyYXJ5L0NhY2hl"
@"cy9nb29nbGUtc2Rrcy1ldmVudHMvZXZlbnQtMTgyOTY5NjUyNDM0ODIxNTA3NDPVABAAZABlAGYAZwBoALIAswBrAGy"
@"ADRMAAAFwRZK8FhMAAAJvcPGzA9cAEAA8AD0APgA/AEAAQQBCAEMAtwC4AEYAugBIgA6ACoAfgCKAAIAjgAnTABAASg"
@"BLAEwARgC/gAiAAIAg0wBQABAAUQBGAFMAw4AAgAeAIV8Q5GZpbGU6Ly8vVXNlcnMvaGFuZXltL0xpYnJhcnkvRGV2Z"
@"WxvcGVyL0NvcmVTaW11bGF0b3IvRGV2aWNlcy83Q0ZDMDBCMS05MDc0LTQ2NDgtQTRCMC1CNzk0RkYzRUM2OTEvZGF0"
@"YS9Db250YWluZXJzL0RhdGEvQXBwbGljYXRpb24vOEUyMDcyQjQtNjYzQi00NTdFLTlGQUMtRjVBQUIwMDZBRkZBL0x"
@"pYnJhcnkvQ2FjaGVzL2dvb2dsZS1zZGtzLWV2ZW50cy9ldmVudC0xODI5OTgzMTA3OTY1MDA2NDQwMBAD1QAQAGQAZQ"
@"BmAGcAaADIAMkAawBsgA0TAAABcEWSveMTAAACb3D4vMfXABAAPAA9AD4APwBAAEEAQgBDAM0AuABGANAASIAOgAqAJ"
@"YAigACAKIAJ0wAQAEoASwBMAEYA1YAIgACAJtMAUAAQAFEARgBTANmAAIAHgCdfEORmaWxlOi8vL1VzZXJzL2hhbmV5"
@"bS9MaWJyYXJ5L0RldmVsb3Blci9Db3JlU2ltdWxhdG9yL0RldmljZXMvN0NGQzAwQjEtOTA3NC00NjQ4LUE0QjAtQjc"
@"5NEZGM0VDNjkxL2RhdGEvQ29udGFpbmVycy9EYXRhL0FwcGxpY2F0aW9uLzhFMjA3MkI0LTY2M0ItNDU3RS05RkFDLU"
@"Y1QUFCMDA2QUZGQS9MaWJyYXJ5L0NhY2hlcy9nb29nbGUtc2Rrcy1ldmVudHMvZXZlbnQtMTgyOTg2ODA3Njk5NzU4M"
@"DUxMjHVABAAZABlAGYAZwBoAN0A3gBrAGyADRMAAAFwRZK+lhMAAAJvcPt089cAEAA8AD0APgA/AEAAQQBCAOEA4gC4"
@"AEYA5QBIgA6ALYAqgCKAAIAugAnTABAASgBLAEwARgDqgAiAAIAr0wBQABAAUQBGAFMA7oAAgAeALF8Q5GZpbGU6Ly8"
@"vVXNlcnMvaGFuZXltL0xpYnJhcnkvRGV2ZWxvcGVyL0NvcmVTaW11bGF0b3IvRGV2aWNlcy83Q0ZDMDBCMS05MDc0LT"
@"Q2NDgtQTRCMC1CNzk0RkYzRUM2OTEvZGF0YS9Db250YWluZXJzL0RhdGEvQXBwbGljYXRpb24vOEUyMDcyQjQtNjYzQ"
@"i00NTdFLTlGQUMtRjVBQUIwMDZBRkZBL0xpYnJhcnkvQ2FjaGVzL2dvb2dsZS1zZGtzLWV2ZW50cy9ldmVudC0xODMw"
@"MDUyNTEyMjAxMDQxNTY5NRED6dUAEABkAGUAZgBnAGgA8wD0AGsAbIANEwAAAXBFksI/EwAAAm9xCcJF1wAQADwAPQA"
@"+AD8AQABBAEIA4QD4ALgARgD7AEiADoAtgDCAIoAAgDOACdMAEABKAEsATABGAQCACIAAgDHTAFAAEABRAEYAUwEEgA"
@"CAB4AyXxDkZmlsZTovLy9Vc2Vycy9oYW5leW0vTGlicmFyeS9EZXZlbG9wZXIvQ29yZVNpbXVsYXRvci9EZXZpY2VzL"
@"zdDRkMwMEIxLTkwNzQtNDY0OC1BNEIwLUI3OTRGRjNFQzY5MS9kYXRhL0NvbnRhaW5lcnMvRGF0YS9BcHBsaWNhdGlv"
@"bi84RTIwNzJCNC02NjNCLTQ1N0UtOUZBQy1GNUFBQjAwNkFGRkEvTGlicmFyeS9DYWNoZXMvZ29vZ2xlLXNka3MtZXZ"
@"lbnRzL2V2ZW50LTE4Mjg1ODc4MDYzMDU2ODM3MjUw1QAQAGQAZQBmAGcAaAEIAQkAawBsgA0TAAABcEWSw8MTAAACb3"
@"EPrWTXABAAPAA9AD4APwBAAEEAQgDhAQ0AuABGARAASIAOgC2ANYAigACAOIAJ0wAQAEoASwBMAEYBFYAIgACANtMAU"
@"AAQAFEARgBTARmAAIAHgDdfEORmaWxlOi8vL1VzZXJzL2hhbmV5bS9MaWJyYXJ5L0RldmVsb3Blci9Db3JlU2ltdWxh"
@"dG9yL0RldmljZXMvN0NGQzAwQjEtOTA3NC00NjQ4LUE0QjAtQjc5NEZGM0VDNjkxL2RhdGEvQ29udGFpbmVycy9EYXR"
@"hL0FwcGxpY2F0aW9uLzhFMjA3MkI0LTY2M0ItNDU3RS05RkFDLUY1QUFCMDA2QUZGQS9MaWJyYXJ5L0NhY2hlcy9nb2"
@"9nbGUtc2Rrcy1ldmVudHMvZXZlbnQtMTgyODY0MTI4Njg1OTcwMzcwNzDVABAAZABlAGYAZwBoAR0BHgBrAGyADRMAA"
@"AFwRZLElRMAAAJvcRLh3tcAEAA8AD0APgA/AEAAQQBCAOEBIgBFAEYBJQBIgA6ALYA6gAuAAIA9gAnTABAASgBLAEwA"
@"RgEqgAiAAIA70wBQABAAUQBGAFMBLoAAgAeAPF8Q5GZpbGU6Ly8vVXNlcnMvaGFuZXltL0xpYnJhcnkvRGV2ZWxvcGV"
@"yL0NvcmVTaW11bGF0b3IvRGV2aWNlcy83Q0ZDMDBCMS05MDc0LTQ2NDgtQTRCMC1CNzk0RkYzRUM2OTEvZGF0YS9Db2"
@"50YWluZXJzL0RhdGEvQXBwbGljYXRpb24vOEUyMDcyQjQtNjYzQi00NTdFLTlGQUMtRjVBQUIwMDZBRkZBL0xpYnJhc"
@"nkvQ2FjaGVzL2dvb2dsZS1zZGtzLWV2ZW50cy9ldmVudC0xODI4NTM5MTg3NjI3NTA4Mjk1MdUAEABkAGUAZgBnAGgB"
@"MgEzAGsAbIANEwAAAXBFksZ7EwAAAm9xGktv1wAQADwAPQA+AD8AQABBAEIA4QE3AEUARgE6AEiADoAtgD+AC4AAgEK"
@"ACdMAEABKAEsATABGAT+ACIAAgEDTAFAAEABRAEYAUwFDgACAB4BBXxDkZmlsZTovLy9Vc2Vycy9oYW5leW0vTGlicm"
@"FyeS9EZXZlbG9wZXIvQ29yZVNpbXVsYXRvci9EZXZpY2VzLzdDRkMwMEIxLTkwNzQtNDY0OC1BNEIwLUI3OTRGRjNFQ"
@"zY5MS9kYXRhL0NvbnRhaW5lcnMvRGF0YS9BcHBsaWNhdGlvbi84RTIwNzJCNC02NjNCLTQ1N0UtOUZBQy1GNUFBQjAw"
@"NkFGRkEvTGlicmFyeS9DYWNoZXMvZ29vZ2xlLXNka3MtZXZlbnRzL2V2ZW50LTE4Mjg4MDcyODk3NDM3NDg0MDYy1QA"
@"QAGQAZQBmAGcAaAFHAUgAawBsgA0TAAABcEWSxxwTAAACb3EcwoHXABAAPAA9AD4APwBAAEEAQgDhAUwARQBGAU8ASI"
@"AOgC2ARIALgACAR4AJ0wAQAEoASwBMAEYBVIAIgACARdMAUAAQAFEARgBTAViAAIAHgEZfEORmaWxlOi8vL1VzZXJzL"
@"2hhbmV5bS9MaWJyYXJ5L0RldmVsb3Blci9Db3JlU2ltdWxhdG9yL0RldmljZXMvN0NGQzAwQjEtOTA3NC00NjQ4LUE0"
@"QjAtQjc5NEZGM0VDNjkxL2RhdGEvQ29udGFpbmVycy9EYXRhL0FwcGxpY2F0aW9uLzhFMjA3MkI0LTY2M0ItNDU3RS0"
@"5RkFDLUY1QUFCMDA2QUZGQS9MaWJyYXJ5L0NhY2hlcy9nb29nbGUtc2Rrcy1ldmVudHMvZXZlbnQtMTgyODkwNDg0ND"
@"AxMzk0MDIwNDbVABAAZABlAGYAZwBoAVwBXQBrAGyADRMAAAFwRZLHuBMAAAJvcR8jBdcAEAA8AD0APgA/AEAAQQBCA"
@"OEBYQFiAEYBZABIgA6ALYBJgEyAAIBNgAnTABAASgBLAEwARgFpgAiAAIBK0wBQABAAUQBGAFMBbYAAgAeAS18Q5GZp"
@"bGU6Ly8vVXNlcnMvaGFuZXltL0xpYnJhcnkvRGV2ZWxvcGVyL0NvcmVTaW11bGF0b3IvRGV2aWNlcy83Q0ZDMDBCMS0"
@"5MDc0LTQ2NDgtQTRCMC1CNzk0RkYzRUM2OTEvZGF0YS9Db250YWluZXJzL0RhdGEvQXBwbGljYXRpb24vOEUyMDcyQj"
@"QtNjYzQi00NTdFLTlGQUMtRjVBQUIwMDZBRkZBL0xpYnJhcnkvQ2FjaGVzL2dvb2dsZS1zZGtzLWV2ZW50cy9ldmVud"
@"C0xODI5MDU2NDQ2MDYyMzE0NDA2NRAF1QAQAGQAZQBmAGcAaAFyAXMAawBsgA0TAAABcEWSydcTAAACb3EnbeLXABAA"
@"PAA9AD4APwBAAEEAQgDhAXcBYgBGAXoASIAOgC2AT4BMgACAUoAJ0wAQAEoASwBMAEYBf4AIgACAUNMAUAAQAFEARgB"
@"TAYOAAIAHgFFfEORmaWxlOi8vL1VzZXJzL2hhbmV5bS9MaWJyYXJ5L0RldmVsb3Blci9Db3JlU2ltdWxhdG9yL0Rldm"
@"ljZXMvN0NGQzAwQjEtOTA3NC00NjQ4LUE0QjAtQjc5NEZGM0VDNjkxL2RhdGEvQ29udGFpbmVycy9EYXRhL0FwcGxpY"
@"2F0aW9uLzhFMjA3MkI0LTY2M0ItNDU3RS05RkFDLUY1QUFCMDA2QUZGQS9MaWJyYXJ5L0NhY2hlcy9nb29nbGUtc2Rr"
@"cy1ldmVudHMvZXZlbnQtMTgyOTAzMzI5MDkwMzM5NDQ3ODLVABAAZABlAGYAZwBoAYcBiABrAGyADRMAAAFwRZLKcRM"
@"AAAJvcSnE+9cAEAA8AD0APgA/AEAAQQBCAOEBjAFiAEYBjwBIgA6ALYBUgEyAAIBXgAnTABAASgBLAEwARgGUgAiAAI"
@"BV0wBQABAAUQBGAFMBmIAAgAeAVl8Q5GZpbGU6Ly8vVXNlcnMvaGFuZXltL0xpYnJhcnkvRGV2ZWxvcGVyL0NvcmVTa"
@"W11bGF0b3IvRGV2aWNlcy83Q0ZDMDBCMS05MDc0LTQ2NDgtQTRCMC1CNzk0RkYzRUM2OTEvZGF0YS9Db250YWluZXJz"
@"L0RhdGEvQXBwbGljYXRpb24vOEUyMDcyQjQtNjYzQi00NTdFLTlGQUMtRjVBQUIwMDZBRkZBL0xpYnJhcnkvQ2FjaGV"
@"zL2dvb2dsZS1zZGtzLWV2ZW50cy9ldmVudC0xODI5MDg3MzY1MzgwODczNjM4NNUAEABkAGUAZgBnAGgBnAGdAGsAbI"
@"ANEwAAAXBFkssiEwAAAm9xLHlG1wAQADwAPQA+AD8AQABBAEIA4QGhAaIARgGkAEiADoAtgFmAXIAAgF2ACdMAEABKA"
@"EsATABGAamACIAAgFrTAFAAEABRAEYAUwGtgACAB4BbXxDkZmlsZTovLy9Vc2Vycy9oYW5leW0vTGlicmFyeS9EZXZl"
@"bG9wZXIvQ29yZVNpbXVsYXRvci9EZXZpY2VzLzdDRkMwMEIxLTkwNzQtNDY0OC1BNEIwLUI3OTRGRjNFQzY5MS9kYXR"
@"hL0NvbnRhaW5lcnMvRGF0YS9BcHBsaWNhdGlvbi84RTIwNzJCNC02NjNCLTQ1N0UtOUZBQy1GNUFBQjAwNkFGRkEvTG"
@"licmFyeS9DYWNoZXMvZ29vZ2xlLXNka3MtZXZlbnRzL2V2ZW50LTE4MjkwMTg5OTgwODEwODcxNjk4EALVABAAZABlA"
@"GYAZwBoAbIBswBrAGyADRMAAAFwRZLMtRMAAAJvcTKfNtcAEAA8AD0APgA/AEAAQQBCAOEBtwGiAEYBugBIgA6ALYBf"
@"gFyAAIBigAnTABAASgBLAEwARgG/gAiAAIBg0wBQABAAUQBGAFMBw4AAgAeAYV8Q5GZpbGU6Ly8vVXNlcnMvaGFuZXl"
@"tL0xpYnJhcnkvRGV2ZWxvcGVyL0NvcmVTaW11bGF0b3IvRGV2aWNlcy83Q0ZDMDBCMS05MDc0LTQ2NDgtQTRCMC1CNz"
@"k0RkYzRUM2OTEvZGF0YS9Db250YWluZXJzL0RhdGEvQXBwbGljYXRpb24vOEUyMDcyQjQtNjYzQi00NTdFLTlGQUMtR"
@"jVBQUIwMDZBRkZBL0xpYnJhcnkvQ2FjaGVzL2dvb2dsZS1zZGtzLWV2ZW50cy9ldmVudC0xODI5MzA1NjUzNjM1MjA0"
@"NDYxOdUAEABkAGUAZgBnAGgBxwHIAGsAbIANEwAAAXBFks2BEwAAAm9xNbwL1wAQADwAPQA+AD8AQABBAEIA4QHMAaI"
@"ARgHPAEiADoAtgGSAXIAAgGeACdMAEABKAEsATABGAdSACIAAgGXTAFAAEABRAEYAUwHYgACAB4BmXxDkZmlsZTovLy"
@"9Vc2Vycy9oYW5leW0vTGlicmFyeS9EZXZlbG9wZXIvQ29yZVNpbXVsYXRvci9EZXZpY2VzLzdDRkMwMEIxLTkwNzQtN"
@"DY0OC1BNEIwLUI3OTRGRjNFQzY5MS9kYXRhL0NvbnRhaW5lcnMvRGF0YS9BcHBsaWNhdGlvbi84RTIwNzJCNC02NjNC"
@"LTQ1N0UtOUZBQy1GNUFBQjAwNkFGRkEvTGlicmFyeS9DYWNoZXMvZ29vZ2xlLXNka3MtZXZlbnRzL2V2ZW50LTE4Mjk"
@"zNDI4OTExMjU2MzE1MjY21QAQAGQAZQBmAGcAaAHcAd0AawBsgA0TAAABcEWSziwTAAACb3E4V7/SAFcAWAHfAeBfEB"
@"NOU011dGFibGVPcmRlcmVkU2V0owHhAeIAW18QE05TTXV0YWJsZU9yZGVyZWRTZXRcTlNPcmRlcmVkU2V00wHkAeUAE"
@"AHmAekB7FdOUy5rZXlzWk5TLm9iamVjdHOiAEMA4YAKgC2iAeoB64BqgGyAbdIB5QAQAe4B9aYANAA4ADAAMgA2ADmA"
@"FIAegAOAD4AZgCSAa9IAVwBYAfcB+FxOU011dGFibGVTZXSjAfkB+gBbXE5TTXV0YWJsZVNldFVOU1NldNIB5QAQAfw"
@"B9awAOgApADUALgAvACwANwAoAC0AKwAqADGAWIBegE6APoBDgGOAU4ApgDmANIAvgEiAa9IAVwBYAgsCDF8QE05TTX"
@"V0YWJsZURpY3Rpb25hcnmjAg0CDgBbXxATTlNNdXRhYmxlRGljdGlvbmFyeVxOU0RpY3Rpb25hcnnRABACEIBv0gBXA"
@"FgCEgITXxAXR0RUQ09SVXBsb2FkQ29vcmRpbmF0b3KiAhQAW18QF0dEVENPUlVwbG9hZENvb3JkaW5hdG9y0gBXAFgC"
@"FgIXXUdEVENPUlN0b3JhZ2WiAhgAW11HRFRDT1JTdG9yYWdlAAgAGQAiACwAMQA6AD8AUQBWAFsAXQFCAUgBWQF9AaA"
@"BvwHGAcgBygHMAc4CHQIpAjYCQgJOAlsCZwJ0AoECjQKaAqYCsgK/AssC2ALkAvAC/QL/AwEDAwMFAwcDCQMLAw0DDw"
@"MRAxMDFQMXAxkDGwMdAx8DIQMjA0ADXQN+A5wDxAPoBAgECgQMBA4EEAQSBBQEFgQjBD0EWgRcBF4EYARtBHUEgQSDB"
@"IUEhwVuBXcFggWLBZEFlgWfBagFuwXABdMF2AXbBd0F8gYKBh4GQQZdBl8GaAZxBnoGgwaMBpgGnQapBrIGxgbLBt8G"
@"/Ab+BwAHAgcEBwYHCAcKBxcHGQcbBx0HKgcsBy4HMAgXCCwILgg3CEAIXQhfCGEIYwhlCGcIaQhrCHgIegh8CH4Iiwi"
@"NCI8IkQl4CY0JjwmYCaEJvgnACcIJxAnGCcgJygnMCdkJ2wndCd8J7AnuCfAJ8grZCu4K8Ar5CwILHwshCyMLJQsnCy"
@"kLKwstCzoLPAs+C0ALTQtPC1ELUww6DDwMUQxTDFwMZQyCDIQMhgyIDIoMjAyODJAMnQyfDKEMowywDLIMtAy2DZ0Ns"
@"g20Db0Nxg3jDeUN5w3pDesN7Q3vDfEN/g4ADgIOBA4RDhMOFQ4XDv4PAQ8WDxgPIQ8qD0cPSQ9LD00PTw9RD1MPVQ9i"
@"D2QPZg9oD3UPdw95D3sQYhB3EHkQghCLEKgQqhCsEK4QsBCyELQQthDDEMUQxxDJENYQ2BDaENwRwxHYEdoR4xHsEgk"
@"SCxINEg8SERITEhUSFxIkEiYSKBIqEjcSORI7Ej0TJBM5EzsTRBNNE2oTbBNuE3ATchN0E3YTeBOFE4cTiROLE5gTmh"
@"OcE54UhRSaFJwUpRSuFMsUzRTPFNEU0xTVFNcU2RTmFOgU6hTsFPkU+xT9FP8V5hX7Ff0WBhYPFiwWLhYwFjIWNBY2F"
@"jgWOhZHFkkWSxZNFloWXBZeFmAXRxdJF14XYBdpF3IXjxeRF5MXlReXF5kXmxedF6oXrBeuF7AXvRe/F8EXwxiqGL8Y"
@"wRjKGNMY8BjyGPQY9hj4GPoY/Bj+GQsZDRkPGREZHhkgGSIZJBoLGiAaIhorGjQaURpTGlUaVxpZGlsaXRpfGmwabhp"
@"wGnIafxqBGoMahRtsG24bgxuFG44blxu0G7YbuBu6G7wbvhvAG8IbzxvRG9Mb1RviG+Qb5hvoHM8c5BzmHO8c+B0VHR"
@"cdGR0bHR0dHx0hHSMdMB0yHTQdNh1DHUUdRx1JHjAeRR5HHlAeWR5iHngefx6VHqIerx63HsIexx7JHsse0B7SHtQe1"
@"h7fHuwe7h7wHvIe9B72Hvge+h8DHxAfFx8kHyofMx9MH04fUB9SH1QfVh9YH1ofXB9eH2AfYh9kH2Yfbx+FH4wfoh+v"
@"H7Qfth+/H9kf3h/4IAEgDyAUAAAAAAAAAgIAAAAAAAACGQAAAAAAAAAAAAAAAAAAICI=";
NSData *v1ArchiveData = [[NSData alloc] initWithBase64EncodedString:base64EncodedArchive
options:0];
XCTAssertNotNil(v1ArchiveData);
NSError *error;
GDTCORFlatFileStorage *archiveStorage = (GDTCORFlatFileStorage *)GDTCORDecodeArchive(
[GDTCORFlatFileStorage class], nil, v1ArchiveData, &error);
XCTAssertNil(error);
XCTAssertNotNil(archiveStorage);
XCTAssertEqual(archiveStorage.targetToEventSet[@(kGDTCORTargetCCT)].count, 6);
XCTAssertEqual(archiveStorage.targetToEventSet[@(kGDTCORTargetFLL)].count, 12);
XCTAssertEqual(archiveStorage.storedEvents.count, 18);
XCTAssertNotNil(archiveStorage.uploadCoordinator);
}
@end