Skip to content

Commit

Permalink
Adding and/or fixing unit tests around data migration.
Browse files Browse the repository at this point in the history
One weird thing: the ATDataModelv2.sqlite file appeared to have been corrupt/un-openable.

I added a bunch of verification around the data manager so that internals were visible for unit testing. This was to ensure that things like migration happened, database was deleted, migration failed, etc.
  • Loading branch information
wooster committed Nov 23, 2013
1 parent f23fe05 commit 4e26eae
Show file tree
Hide file tree
Showing 16 changed files with 131 additions and 16 deletions.
36 changes: 27 additions & 9 deletions ApptentiveConnect/ApptentiveConnect.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion ApptentiveConnect/source/ATConnect.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#import <Cocoa/Cocoa.h>
#endif

#define kATConnectVersionString @"1.2.3a"
#define kATConnectVersionString @"1.2.3"

#if TARGET_OS_IPHONE
# define kATConnectPlatformString @"iOS"
Expand Down
3 changes: 3 additions & 0 deletions ApptentiveConnect/source/Persistence/ATDataManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ extern NSString *const ATDataManagerUpgradeCanaryKey;
@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, readonly) BOOL didRemovePersistentStore;
@property (nonatomic, readonly) BOOL didFailToMigrateStore;
@property (nonatomic, readonly) BOOL didMigrateStore;

- (id)initWithModelName:(NSString *)modelName inBundle:(NSBundle *)bundle storagePath:(NSString *)path;

Expand Down
5 changes: 5 additions & 0 deletions ApptentiveConnect/source/Persistence/ATDataManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ @implementation ATDataManager {
NSBundle *bundle;
NSString *supportDirectoryPath;
}
@synthesize didRemovePersistentStore, didFailToMigrateStore, didMigrateStore;

- (id)initWithModelName:(NSString *)aModelName inBundle:(NSBundle *)aBundle storagePath:(NSString *)path {
if ((self = [super init])) {
Expand Down Expand Up @@ -149,7 +150,10 @@ - (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (storeExists && [self isMigrationNecessary:persistentStoreCoordinator]) {
if (![self migrateStoreError:&error]) {
ATLogError(@"Failed to migrate store. Need to start over from scratch: %@", error);
didFailToMigrateStore = YES;
[self removePersistentStore];
} else {
didMigrateStore = YES;
}
}

Expand Down Expand Up @@ -185,6 +189,7 @@ - (void)removePersistentStore {
}
}
[self removeSQLiteSidecarsForPath:sourcePath];
didRemovePersistentStore = YES;
}
@end

Expand Down
101 changes: 95 additions & 6 deletions ApptentiveConnect/tests/ApptentiveMigrationTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#import "ATDataManager.h"

@implementation ApptentiveMigrationTests
- (void)performTestWithStoreName:(NSString *)name {
- (ATDataManager *)dataManagerWithStoreName:(NSString *)name {
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSURL *storeURL = [bundle URLForResource:name withExtension:@"sqlite"];

Expand All @@ -24,18 +24,107 @@ - (void)performTestWithStoreName:(NSString *)name {
[fileManager removeItemAtURL:[dataManager persistentStoreURL] error:nil];
if (![fileManager copyItemAtURL:storeURL toURL:[dataManager persistentStoreURL] error:&error]) {
STFail(@"Unable to copy item: %@", error);
return;
[dataManager release];
return nil;
}

STAssertNotNil([dataManager persistentStoreCoordinator], @"Shouldn't be nil");
return dataManager;
}

- (void)testV1Upgrade {
// For example, we will do the following with a copy of an old data model.
[self performTestWithStoreName:@"ATDataModelv1"];
ATDataManager *dataManager = [self dataManagerWithStoreName:@"ATDataModelv1"];

STAssertTrue([dataManager setupAndVerify], @"Should be able to setup database.");
STAssertNotNil([dataManager persistentStoreCoordinator], @"Shouldn't be nil");
STAssertTrue([dataManager didMigrateStore], @"Should have had to migrate the datastore.");
STAssertFalse([dataManager didFailToMigrateStore], @"Failed to migrate the datastore.");
STAssertFalse([dataManager didRemovePersistentStore], @"Shouldn't have had to delete datastore.");
[dataManager release];
}

- (void)testV2Upgrade {
[self performTestWithStoreName:@"ATDataModelv2"];
ATDataManager *dataManager = [self dataManagerWithStoreName:@"ATDataModelv2"];

STAssertTrue([dataManager setupAndVerify], @"Should be able to setup database.");
STAssertNotNil([dataManager persistentStoreCoordinator], @"Shouldn't be nil");
STAssertTrue([dataManager didMigrateStore], @"Should have had to migrate the datastore.");
STAssertFalse([dataManager didFailToMigrateStore], @"Failed to migrate the datastore.");
STAssertFalse([dataManager didRemovePersistentStore], @"Shouldn't have had to delete datastore.");
[dataManager release];
}

- (void)testCurrentDatabaseVersion {
ATDataManager *dataManager = [self dataManagerWithStoreName:@"ATDataModelv3"];

STAssertTrue([dataManager setupAndVerify], @"Should be able to setup database.");
STAssertNotNil([dataManager persistentStoreCoordinator], @"Shouldn't be nil");
STAssertFalse([dataManager didMigrateStore], @"Should not have had to migrate the datastore.");
STAssertFalse([dataManager didFailToMigrateStore], @"Failed to migrate the datastore.");
STAssertFalse([dataManager didRemovePersistentStore], @"Shouldn't have had to delete datastore.");
[dataManager release];
}

- (ATDataManager *)dataManagerByCopyingSQLFilesInDirectory:(NSString *)directoryName {
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSArray *files = @[@"ATDataModel.sqlite", @"ATDataModel.sqlite-shm", @"ATDataModel.sqlite-wal"];

NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString *path = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;

for (NSString *filename in files) {
NSString *name = [filename stringByDeletingPathExtension];
NSString *extension = [filename pathExtension];

NSString *source = [bundle pathForResource:name ofType:extension inDirectory:directoryName];
NSString *destination = [path stringByAppendingPathComponent:filename];
NSError *error = nil;

[fileManager removeItemAtPath:destination error:nil];
if (![fileManager fileExistsAtPath:source isDirectory:NULL]) {
STFail(@"Unable to find file: %@", source);
}
if (![fileManager copyItemAtPath:source toPath:destination error:&error]) {
STFail(@"Unable to copy item: %@", error);
return nil;
}
}

ATDataManager *dataManager = [[ATDataManager alloc] initWithModelName:@"ATDataModel" inBundle:bundle storagePath:path];
return dataManager;
}

- (void)testV2WALDatabase {
// A valid v2 database, albeit in WAL format.
ATDataManager *dataManager = [self dataManagerByCopyingSQLFilesInDirectory:@"v2WALDatabase"];

STAssertTrue([dataManager setupAndVerify], @"Should be able to setup database.");
STAssertNotNil([dataManager persistentStoreCoordinator], @"Should be able to use existing WAL database.");
STAssertTrue([dataManager didMigrateStore], @"Should have had to migrate the datastore.");
STAssertFalse([dataManager didFailToMigrateStore], @"Should not have failed to migrate the persistent store.");
STAssertFalse([dataManager didRemovePersistentStore], @"Should not have had to delete the persistent store.");
[dataManager release];
}

- (void)testV3WALDatabase {
// A valid v3 database, albeit in WAL format.
ATDataManager *dataManager = [self dataManagerByCopyingSQLFilesInDirectory:@"v3WALDatabase"];

STAssertTrue([dataManager setupAndVerify], @"Should be able to setup database.");
STAssertNotNil([dataManager persistentStoreCoordinator], @"Should be able to use existing WAL database.");
STAssertFalse([dataManager didMigrateStore], @"Should not have had to migrate the datastore.");
STAssertFalse([dataManager didFailToMigrateStore], @"Should not have failed to migrate the persistent store.");
STAssertFalse([dataManager didRemovePersistentStore], @"Should not have had to delete the persistent store.");
[dataManager release];
}

- (void)testCorruptV2DatabaseRecovery {
// A corrupt v3 database in WAL format.
ATDataManager *dataManager = [self dataManagerByCopyingSQLFilesInDirectory:@"v2CorruptDatabase"];

STAssertTrue([dataManager setupAndVerify], @"Should be able to setup database.");
STAssertNotNil([dataManager persistentStoreCoordinator], @"Shouldn't be nil after fixing it.");
STAssertTrue([dataManager didRemovePersistentStore], @"Should have had to delete the persistent store.");
[dataManager release];
}
@end
Binary file modified ApptentiveConnect/tests/data/ATDataModelv2.sqlite
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit 4e26eae

Please sign in to comment.