Files
RestKit/Tests/Logic/CoreData/RKManagedObjectStoreTest.m

567 lines
34 KiB
Objective-C

//
// RKManagedObjectStoreTest.m
// RestKit
//
// Created by Blake Watters on 7/2/11.
// Copyright (c) 2009-2012 RestKit. All rights reserved.
//
// 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 "RKTestEnvironment.h"
#import "RKHuman.h"
#import "RKPathUtilities.h"
#import "RKSearchIndexer.h"
// TODO: Does this become `RKManagedObjectStore managedObjectModelWithName:version:inBundle:` ??? URLForManagedObjectModel
static NSURL *RKURLForManagedObjectModelWithNameAtVersion(NSString *modelName, NSUInteger version)
{
NSString *modelNameAndVersion = (version == 1) ? modelName : [NSString stringWithFormat:@"%@ %ld", modelName, (unsigned long) version];
return [[RKTestFixture fixtureBundle] URLForResource:[NSString stringWithFormat:@"%@.momd/%@", modelName, modelNameAndVersion] withExtension:@"mom"];
}
static NSManagedObjectModel *RKManagedObjectModelWithNameAtVersion(NSString *modelName, NSUInteger version)
{
NSURL *URL = RKURLForManagedObjectModelWithNameAtVersion(modelName, version);
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:URL];
return model;
}
@interface RKManagedObjectStoreTest : RKTestCase
@end
@implementation RKManagedObjectStoreTest
- (void)setUp
{
[RKTestFactory setUp];
// Delete any sqlite files in the app data directory
NSError *error;
NSArray *paths = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:RKApplicationDataDirectory() error:&error];
for (NSString *path in paths) {
if ([[path pathExtension] isEqualToString:@"sqlite"]) {
NSString *fullPath = [RKApplicationDataDirectory() stringByAppendingPathComponent:path];
BOOL success = [[NSFileManager defaultManager] removeItemAtPath:fullPath error:&error];
NSAssert(success, @"Failed to remove SQLite file at path: %@", fullPath);
}
}
}
- (void)tearDown
{
[RKTestFactory tearDown];
}
- (void)testAdditionOfSQLiteStoreRetainsPathOfSeedDatabase
{
// Create a store with a SQLite database to use as our store
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *seedStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSString *seedPath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"Seed.sqlite"];
NSError *error;
NSPersistentStore *persistentStore = [seedStore addSQLitePersistentStoreAtPath:seedPath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
expect(persistentStore).notTo.beNil();
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:seedPath];
expect(fileExists).to.beTruthy();
// Create a secondary store using the seed
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"Test.sqlite"];
persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:seedPath withConfiguration:nil options:nil error:&error];
expect(persistentStore).notTo.beNil();
fileExists = [[NSFileManager defaultManager] fileExistsAtPath:storePath];
expect(fileExists).to.beTruthy();
// Check that the store has a reference to the seed file option
NSString *seedDatabasePath = [[persistentStore options] valueForKey:RKSQLitePersistentStoreSeedDatabasePathOption];
expect(seedDatabasePath).to.equal(seedPath);
}
- (void)testAddingPersistentSQLiteStoreFromSeedDatabase
{
// Create a store with an object to serve as our seed database
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *seedStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSError *error;
NSString *seedPath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"Seed.sqlite"];
NSPersistentStore *seedPersistentStore = [seedStore addSQLitePersistentStoreAtPath:seedPath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
assertThat(seedPersistentStore, is(notNilValue()));
[seedStore createManagedObjectContexts];
RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:seedStore.persistentStoreManagedObjectContext];
human.name = @"Blake";
BOOL success = [seedStore.mainQueueManagedObjectContext saveToPersistentStore:&error];
assertThatBool(success, is(equalToBool(YES)));
NSManagedObjectID *seedObjectID = human.objectID;
// Create a secondary store using the first store as the seed
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"SeededStore.sqlite"];
RKManagedObjectStore *seededStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSPersistentStore *persistentStore = [seededStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:seedPath withConfiguration:nil options:nil error:&error];
assertThat(persistentStore, is(notNilValue()));
[seededStore createManagedObjectContexts];
// Get back the seeded object
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name = %@", @"Blake"];
NSArray *array = [seededStore.persistentStoreManagedObjectContext executeFetchRequest:fetchRequest error:&error];
assertThat(array, isNot(empty()));
RKHuman *seededHuman = [array objectAtIndex:0];
assertThat([[seededHuman.objectID URIRepresentation] URLByDeletingLastPathComponent], is(equalTo([[seedObjectID URIRepresentation] URLByDeletingLastPathComponent])));
}
- (void)testResetPersistentStoresRecreatesInMemoryStoreThusDeletingAllManagedObjects
{
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSError *error;
NSPersistentStore *persistentStore = [managedObjectStore addInMemoryPersistentStore:&error];
assertThat(persistentStore, is(notNilValue()));
[managedObjectStore createManagedObjectContexts];
RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
human.name = @"Blake";
BOOL success = [managedObjectStore.mainQueueManagedObjectContext saveToPersistentStore:&error];
assertThatBool(success, is(equalToBool(YES)));
// Spin the run loop to allow the did save notifications to propogate
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
success = [managedObjectStore resetPersistentStores:&error];
assertThatBool(success, is(equalToBool(YES)));
// Check that the persistent store has changed
NSPersistentStore *newPersistentStore = [managedObjectStore.persistentStoreCoordinator.persistentStores objectAtIndex:0];
assertThat(newPersistentStore, isNot(equalTo(persistentStore)));
// Check that the object is gone
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name = %@", @"Blake"];
NSArray *array = [managedObjectStore.mainQueueManagedObjectContext executeFetchRequest:fetchRequest error:&error];
assertThat(array, is(empty()));
}
- (void)testResetPersistentStoresRecreatesSQLiteStoreThusDeletingAllManagedObjects
{
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSError *error;
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"Test.sqlite"];
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
assertThat(persistentStore, is(notNilValue()));
[managedObjectStore createManagedObjectContexts];
RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
human.name = @"Blake";
BOOL success = [managedObjectStore.mainQueueManagedObjectContext saveToPersistentStore:&error];
assertThatBool(success, is(equalToBool(YES)));
// Spin the run loop to allow the did save notifications to propogate
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
success = [managedObjectStore resetPersistentStores:&error];
assertThatBool(success, is(equalToBool(YES)));
// Check that the object is gone
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name = %@", @"Blake"];
NSArray *array = [managedObjectStore.mainQueueManagedObjectContext executeFetchRequest:fetchRequest error:&error];
assertThat(array, is(empty()));
}
- (void)testResetPersistentStoreRecreatesSQLiteStoreThusRecreatingTheStoreFileOnDisk
{
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSError *error;
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"Test.sqlite"];
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
assertThat(persistentStore, is(notNilValue()));
[managedObjectStore createManagedObjectContexts];
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:storePath error:&error];
assertThat(attributes, is(notNilValue()));
NSDate *modificationDate = [attributes objectForKey:NSFileModificationDate];
BOOL success = [managedObjectStore resetPersistentStores:&error];
assertThatBool(success, is(equalToBool(YES)));
// Check that the persistent store has changed
NSPersistentStore *newPersistentStore = [managedObjectStore.persistentStoreCoordinator.persistentStores objectAtIndex:0];
assertThat(newPersistentStore, isNot(equalTo(persistentStore)));
attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:storePath error:&error];
assertThat(attributes, is(notNilValue()));
NSDate *newModificationDate = [attributes objectForKey:NSFileModificationDate];
NSDate *laterDate = [modificationDate laterDate:newModificationDate];
assertThat(laterDate, is(equalTo(newModificationDate)));
}
- (void)testResetPersistentStoreForSQLiteStoreSeededWithDatabaseReclonesTheSeedDatabaseToTheStoreLocation
{
// Create a store with an object to serve as our seed database
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *seedStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSError *error;
NSString *seedPath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"Seed.sqlite"];
NSPersistentStore *seedPersistentStore = [seedStore addSQLitePersistentStoreAtPath:seedPath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
assertThat(seedPersistentStore, is(notNilValue()));
[seedStore createManagedObjectContexts];
RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:seedStore.persistentStoreManagedObjectContext];
human.name = @"Blake";
BOOL success = [seedStore.mainQueueManagedObjectContext saveToPersistentStore:&error];
assertThatBool(success, is(equalToBool(YES)));
NSManagedObjectID *seedObjectID = human.objectID;
// Create a secondary store using the first store as the seed
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"SeededStore.sqlite"];
RKManagedObjectStore *seededStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSPersistentStore *persistentStore = [seededStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:seedPath withConfiguration:nil options:nil error:&error];
assertThat(persistentStore, is(notNilValue()));
[seededStore createManagedObjectContexts];
// Create a second object in the seeded store
RKHuman *human2 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:seedStore.persistentStoreManagedObjectContext];
human2.name = @"Sarah";
success = [seededStore.mainQueueManagedObjectContext saveToPersistentStore:&error];
assertThatBool(success, is(equalToBool(YES)));
// Reset the persistent stores, causing the seed database to be recopied and orphaning the second object
success = [seededStore resetPersistentStores:&error];
assertThatBool(success, is(equalToBool(YES)));
// Get back the seeded object and check against the seeded object ID
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name = %@", @"Blake"];
NSArray *array = [seededStore.persistentStoreManagedObjectContext executeFetchRequest:fetchRequest error:&error];
assertThat(array, isNot(empty()));
RKHuman *seededHuman = [array objectAtIndex:0];
assertThat([[seededHuman.objectID URIRepresentation] URLByDeletingLastPathComponent], is(equalTo([[seedObjectID URIRepresentation] URLByDeletingLastPathComponent])));
// Check that the secondary object does not exist
fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name = %@", @"Sarah"];
array = [seededStore.persistentStoreManagedObjectContext executeFetchRequest:fetchRequest error:&error];
assertThat(array, is(empty()));
}
#if __IPHONE_OS_VERSION_MIN_REQUIRED
- (void)testThatAddingASQLiteStoreExcludesThePathFromiCloudBackups
{
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSError *error;
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"TestBackupExclusion.sqlite"];
[managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
id resourceValue = nil;
BOOL success = [storeURL getResourceValue:&resourceValue forKey:NSURLIsExcludedFromBackupKey error:&error];
expect(success).to.beTruthy();
expect(resourceValue).to.equal(@(YES));
}
#endif
- (void)testResetPersistentStoresDoesNotTriggerDeadlock
{
RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
NSManagedObject *managedObject1 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.mainQueueManagedObjectContext];
NSManagedObject *managedObject2 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
[fetchRequest setSortDescriptors:@[ [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES] ]];
NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectStore.mainQueueManagedObjectContext sectionNameKeyPath:nil cacheName:nil];
NSError *error = nil;
[fetchedResultsController performFetch:&error];
BOOL success = [managedObjectStore resetPersistentStores:&error];
expect(success).to.equal(YES);
expect(error).to.beNil();
[fetchedResultsController performFetch:&error];
[managedObject1 setValue:@"Blake" forKey:@"name"];
[managedObject2.managedObjectContext performBlockAndWait:^{
[managedObject2 setValue:@"Blake" forKey:@"name"];
}];
}
- (void)testCleanupOfExternalStorageDirectoryOnReset
{
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSEntityDescription *humanEntity = [managedObjectStore.managedObjectModel entitiesByName][@"Human"];
NSAttributeDescription *photoAttribute = [[NSAttributeDescription alloc] init];
[photoAttribute setName:@"photo"];
[photoAttribute setAttributeType:NSTransformableAttributeType];
[photoAttribute setAllowsExternalBinaryDataStorage:YES];
NSArray *newProperties = [[humanEntity properties] arrayByAddingObject:photoAttribute];
[humanEntity setProperties:newProperties];
NSError *error = nil;
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"RKTestsStore.sqlite"];
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
assertThat(persistentStore, is(notNilValue()));
[managedObjectStore createManagedObjectContexts];
// Check that there is a support directory
NSString *supportDirectoryName = [NSString stringWithFormat:@".%@_SUPPORT", [[persistentStore.URL lastPathComponent] stringByDeletingPathExtension]];
NSURL *supportDirectoryFileURL = [NSURL URLWithString:supportDirectoryName relativeToURL:[persistentStore.URL URLByDeletingLastPathComponent]];
BOOL isDirectory = NO;
BOOL supportDirectoryExists = [[NSFileManager defaultManager] fileExistsAtPath:[supportDirectoryFileURL path] isDirectory:&isDirectory];
expect(supportDirectoryExists).to.equal(YES);
expect(isDirectory).to.equal(YES);
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[supportDirectoryFileURL path] error:&error];
NSDate *creationDate = attributes[NSFileCreationDate];
BOOL success = [managedObjectStore resetPersistentStores:&error];
expect(success).to.equal(YES);
attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[supportDirectoryFileURL path] error:&error];
NSDate *newCreationDate = attributes[NSFileCreationDate];
expect([creationDate laterDate:newCreationDate]).to.equal(newCreationDate);
}
- (void)testThatPersistentStoreWithLongNameHasExternalStorageResetCorrectly
{
// Create a store with an object to serve as our seed database
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSError *error = nil;
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"This is the Store.sqlite"];
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
assertThat(persistentStore, is(notNilValue()));
[managedObjectStore createManagedObjectContexts];
// Check that there is a support directory
NSString *supportDirectoryName = [NSString stringWithFormat:@".%@_SUPPORT", [[persistentStore.URL lastPathComponent] stringByDeletingPathExtension]];
NSString *supportDirectoryPath = [[[[persistentStore URL] path] stringByDeletingLastPathComponent] stringByAppendingPathComponent:supportDirectoryName];
BOOL isDirectory = NO;
BOOL supportDirectoryExists = [[NSFileManager defaultManager] fileExistsAtPath:supportDirectoryPath isDirectory:&isDirectory];
expect(supportDirectoryExists).to.equal(YES);
expect(isDirectory).to.equal(YES);
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:supportDirectoryPath error:&error];
NSDate *creationDate = attributes[NSFileCreationDate];
BOOL success = [managedObjectStore resetPersistentStores:&error];
expect(success).to.equal(YES);
attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:supportDirectoryPath error:&error];
NSDate *newCreationDate = attributes[NSFileCreationDate];
expect([creationDate laterDate:newCreationDate]).to.equal(newCreationDate);
}
#pragma mark - Versioning Tests
- (void)testThatAttemptToMigrateStoreAtNonExistantFileURLReturnsError
{
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"NonExistantStore.sqlite"];
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
NSError *error = nil;
NSURL *modelURL = RKURLForManagedObjectModelWithNameAtVersion(@"VersionedModel", 1);
BOOL success = [RKManagedObjectStore migratePersistentStoreOfType:NSSQLiteStoreType atURL:storeURL toModelAtURL:modelURL error:&error configuringModelsWithBlock:nil];
expect(success).to.equal(NO);
expect(error.code).to.equal(0);
}
- (void)testThatAttemptingToMigrateToANonVersionedModelReturnsError
{
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model_v1 = RKManagedObjectModelWithNameAtVersion(@"VersionedModel", 1);
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model_v1];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"TestStore.sqlite"];
NSError *error = nil;
NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption: @(NO), NSInferMappingModelAutomaticallyOption: @(NO) };
[managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:options error:&error];
[managedObjectStore createManagedObjectContexts];
managedObjectStore = nil;
// Attempting to update to `modelURL` will fail because this is an unversioned model
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
BOOL success = [RKManagedObjectStore migratePersistentStoreOfType:NSSQLiteStoreType atURL:storeURL toModelAtURL:modelURL error:&error configuringModelsWithBlock:nil];
expect(success).to.equal(NO);
expect(error).notTo.beNil();
expect(error.code).to.equal(NSMigrationError);
assertThat([error localizedDescription], startsWith(@"Migration failed: Migrations can only be performed to versioned destination models contained in a .momd package. Incompatible destination model given at path"));
}
- (void)testThatAttemptingToMigrateFromAnUnidentifiableSourceModelReturnsError
{
NSManagedObjectModel *model_v1 = RKManagedObjectModelWithNameAtVersion(@"VersionedModel", 1);
NSEntityDescription *extraEntity = [NSEntityDescription new];
[extraEntity setName:@"Extra"];
[model_v1 setEntities:[[model_v1 entities] arrayByAddingObject:extraEntity]];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model_v1];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"TestStore.sqlite"];
NSError *error = nil;
NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption: @(NO), NSInferMappingModelAutomaticallyOption: @(NO) };
[managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:options error:&error];
[managedObjectStore createManagedObjectContexts];
managedObjectStore = nil;
// Attempting to upgrade to v2 returns NO, because it can't find the source model (due to the added entity)
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
NSURL *modelURL = RKURLForManagedObjectModelWithNameAtVersion(@"VersionedModel", 2);
BOOL success = [RKManagedObjectStore migratePersistentStoreOfType:NSSQLiteStoreType atURL:storeURL toModelAtURL:modelURL error:&error configuringModelsWithBlock:nil];
expect(success).to.equal(NO);
expect(error).notTo.beNil();
expect(error.code).to.equal(NSMigrationMissingSourceModelError);
assertThat([error localizedDescription], startsWith(@"Migration failed: Unable to find the source managed object model used to create the SQLite store at path"));
}
- (void)testMigrationToCompatibleVersionReturnsYes
{
NSManagedObjectModel *model_v1 = RKManagedObjectModelWithNameAtVersion(@"VersionedModel", 1);
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model_v1];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"TestStore.sqlite"];
NSError *error = nil;
NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption: @(NO), NSInferMappingModelAutomaticallyOption: @(NO) };
[managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:options error:&error];
[managedObjectStore createManagedObjectContexts];
managedObjectStore = nil;
// Attempting to upgrade to v1 returns YES, because its already compatible
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
NSURL *modelURL = RKURLForManagedObjectModelWithNameAtVersion(@"VersionedModel", 1);
BOOL success = [RKManagedObjectStore migratePersistentStoreOfType:NSSQLiteStoreType atURL:storeURL toModelAtURL:modelURL error:&error configuringModelsWithBlock:nil];
expect(success).to.equal(YES);
expect(error).to.beNil();
}
- (void)testUpgradingFromVersionedModelAt1_0to2_0
{
// Create a v1 Store
NSManagedObjectModel *model_v1 = RKManagedObjectModelWithNameAtVersion(@"VersionedModel", 1);
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model_v1];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"TestStore.sqlite"];
NSError *error = nil;
NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption: @(NO), NSInferMappingModelAutomaticallyOption: @(NO) };
[managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:options error:&error];
[managedObjectStore createManagedObjectContexts];
managedObjectStore = nil;
// Now upgrade it to v2
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
NSURL *modelURL = RKURLForManagedObjectModelWithNameAtVersion(@"VersionedModel", 2);
BOOL success = [RKManagedObjectStore migratePersistentStoreOfType:NSSQLiteStoreType atURL:storeURL toModelAtURL:modelURL error:&error configuringModelsWithBlock:nil];
expect(success).to.equal(YES);
expect(error).to.beNil();
}
- (void)testUpgradingFromVersionedModelAt1_0to_4_0
{
// Create a v1 Store
NSManagedObjectModel *model_v1 = RKManagedObjectModelWithNameAtVersion(@"VersionedModel", 1);
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model_v1];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"TestStore.sqlite"];
NSError *error = nil;
NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption: @(NO), NSInferMappingModelAutomaticallyOption: @(NO) };
[managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:options error:&error];
[managedObjectStore createManagedObjectContexts];
managedObjectStore = nil;
// Now upgrade it to v4
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
NSURL *modelURL = RKURLForManagedObjectModelWithNameAtVersion(@"VersionedModel", 4);
BOOL success = [RKManagedObjectStore migratePersistentStoreOfType:NSSQLiteStoreType atURL:storeURL toModelAtURL:modelURL error:&error configuringModelsWithBlock:nil];
expect(success).to.equal(YES);
expect(error).to.beNil();
}
- (void)testUpgradingFromVersionedModelWithSearchAttributesAt2_0to_3_0
{
// Create a v2 Store
NSManagedObjectModel *model_v2 = RKManagedObjectModelWithNameAtVersion(@"VersionedModel", 2);
// Add search indexing on the title attribute
NSEntityDescription *articleEntity = [[model_v2 entitiesByName] objectForKey:@"Article"];
[RKSearchIndexer addSearchIndexingToEntity:articleEntity onAttributes:@[ @"title" ]];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model_v2];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"TestStore.sqlite"];
NSError *error = nil;
NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption: @(NO), NSInferMappingModelAutomaticallyOption: @(NO) };
[managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:options error:&error];
[managedObjectStore createManagedObjectContexts];
managedObjectStore = nil;
// Now upgrade it to v3
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
NSURL *modelURL = RKURLForManagedObjectModelWithNameAtVersion(@"VersionedModel", 4);
BOOL success = [RKManagedObjectStore migratePersistentStoreOfType:NSSQLiteStoreType atURL:storeURL toModelAtURL:modelURL error:&error configuringModelsWithBlock:^(NSManagedObjectModel *model, NSURL *sourceURL) {
if ([[model versionIdentifiers] isEqualToSet:[NSSet setWithObject:@"2.0"]]) {
NSEntityDescription *articleEntity = [[model entitiesByName] objectForKey:@"Article"];
[RKSearchIndexer addSearchIndexingToEntity:articleEntity onAttributes:@[ @"title" ]];
}
}];
expect(success).to.equal(YES);
expect(error).to.beNil();
}
- (void)testUpgradingFromVersionedModelWithSearchAttributesAt_1_0toLatest
{
// Create a v1 Store
NSManagedObjectModel *model_v1 = RKManagedObjectModelWithNameAtVersion(@"VersionedModel", 1);
// Add search indexing on the title attribute
NSEntityDescription *articleEntity = [[model_v1 entitiesByName] objectForKey:@"Article"];
NSEntityDescription *tagEntity = [[model_v1 entitiesByName] objectForKey:@"Tag"];
[RKSearchIndexer addSearchIndexingToEntity:articleEntity onAttributes:@[ @"title" ]];
[RKSearchIndexer addSearchIndexingToEntity:tagEntity onAttributes:@[ @"name" ]];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model_v1];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"TestStore.sqlite"];
NSError *error = nil;
NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption: @(NO), NSInferMappingModelAutomaticallyOption: @(NO) };
[managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:options error:&error];
[managedObjectStore createManagedObjectContexts];
managedObjectStore = nil;
// Now upgrade it to the latest version
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"VersionedModel" withExtension:@"momd"];
BOOL success = [RKManagedObjectStore migratePersistentStoreOfType:NSSQLiteStoreType atURL:storeURL toModelAtURL:modelURL error:&error configuringModelsWithBlock:^(NSManagedObjectModel *model, NSURL *sourceURL) {
if ([[model versionIdentifiers] isEqualToSet:[NSSet setWithObject:@"1.0"]]) {
NSEntityDescription *articleEntity = [[model entitiesByName] objectForKey:@"Article"];
NSEntityDescription *tagEntity = [[model entitiesByName] objectForKey:@"Tag"];
[RKSearchIndexer addSearchIndexingToEntity:articleEntity onAttributes:@[ @"title" ]];
[RKSearchIndexer addSearchIndexingToEntity:tagEntity onAttributes:@[ @"name" ]];
} else if ([[model versionIdentifiers] isEqualToSet:[NSSet setWithObject:@"2.0"]]) {
NSEntityDescription *articleEntity = [[model entitiesByName] objectForKey:@"Article"];
NSEntityDescription *tagEntity = [[model entitiesByName] objectForKey:@"Tag"];
[RKSearchIndexer addSearchIndexingToEntity:articleEntity onAttributes:@[ @"title", @"body" ]];
[RKSearchIndexer addSearchIndexingToEntity:tagEntity onAttributes:@[ @"name" ]];
} else if ([[model versionIdentifiers] containsObject:@"3.0"] || [[model versionIdentifiers] containsObject:@"4.0"]) {
// We index the same attributes on v3 and v4
NSEntityDescription *articleEntity = [[model entitiesByName] objectForKey:@"Article"];
NSEntityDescription *tagEntity = [[model entitiesByName] objectForKey:@"Tag"];
[RKSearchIndexer addSearchIndexingToEntity:articleEntity onAttributes:@[ @"title", @"body", @"authorName" ]];
[RKSearchIndexer addSearchIndexingToEntity:tagEntity onAttributes:@[ @"name" ]];
}
}];
expect(success).to.equal(YES);
expect(error).to.beNil();
}
@end