From f58dfcb15602eaac00d2149e8fc45697bfb44b8d Mon Sep 17 00:00:00 2001 From: Dominic Frei Date: Thu, 4 Feb 2021 15:37:07 +0000 Subject: [PATCH 1/4] Add tests. --- Realm/TestUtils/RLMTestObjects.m | 6 + Realm/TestUtils/include/RLMTestObjects.h | 8 + Realm/Tests/MigrationTests.mm | 177 +++++++++++++++++++++++ 3 files changed, 191 insertions(+) diff --git a/Realm/TestUtils/RLMTestObjects.m b/Realm/TestUtils/RLMTestObjects.m index 844b858676..eb262dfd0b 100644 --- a/Realm/TestUtils/RLMTestObjects.m +++ b/Realm/TestUtils/RLMTestObjects.m @@ -411,6 +411,12 @@ + (NSDictionary *)_realmColumnNames { } @end +@implementation EmbeddedChildObject +@end + +@implementation EmbeddedParentObject +@end + #pragma mark FakeObject @implementation FakeObject diff --git a/Realm/TestUtils/include/RLMTestObjects.h b/Realm/TestUtils/include/RLMTestObjects.h index ce02840088..3e997b68a1 100644 --- a/Realm/TestUtils/include/RLMTestObjects.h +++ b/Realm/TestUtils/include/RLMTestObjects.h @@ -484,6 +484,14 @@ RLM_ARRAY_TYPE(RenamedProperties2) @property (nonatomic) int value; @end +@interface EmbeddedChildObject : RLMObject +@property (nonatomic) int intCol; +@end + +@interface EmbeddedParentObject : RLMObject +@property (nonatomic) EmbeddedChildObject *embeddedObject; +@end + #pragma mark FakeObject @interface FakeObject : RLMObject diff --git a/Realm/Tests/MigrationTests.mm b/Realm/Tests/MigrationTests.mm index f18cc95014..012cd4dbf6 100644 --- a/Realm/Tests/MigrationTests.mm +++ b/Realm/Tests/MigrationTests.mm @@ -1398,6 +1398,183 @@ - (void)testModifyPrimaryKeyInMigration { } } +- (void)testChangeEmptyTableFromTopLevelToEmbedded { + RLMObjectSchema *parentSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedParentObject.class]; + RLMObjectSchema *childSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedChildObject.class]; + [self createTestRealmWithSchema:@[parentSchema, childSchema] block:^(RLMRealm *realm) {}]; + + childSchema.isEmbedded = YES; + + RLMRealmConfiguration *realmConfiguration = self.config; + realmConfiguration.customSchema = [self schemaWithObjects:@[childSchema]]; + realmConfiguration.schemaVersion = 1; + XCTAssertTrue([RLMRealm performMigrationForConfiguration:realmConfiguration error:nil]); +} + +- (void)testChangeTableToEmbeddedWithOnlyOneLinkPerObject { + RLMObjectSchema *parentSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedParentObject.class]; + RLMObjectSchema *childSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedChildObject.class]; + [self createTestRealmWithSchema:@[parentSchema, childSchema] block:^(RLMRealm *realm) { + EmbeddedChildObject *childObject1 = (EmbeddedChildObject *)[realm createObject:EmbeddedChildObject.className withValue:@[@42]]; + [EmbeddedParentObject createInRealm:realm withValue:@[childObject1]]; + EmbeddedChildObject *childObject2 = (EmbeddedChildObject *)[realm createObject:EmbeddedChildObject.className withValue:@[@43]]; + [EmbeddedParentObject createInRealm:realm withValue:@[childObject2]]; + }]; + + childSchema.isEmbedded = YES; + + RLMRealmConfiguration *realmConfiguration = self.config; + realmConfiguration.customSchema = [self schemaWithObjects:@[parentSchema, childSchema]]; + realmConfiguration.schemaVersion = 1; + __block int parentEnumerateCalls = 0; + __block int childEnumerateCalls = 0; + realmConfiguration.migrationBlock = ^(RLMMigration *migration, uint64_t) { + [migration enumerateObjects:EmbeddedParentObject.className block:^(RLMObject *oldObject, RLMObject *newObject) { + parentEnumerateCalls++; + XCTAssertNotNil(oldObject); + XCTAssertNotNil(newObject); + XCTAssertEqual(oldObject[@"embeddedObject"][@"intCol"], newObject[@"embeddedObject"][@"intCol"]); + XCTAssert([newObject[@"embeddedObject"][@"intCol"] intValue] == 42 || [newObject[@"embeddedObject"][@"intCol"] intValue] == 43); + }]; + [migration enumerateObjects:EmbeddedChildObject.className block:^(RLMObject *oldObject, RLMObject *newObject) { + childEnumerateCalls++; + XCTAssertNotNil(oldObject); + XCTAssertNotNil(newObject); + XCTAssertEqual([oldObject[@"intCol"] intValue], [newObject[@"intCol"] intValue]); + XCTAssert([newObject[@"intCol"] intValue] == 42 || [newObject[@"intCol"] intValue] == 43); + }]; + }; + XCTAssertTrue([RLMRealm performMigrationForConfiguration:realmConfiguration error:nil]); + + XCTAssertEqual(parentEnumerateCalls, 2); + XCTAssertEqual(childEnumerateCalls, 2); + + RLMRealm *realm = [RLMRealm realmWithConfiguration:realmConfiguration error:nil]; + RLMResults *parentObjects = RLMGetObjects(realm, EmbeddedParentObject.className, nil); + XCTAssertEqual(parentObjects.count, 2); + EmbeddedParentObject *firstParentObject = (EmbeddedParentObject *)parentObjects[0]; + EmbeddedChildObject *firstParentsChild = firstParentObject.embeddedObject; + XCTAssert([firstParentsChild isKindOfClass:[RLMEmbeddedObject class]]); + XCTAssertEqual(firstParentsChild.intCol, 42); + EmbeddedParentObject *secondParentObject = (EmbeddedParentObject *)parentObjects[0]; + EmbeddedChildObject *secondParentsChild = secondParentObject.embeddedObject; + XCTAssert([secondParentsChild isKindOfClass:[RLMEmbeddedObject class]]); + XCTAssertEqual(secondParentsChild.intCol, 43); +} + +- (void)testEmbeddedObjectsWithoutIncomingLinksGetDeleted { + RLMObjectSchema *childSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedChildObject.class]; + [self createTestRealmWithSchema:@[childSchema] block:^(RLMRealm *realm) { + [realm createObject:EmbeddedChildObject.className withValue:@[@42]]; + [realm createObject:EmbeddedChildObject.className withValue:@[@43]]; + }]; + + childSchema.isEmbedded = YES; + + RLMRealmConfiguration *realmConfiguration = self.config; + realmConfiguration.customSchema = [self schemaWithObjects:@[childSchema]]; + realmConfiguration.schemaVersion = 1; + __block int childEnumerateCalls = 0; + realmConfiguration.migrationBlock = ^(RLMMigration *migration, uint64_t) { + [migration enumerateObjects:EmbeddedChildObject.className block:^(RLMObject *oldObject, RLMObject *newObject) { + childEnumerateCalls++; + XCTAssertNotNil(oldObject); + XCTAssertNil(newObject); + }]; + }; + XCTAssertTrue([RLMRealm performMigrationForConfiguration:realmConfiguration error:nil]); + + XCTAssertEqual(childEnumerateCalls, 2); + + RLMRealm *realm = [RLMRealm realmWithConfiguration:realmConfiguration error:nil]; + RLMResults *childObjects = RLMGetObjects(realm, EmbeddedChildObject.className, nil); + XCTAssertEqual(childObjects.count, 0); +} + +- (void)testConvertToEmbeddedWithoutMigrationBlockFails { + RLMObjectSchema *parentSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedParentObject.class]; + RLMObjectSchema *childSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedChildObject.class]; + [self createTestRealmWithSchema:@[parentSchema, childSchema] block:^(RLMRealm *realm) { + EmbeddedChildObject *childObject = (EmbeddedChildObject *)[realm createObject:EmbeddedChildObject.className withValue:@[@42]]; + [EmbeddedParentObject createInRealm:realm withValue:@[childObject]]; + [EmbeddedParentObject createInRealm:realm withValue:@[childObject]]; + }]; + + childSchema.isEmbedded = YES; + + RLMRealmConfiguration *realmConfiguration = self.config; + realmConfiguration.customSchema = [self schemaWithObjects:@[parentSchema, childSchema]]; + realmConfiguration.schemaVersion = 1; + XCTAssertFalse([RLMRealm performMigrationForConfiguration:realmConfiguration error:nil]); +} + +- (void)testConvertToEmbeddedWithMultipleIncomingLinksResolvedInMigrationBlock { + RLMObjectSchema *parentSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedParentObject.class]; + RLMObjectSchema *childSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedChildObject.class]; + [self createTestRealmWithSchema:@[parentSchema, childSchema] block:^(RLMRealm *realm) { + EmbeddedChildObject *childObject = (EmbeddedChildObject *)[realm createObject:EmbeddedChildObject.className withValue:@[@42]]; + [EmbeddedParentObject createInRealm:realm withValue:@[childObject]]; + [EmbeddedParentObject createInRealm:realm withValue:@[childObject]]; + }]; + + childSchema.isEmbedded = YES; + + RLMRealmConfiguration *realmConfiguration = self.config; + realmConfiguration.customSchema = [self schemaWithObjects:@[parentSchema, childSchema]]; + realmConfiguration.schemaVersion = 1; + __block int parentEnumerateCalls = 0; + realmConfiguration.migrationBlock = ^(RLMMigration *migration, uint64_t) { + [migration enumerateObjects:EmbeddedParentObject.className block:^(RLMObject *oldObject, RLMObject *newObject) { + parentEnumerateCalls++; + XCTAssertNotNil(oldObject); + XCTAssertNotNil(newObject); + RLMObject *newChild = [migration createObject:EmbeddedChildObject.className withValue:@[newObject[@"embeddedObject"][@"intCol"]]]; + newObject[@"embeddedObject"] = newChild; + }]; + }; + XCTAssertTrue([RLMRealm performMigrationForConfiguration:realmConfiguration error:nil]); + + XCTAssertEqual(parentEnumerateCalls, 2); + + RLMRealm *realm = [RLMRealm realmWithConfiguration:realmConfiguration error:nil]; + RLMResults *parentObjects = RLMGetObjects(realm, EmbeddedParentObject.className, nil); + XCTAssertEqual(parentObjects.count, 1); + EmbeddedParentObject *firstParentObject = (EmbeddedParentObject *)parentObjects[0]; + EmbeddedChildObject *firstParentsChild = firstParentObject.embeddedObject; + XCTAssert([firstParentsChild isKindOfClass:[RLMEmbeddedObject class]]); + XCTAssertEqual(firstParentsChild.intCol, 42); + EmbeddedParentObject *secondParentObject = (EmbeddedParentObject *)parentObjects[0]; + EmbeddedChildObject *secondParentsChild = secondParentObject.embeddedObject; + XCTAssert([secondParentsChild isKindOfClass:[RLMEmbeddedObject class]]); + XCTAssertEqual(secondParentsChild.intCol, 42); +} + +- (void)testConvertToEmbeddedAddingMoreLinks { + RLMObjectSchema *parentSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedParentObject.class]; + RLMObjectSchema *childSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedChildObject.class]; + [self createTestRealmWithSchema:@[parentSchema, childSchema] block:^(RLMRealm *realm) { + EmbeddedChildObject *childObject = (EmbeddedChildObject *)[realm createObject:EmbeddedChildObject.className withValue:@[@42]]; + [EmbeddedParentObject createInRealm:realm withValue:@[childObject]]; + }]; + + childSchema.isEmbedded = YES; + + RLMRealmConfiguration *realmConfiguration = self.config; + realmConfiguration.customSchema = [self schemaWithObjects:@[parentSchema, childSchema]]; + realmConfiguration.schemaVersion = 1; + __block int parentEnumerateCalls = 0; + realmConfiguration.migrationBlock = ^(RLMMigration *migration, uint64_t) { + [migration enumerateObjects:EmbeddedParentObject.className block:^(RLMObject *oldObject, RLMObject *newObject) { + parentEnumerateCalls++; + XCTAssertNotNil(oldObject); + XCTAssertNotNil(newObject); + RLMObject *child = newObject[@"embeddedObject"]; + [migration createObject:EmbeddedParentObject.className withValue:@[child]]; + }]; + }; + XCTAssertFalse([RLMRealm performMigrationForConfiguration:realmConfiguration error:nil]); +} + #pragma mark - Property Rename // Successful Property Rename Tests From 6d3f6838c30d31d870dd1c9679979022f0450df0 Mon Sep 17 00:00:00 2001 From: Dominic Frei Date: Thu, 4 Feb 2021 15:52:16 +0000 Subject: [PATCH 2/4] Adjust array indices. --- Realm/Tests/MigrationTests.mm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Realm/Tests/MigrationTests.mm b/Realm/Tests/MigrationTests.mm index 012cd4dbf6..62fd420d0f 100644 --- a/Realm/Tests/MigrationTests.mm +++ b/Realm/Tests/MigrationTests.mm @@ -1456,7 +1456,7 @@ - (void)testChangeTableToEmbeddedWithOnlyOneLinkPerObject { EmbeddedChildObject *firstParentsChild = firstParentObject.embeddedObject; XCTAssert([firstParentsChild isKindOfClass:[RLMEmbeddedObject class]]); XCTAssertEqual(firstParentsChild.intCol, 42); - EmbeddedParentObject *secondParentObject = (EmbeddedParentObject *)parentObjects[0]; + EmbeddedParentObject *secondParentObject = (EmbeddedParentObject *)parentObjects[1]; EmbeddedChildObject *secondParentsChild = secondParentObject.embeddedObject; XCTAssert([secondParentsChild isKindOfClass:[RLMEmbeddedObject class]]); XCTAssertEqual(secondParentsChild.intCol, 43); @@ -1538,12 +1538,12 @@ - (void)testConvertToEmbeddedWithMultipleIncomingLinksResolvedInMigrationBlock { RLMRealm *realm = [RLMRealm realmWithConfiguration:realmConfiguration error:nil]; RLMResults *parentObjects = RLMGetObjects(realm, EmbeddedParentObject.className, nil); - XCTAssertEqual(parentObjects.count, 1); + XCTAssertEqual(parentObjects.count, 2); EmbeddedParentObject *firstParentObject = (EmbeddedParentObject *)parentObjects[0]; EmbeddedChildObject *firstParentsChild = firstParentObject.embeddedObject; XCTAssert([firstParentsChild isKindOfClass:[RLMEmbeddedObject class]]); XCTAssertEqual(firstParentsChild.intCol, 42); - EmbeddedParentObject *secondParentObject = (EmbeddedParentObject *)parentObjects[0]; + EmbeddedParentObject *secondParentObject = (EmbeddedParentObject *)parentObjects[1]; EmbeddedChildObject *secondParentsChild = secondParentObject.embeddedObject; XCTAssert([secondParentsChild isKindOfClass:[RLMEmbeddedObject class]]); XCTAssertEqual(secondParentsChild.intCol, 42); From 9c03d18b054cb3fad3aff21cb523e765fbadbd09 Mon Sep 17 00:00:00 2001 From: Dominic Frei Date: Mon, 8 Feb 2021 09:47:47 +0000 Subject: [PATCH 3/4] Adjust tests. --- Realm/Tests/MigrationTests.mm | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Realm/Tests/MigrationTests.mm b/Realm/Tests/MigrationTests.mm index 62fd420d0f..2f54426191 100644 --- a/Realm/Tests/MigrationTests.mm +++ b/Realm/Tests/MigrationTests.mm @@ -1454,11 +1454,9 @@ - (void)testChangeTableToEmbeddedWithOnlyOneLinkPerObject { XCTAssertEqual(parentObjects.count, 2); EmbeddedParentObject *firstParentObject = (EmbeddedParentObject *)parentObjects[0]; EmbeddedChildObject *firstParentsChild = firstParentObject.embeddedObject; - XCTAssert([firstParentsChild isKindOfClass:[RLMEmbeddedObject class]]); XCTAssertEqual(firstParentsChild.intCol, 42); EmbeddedParentObject *secondParentObject = (EmbeddedParentObject *)parentObjects[1]; EmbeddedChildObject *secondParentsChild = secondParentObject.embeddedObject; - XCTAssert([secondParentsChild isKindOfClass:[RLMEmbeddedObject class]]); XCTAssertEqual(secondParentsChild.intCol, 43); } @@ -1479,7 +1477,6 @@ - (void)testEmbeddedObjectsWithoutIncomingLinksGetDeleted { [migration enumerateObjects:EmbeddedChildObject.className block:^(RLMObject *oldObject, RLMObject *newObject) { childEnumerateCalls++; XCTAssertNotNil(oldObject); - XCTAssertNil(newObject); }]; }; XCTAssertTrue([RLMRealm performMigrationForConfiguration:realmConfiguration error:nil]); @@ -1528,8 +1525,7 @@ - (void)testConvertToEmbeddedWithMultipleIncomingLinksResolvedInMigrationBlock { parentEnumerateCalls++; XCTAssertNotNil(oldObject); XCTAssertNotNil(newObject); - RLMObject *newChild = [migration createObject:EmbeddedChildObject.className withValue:@[newObject[@"embeddedObject"][@"intCol"]]]; - newObject[@"embeddedObject"] = newChild; + newObject[@"embeddedObject"] = nil; }]; }; XCTAssertTrue([RLMRealm performMigrationForConfiguration:realmConfiguration error:nil]); @@ -1541,12 +1537,12 @@ - (void)testConvertToEmbeddedWithMultipleIncomingLinksResolvedInMigrationBlock { XCTAssertEqual(parentObjects.count, 2); EmbeddedParentObject *firstParentObject = (EmbeddedParentObject *)parentObjects[0]; EmbeddedChildObject *firstParentsChild = firstParentObject.embeddedObject; - XCTAssert([firstParentsChild isKindOfClass:[RLMEmbeddedObject class]]); - XCTAssertEqual(firstParentsChild.intCol, 42); + XCTAssertNil(firstParentsChild); EmbeddedParentObject *secondParentObject = (EmbeddedParentObject *)parentObjects[1]; EmbeddedChildObject *secondParentsChild = secondParentObject.embeddedObject; - XCTAssert([secondParentsChild isKindOfClass:[RLMEmbeddedObject class]]); - XCTAssertEqual(secondParentsChild.intCol, 42); + XCTAssertNil(secondParentsChild); + RLMResults *childObjects = RLMGetObjects(realm, EmbeddedChildObject.className, nil); + XCTAssertEqual(childObjects.count, 0); } - (void)testConvertToEmbeddedAddingMoreLinks { From aa10a604a892d40bc7c31d47fa1c60c5a3c93984 Mon Sep 17 00:00:00 2001 From: Dominic Frei Date: Mon, 1 Mar 2021 16:49:43 +0000 Subject: [PATCH 4/4] Update tests. --- Realm/TestUtils/RLMTestObjects.m | 6 - Realm/TestUtils/include/RLMTestObjects.h | 8 - Realm/Tests/MigrationTests.mm | 409 +++++++++++++++++------ 3 files changed, 310 insertions(+), 113 deletions(-) diff --git a/Realm/TestUtils/RLMTestObjects.m b/Realm/TestUtils/RLMTestObjects.m index eb262dfd0b..844b858676 100644 --- a/Realm/TestUtils/RLMTestObjects.m +++ b/Realm/TestUtils/RLMTestObjects.m @@ -411,12 +411,6 @@ + (NSDictionary *)_realmColumnNames { } @end -@implementation EmbeddedChildObject -@end - -@implementation EmbeddedParentObject -@end - #pragma mark FakeObject @implementation FakeObject diff --git a/Realm/TestUtils/include/RLMTestObjects.h b/Realm/TestUtils/include/RLMTestObjects.h index 3e997b68a1..ce02840088 100644 --- a/Realm/TestUtils/include/RLMTestObjects.h +++ b/Realm/TestUtils/include/RLMTestObjects.h @@ -484,14 +484,6 @@ RLM_ARRAY_TYPE(RenamedProperties2) @property (nonatomic) int value; @end -@interface EmbeddedChildObject : RLMObject -@property (nonatomic) int intCol; -@end - -@interface EmbeddedParentObject : RLMObject -@property (nonatomic) EmbeddedChildObject *embeddedObject; -@end - #pragma mark FakeObject @interface FakeObject : RLMObject diff --git a/Realm/Tests/MigrationTests.mm b/Realm/Tests/MigrationTests.mm index 2f54426191..a33d4d607e 100644 --- a/Realm/Tests/MigrationTests.mm +++ b/Realm/Tests/MigrationTests.mm @@ -1399,176 +1399,387 @@ - (void)testModifyPrimaryKeyInMigration { } - (void)testChangeEmptyTableFromTopLevelToEmbedded { - RLMObjectSchema *parentSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedParentObject.class]; - RLMObjectSchema *childSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedChildObject.class]; - [self createTestRealmWithSchema:@[parentSchema, childSchema] block:^(RLMRealm *realm) {}]; + RLMProperty *intProperty = [[RLMProperty alloc] initWithName:@"intProperty" + type:RLMPropertyTypeInt + objectClassName:nil + linkOriginPropertyName:nil + indexed:NO + optional:NO]; + RLMObjectSchema *childObjectSchema = [[RLMObjectSchema alloc] initWithClassName:@"ChildClass" + objectClass:RLMObject.class + properties:@[intProperty]]; + RLMProperty *childProperty = [[RLMProperty alloc] initWithName:@"childProperty" + type:RLMPropertyTypeObject + objectClassName:@"ChildClass" + linkOriginPropertyName:nil + indexed:NO + optional:YES]; + RLMObjectSchema *parentObjectSchema = [[RLMObjectSchema alloc] initWithClassName:@"ParentClass" + objectClass:RLMObject.class + properties:@[childProperty]]; + [self createTestRealmWithSchema:@[parentObjectSchema, childObjectSchema] block:^(RLMRealm *realm) {}]; - childSchema.isEmbedded = YES; + childObjectSchema.isEmbedded = YES; RLMRealmConfiguration *realmConfiguration = self.config; - realmConfiguration.customSchema = [self schemaWithObjects:@[childSchema]]; realmConfiguration.schemaVersion = 1; - XCTAssertTrue([RLMRealm performMigrationForConfiguration:realmConfiguration error:nil]); + realmConfiguration.customSchema = [self schemaWithObjects:@[parentObjectSchema, childObjectSchema]]; + NSError *error; + XCTAssertTrue([RLMRealm performMigrationForConfiguration:realmConfiguration error:&error]); + XCTAssertNil(error); + XCTAssertTrue(childObjectSchema.isEmbedded); +} + +- (void)testChangeEmptyTableFromEmbeddedToTopLevel { + RLMProperty *intProperty = [[RLMProperty alloc] initWithName:@"intProperty" + type:RLMPropertyTypeInt + objectClassName:nil + linkOriginPropertyName:nil + indexed:NO + optional:NO]; + RLMObjectSchema *childObjectSchema = [[RLMObjectSchema alloc] initWithClassName:@"ChildClass" + objectClass:RLMEmbeddedObject.class + properties:@[intProperty]]; + RLMProperty *childProperty = [[RLMProperty alloc] initWithName:@"childProperty" + type:RLMPropertyTypeObject + objectClassName:@"ChildClass" + linkOriginPropertyName:nil + indexed:NO + optional:YES]; + RLMObjectSchema *parentObjectSchema = [[RLMObjectSchema alloc] initWithClassName:@"ParentClass" + objectClass:RLMObject.class + properties:@[childProperty]]; + [self createTestRealmWithSchema:@[parentObjectSchema, childObjectSchema] block:^(RLMRealm *realm) {}]; + + childObjectSchema.isEmbedded = NO; + + RLMRealmConfiguration *realmConfiguration = self.config; + realmConfiguration.schemaVersion = 1; + realmConfiguration.customSchema = [self schemaWithObjects:@[parentObjectSchema, childObjectSchema]]; + NSError *error; + XCTAssertTrue([RLMRealm performMigrationForConfiguration:realmConfiguration error:&error]); + XCTAssertNil(error); + XCTAssertFalse(childObjectSchema.isEmbedded); +} + +- (void)testReApplyEmbeddedFlagToEmbeddedTable { + RLMProperty *intProperty = [[RLMProperty alloc] initWithName:@"intProperty" + type:RLMPropertyTypeInt + objectClassName:nil + linkOriginPropertyName:nil + indexed:NO + optional:NO]; + RLMObjectSchema *childObjectSchema = [[RLMObjectSchema alloc] initWithClassName:@"ChildClass" + objectClass:RLMEmbeddedObject.class + properties:@[intProperty]]; + RLMProperty *childProperty = [[RLMProperty alloc] initWithName:@"childProperty" + type:RLMPropertyTypeObject + objectClassName:@"ChildClass" + linkOriginPropertyName:nil + indexed:NO + optional:YES]; + RLMObjectSchema *parentObjectSchema = [[RLMObjectSchema alloc] initWithClassName:@"ParentClass" + objectClass:RLMObject.class + properties:@[childProperty]]; + [self createTestRealmWithSchema:@[parentObjectSchema, childObjectSchema] block:^(RLMRealm *realm) {}]; + + childObjectSchema.isEmbedded = YES; + + RLMRealmConfiguration *realmConfiguration = self.config; + realmConfiguration.schemaVersion = 1; + realmConfiguration.customSchema = [self schemaWithObjects:@[parentObjectSchema, childObjectSchema]]; + NSError *error; + XCTAssertTrue([RLMRealm performMigrationForConfiguration:realmConfiguration error:&error]); + XCTAssertNil(error); + XCTAssertTrue(childObjectSchema.isEmbedded); +} + +- (void)testChangeToEmbeddedWithoutMigrationBlock { + RLMProperty *intProperty = [[RLMProperty alloc] initWithName:@"intProperty" + type:RLMPropertyTypeInt + objectClassName:nil + linkOriginPropertyName:nil + indexed:NO + optional:NO]; + RLMObjectSchema *childObjectSchema = [[RLMObjectSchema alloc] initWithClassName:@"ChildClass" + objectClass:RLMObject.class + properties:@[intProperty]]; + RLMProperty *childProperty = [[RLMProperty alloc] initWithName:@"childProperty" + type:RLMPropertyTypeObject + objectClassName:@"ChildClass" + linkOriginPropertyName:nil + indexed:NO + optional:YES]; + RLMObjectSchema *parentObjectSchema = [[RLMObjectSchema alloc] initWithClassName:@"ParentClass" + objectClass:RLMObject.class + properties:@[childProperty]]; + [self createTestRealmWithSchema:@[parentObjectSchema, childObjectSchema] block:^(RLMRealm *realm) { + id childObject = [realm createObject:@"ChildClass" withValue:@[@42]]; + [realm createObject:@"ParentClass" withValue:@[childObject]]; + [realm createObject:@"ParentClass" withValue:@[childObject]]; + }]; + + childObjectSchema.isEmbedded = YES; + + RLMRealmConfiguration *realmConfiguration = self.config; + realmConfiguration.customSchema = [self schemaWithObjects:@[parentObjectSchema, childObjectSchema]]; + realmConfiguration.schemaVersion = 1; + NSError *error; + XCTAssertFalse([RLMRealm performMigrationForConfiguration:realmConfiguration error:&error]); + XCTAssertNotNil(error); +} + +- (void)testChangeTableToEmbeddedWithoutBacklinks { + RLMProperty *intProperty = [[RLMProperty alloc] initWithName:@"intProperty" + type:RLMPropertyTypeInt + objectClassName:nil + linkOriginPropertyName:nil + indexed:NO + optional:NO]; + RLMObjectSchema *childObjectSchema = [[RLMObjectSchema alloc] initWithClassName:@"ChildClass" + objectClass:RLMEmbeddedObject.class + properties:@[intProperty]]; + [self createTestRealmWithSchema:@[childObjectSchema] block:^(RLMRealm *realm) {}]; + + childObjectSchema.isEmbedded = YES; + + RLMRealmConfiguration *realmConfiguration = self.config; + realmConfiguration.schemaVersion = 1; + realmConfiguration.customSchema = [self schemaWithObjects:@[childObjectSchema]]; + NSError *error; + XCTAssertFalse([RLMRealm performMigrationForConfiguration:realmConfiguration error:&error]); + XCTAssertNotNil(error); + XCTAssertTrue(childObjectSchema.isEmbedded); } - (void)testChangeTableToEmbeddedWithOnlyOneLinkPerObject { - RLMObjectSchema *parentSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedParentObject.class]; - RLMObjectSchema *childSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedChildObject.class]; - [self createTestRealmWithSchema:@[parentSchema, childSchema] block:^(RLMRealm *realm) { - EmbeddedChildObject *childObject1 = (EmbeddedChildObject *)[realm createObject:EmbeddedChildObject.className withValue:@[@42]]; - [EmbeddedParentObject createInRealm:realm withValue:@[childObject1]]; - EmbeddedChildObject *childObject2 = (EmbeddedChildObject *)[realm createObject:EmbeddedChildObject.className withValue:@[@43]]; - [EmbeddedParentObject createInRealm:realm withValue:@[childObject2]]; + RLMProperty *intProperty = [[RLMProperty alloc] initWithName:@"intProperty" + type:RLMPropertyTypeInt + objectClassName:nil + linkOriginPropertyName:nil + indexed:NO + optional:NO]; + RLMObjectSchema *childObjectSchema = [[RLMObjectSchema alloc] initWithClassName:@"ChildClass" + objectClass:RLMObject.class + properties:@[intProperty]]; + RLMProperty *childProperty = [[RLMProperty alloc] initWithName:@"childProperty" + type:RLMPropertyTypeObject + objectClassName:@"ChildClass" + linkOriginPropertyName:nil + indexed:NO + optional:YES]; + RLMObjectSchema *parentObjectSchema = [[RLMObjectSchema alloc] initWithClassName:@"ParentClass" + objectClass:RLMObject.class + properties:@[childProperty]]; + [self createTestRealmWithSchema:@[parentObjectSchema, childObjectSchema] block:^(RLMRealm *realm) { + id childObject1 = [realm createObject:@"ChildClass" withValue:@[@42]]; + [realm createObject:@"ParentClass" withValue:@[childObject1]]; + id childObject2 = [realm createObject:@"ChildClass" withValue:@[@43]]; + [realm createObject:@"ParentClass" withValue:@[childObject2]]; }]; - childSchema.isEmbedded = YES; + childObjectSchema.isEmbedded = YES; RLMRealmConfiguration *realmConfiguration = self.config; - realmConfiguration.customSchema = [self schemaWithObjects:@[parentSchema, childSchema]]; + realmConfiguration.customSchema = [self schemaWithObjects:@[parentObjectSchema, childObjectSchema]]; realmConfiguration.schemaVersion = 1; __block int parentEnumerateCalls = 0; __block int childEnumerateCalls = 0; realmConfiguration.migrationBlock = ^(RLMMigration *migration, uint64_t) { - [migration enumerateObjects:EmbeddedParentObject.className block:^(RLMObject *oldObject, RLMObject *newObject) { + [migration enumerateObjects:@"ParentClass" block:^(RLMObject *oldObject, RLMObject *newObject) { parentEnumerateCalls++; XCTAssertNotNil(oldObject); XCTAssertNotNil(newObject); - XCTAssertEqual(oldObject[@"embeddedObject"][@"intCol"], newObject[@"embeddedObject"][@"intCol"]); - XCTAssert([newObject[@"embeddedObject"][@"intCol"] intValue] == 42 || [newObject[@"embeddedObject"][@"intCol"] intValue] == 43); + XCTAssertEqual(oldObject[@"childProperty"][@"intProperty"], newObject[@"childProperty"][@"intProperty"]); + XCTAssert([newObject[@"childProperty"][@"intProperty"] intValue] == 42 || [newObject[@"childProperty"][@"intProperty"] intValue] == 43); }]; - [migration enumerateObjects:EmbeddedChildObject.className block:^(RLMObject *oldObject, RLMObject *newObject) { + [migration enumerateObjects:@"ChildClass" block:^(RLMObject *oldObject, RLMObject *newObject) { childEnumerateCalls++; XCTAssertNotNil(oldObject); XCTAssertNotNil(newObject); - XCTAssertEqual([oldObject[@"intCol"] intValue], [newObject[@"intCol"] intValue]); - XCTAssert([newObject[@"intCol"] intValue] == 42 || [newObject[@"intCol"] intValue] == 43); + XCTAssertEqual([oldObject[@"intProperty"] intValue], [newObject[@"intProperty"] intValue]); + XCTAssert([newObject[@"intProperty"] intValue] == 42 || [newObject[@"intProperty"] intValue] == 43); }]; }; - XCTAssertTrue([RLMRealm performMigrationForConfiguration:realmConfiguration error:nil]); - + NSError *error; + XCTAssertTrue([RLMRealm performMigrationForConfiguration:realmConfiguration error:&error]); + XCTAssertNil(error); XCTAssertEqual(parentEnumerateCalls, 2); XCTAssertEqual(childEnumerateCalls, 2); - - RLMRealm *realm = [RLMRealm realmWithConfiguration:realmConfiguration error:nil]; - RLMResults *parentObjects = RLMGetObjects(realm, EmbeddedParentObject.className, nil); + RLMRealm *realm = [RLMRealm realmWithConfiguration:realmConfiguration error:&error]; + XCTAssertNil(error); + RLMResults *parentObjects = RLMGetObjects(realm, @"ParentClass", nil); XCTAssertEqual(parentObjects.count, 2); - EmbeddedParentObject *firstParentObject = (EmbeddedParentObject *)parentObjects[0]; - EmbeddedChildObject *firstParentsChild = firstParentObject.embeddedObject; - XCTAssertEqual(firstParentsChild.intCol, 42); - EmbeddedParentObject *secondParentObject = (EmbeddedParentObject *)parentObjects[1]; - EmbeddedChildObject *secondParentsChild = secondParentObject.embeddedObject; - XCTAssertEqual(secondParentsChild.intCol, 43); -} - -- (void)testEmbeddedObjectsWithoutIncomingLinksGetDeleted { - RLMObjectSchema *childSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedChildObject.class]; - [self createTestRealmWithSchema:@[childSchema] block:^(RLMRealm *realm) { - [realm createObject:EmbeddedChildObject.className withValue:@[@42]]; - [realm createObject:EmbeddedChildObject.className withValue:@[@43]]; + RLMObject *firstParentObject = parentObjects[0]; + RLMObject *firstParentsChild = firstParentObject[@"childProperty"]; + NSLog(@"%@", firstParentsChild[@"intProperty"]); + XCTAssertEqual([firstParentsChild[@"intProperty"] intValue], 42); + RLMObject *secondParentObject = parentObjects[1]; + RLMObject *secondParentsChild = secondParentObject[@"childProperty"]; + XCTAssertEqual([secondParentsChild[@"intProperty"] intValue], 43); +} + +- (void)testChangeToEmbeddedWithMultipleBacklinksWithoutProperMigration { + RLMProperty *intProperty = [[RLMProperty alloc] initWithName:@"intProperty" + type:RLMPropertyTypeInt + objectClassName:nil + linkOriginPropertyName:nil + indexed:NO + optional:NO]; + RLMObjectSchema *childObjectSchema = [[RLMObjectSchema alloc] initWithClassName:@"ChildClass" + objectClass:RLMObject.class + properties:@[intProperty]]; + RLMProperty *childProperty = [[RLMProperty alloc] initWithName:@"childProperty" + type:RLMPropertyTypeObject + objectClassName:@"ChildClass" + linkOriginPropertyName:nil + indexed:NO + optional:YES]; + RLMObjectSchema *parentObjectSchema = [[RLMObjectSchema alloc] initWithClassName:@"ParentClass" + objectClass:RLMObject.class + properties:@[childProperty]]; + [self createTestRealmWithSchema:@[parentObjectSchema, childObjectSchema] block:^(RLMRealm *realm) { + id childObject = [realm createObject:@"ChildClass" withValue:@[@42]]; + [realm createObject:@"ParentClass" withValue:@[childObject]]; + [realm createObject:@"ParentClass" withValue:@[childObject]]; }]; - childSchema.isEmbedded = YES; + childObjectSchema.isEmbedded = YES; RLMRealmConfiguration *realmConfiguration = self.config; - realmConfiguration.customSchema = [self schemaWithObjects:@[childSchema]]; + realmConfiguration.customSchema = [self schemaWithObjects:@[parentObjectSchema, childObjectSchema]]; realmConfiguration.schemaVersion = 1; + __block int parentEnumerateCalls = 0; __block int childEnumerateCalls = 0; realmConfiguration.migrationBlock = ^(RLMMigration *migration, uint64_t) { - [migration enumerateObjects:EmbeddedChildObject.className block:^(RLMObject *oldObject, RLMObject *newObject) { + [migration enumerateObjects:@"ParentClass" block:^(RLMObject *oldObject, RLMObject *newObject) { + parentEnumerateCalls++; + XCTAssertNotNil(oldObject); + XCTAssertNotNil(newObject); + XCTAssertEqual(oldObject[@"childProperty"][@"intProperty"], newObject[@"childProperty"][@"intProperty"]); + XCTAssert([newObject[@"childProperty"][@"intProperty"] intValue] == 42 || [newObject[@"childProperty"][@"intProperty"] intValue] == 43); + }]; + [migration enumerateObjects:@"ChildClass" block:^(RLMObject *oldObject, RLMObject *newObject) { childEnumerateCalls++; XCTAssertNotNil(oldObject); + XCTAssertNotNil(newObject); + XCTAssertEqual([oldObject[@"intProperty"] intValue], [newObject[@"intProperty"] intValue]); + XCTAssert([newObject[@"intProperty"] intValue] == 42); }]; }; - XCTAssertTrue([RLMRealm performMigrationForConfiguration:realmConfiguration error:nil]); - - XCTAssertEqual(childEnumerateCalls, 2); - - RLMRealm *realm = [RLMRealm realmWithConfiguration:realmConfiguration error:nil]; - RLMResults *childObjects = RLMGetObjects(realm, EmbeddedChildObject.className, nil); - XCTAssertEqual(childObjects.count, 0); -} - -- (void)testConvertToEmbeddedWithoutMigrationBlockFails { - RLMObjectSchema *parentSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedParentObject.class]; - RLMObjectSchema *childSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedChildObject.class]; - [self createTestRealmWithSchema:@[parentSchema, childSchema] block:^(RLMRealm *realm) { - EmbeddedChildObject *childObject = (EmbeddedChildObject *)[realm createObject:EmbeddedChildObject.className withValue:@[@42]]; - [EmbeddedParentObject createInRealm:realm withValue:@[childObject]]; - [EmbeddedParentObject createInRealm:realm withValue:@[childObject]]; - }]; - - childSchema.isEmbedded = YES; - - RLMRealmConfiguration *realmConfiguration = self.config; - realmConfiguration.customSchema = [self schemaWithObjects:@[parentSchema, childSchema]]; - realmConfiguration.schemaVersion = 1; - XCTAssertFalse([RLMRealm performMigrationForConfiguration:realmConfiguration error:nil]); + NSError *error; + XCTAssertFalse([RLMRealm performMigrationForConfiguration:realmConfiguration error:&error]); + XCTAssertNotNil(error); + XCTAssertEqual(parentEnumerateCalls, 2); + XCTAssertEqual(childEnumerateCalls, 1); } - (void)testConvertToEmbeddedWithMultipleIncomingLinksResolvedInMigrationBlock { - RLMObjectSchema *parentSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedParentObject.class]; - RLMObjectSchema *childSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedChildObject.class]; - [self createTestRealmWithSchema:@[parentSchema, childSchema] block:^(RLMRealm *realm) { - EmbeddedChildObject *childObject = (EmbeddedChildObject *)[realm createObject:EmbeddedChildObject.className withValue:@[@42]]; - [EmbeddedParentObject createInRealm:realm withValue:@[childObject]]; - [EmbeddedParentObject createInRealm:realm withValue:@[childObject]]; + RLMProperty *intProperty = [[RLMProperty alloc] initWithName:@"intProperty" + type:RLMPropertyTypeInt + objectClassName:nil + linkOriginPropertyName:nil + indexed:NO + optional:NO]; + RLMObjectSchema *childObjectSchema = [[RLMObjectSchema alloc] initWithClassName:@"ChildClass" + objectClass:RLMObject.class + properties:@[intProperty]]; + RLMProperty *childProperty = [[RLMProperty alloc] initWithName:@"childProperty" + type:RLMPropertyTypeObject + objectClassName:@"ChildClass" + linkOriginPropertyName:nil + indexed:NO + optional:YES]; + RLMObjectSchema *parentObjectSchema = [[RLMObjectSchema alloc] initWithClassName:@"ParentClass" + objectClass:RLMObject.class + properties:@[childProperty]]; + [self createTestRealmWithSchema:@[parentObjectSchema, childObjectSchema] block:^(RLMRealm *realm) { + id childObject = [realm createObject:@"ChildClass" withValue:@[@42]]; + [realm createObject:@"ParentClass" withValue:@[childObject]]; + [realm createObject:@"ParentClass" withValue:@[childObject]]; }]; - - childSchema.isEmbedded = YES; - + + childObjectSchema.isEmbedded = YES; + RLMRealmConfiguration *realmConfiguration = self.config; - realmConfiguration.customSchema = [self schemaWithObjects:@[parentSchema, childSchema]]; + realmConfiguration.customSchema = [self schemaWithObjects:@[parentObjectSchema, childObjectSchema]]; realmConfiguration.schemaVersion = 1; __block int parentEnumerateCalls = 0; realmConfiguration.migrationBlock = ^(RLMMigration *migration, uint64_t) { - [migration enumerateObjects:EmbeddedParentObject.className block:^(RLMObject *oldObject, RLMObject *newObject) { + [migration enumerateObjects:@"ParentClass" block:^(RLMObject *oldObject, RLMObject *newObject) { parentEnumerateCalls++; XCTAssertNotNil(oldObject); XCTAssertNotNil(newObject); - newObject[@"embeddedObject"] = nil; + newObject[@"childProperty"] = nil; + }]; + [migration enumerateObjects:@"ChildClass" block:^(RLMObject *oldObject, RLMObject *newObject) { + XCTAssertNotNil(oldObject); + XCTAssertNotNil(newObject); + [migration deleteObject:newObject]; }]; }; XCTAssertTrue([RLMRealm performMigrationForConfiguration:realmConfiguration error:nil]); - + XCTAssertEqual(parentEnumerateCalls, 2); - + RLMRealm *realm = [RLMRealm realmWithConfiguration:realmConfiguration error:nil]; - RLMResults *parentObjects = RLMGetObjects(realm, EmbeddedParentObject.className, nil); + RLMResults *parentObjects = RLMGetObjects(realm, @"ParentClass", nil); XCTAssertEqual(parentObjects.count, 2); - EmbeddedParentObject *firstParentObject = (EmbeddedParentObject *)parentObjects[0]; - EmbeddedChildObject *firstParentsChild = firstParentObject.embeddedObject; + RLMObject *firstParentObject = parentObjects[0]; + RLMObject *firstParentsChild = firstParentObject[@"childProperty"]; XCTAssertNil(firstParentsChild); - EmbeddedParentObject *secondParentObject = (EmbeddedParentObject *)parentObjects[1]; - EmbeddedChildObject *secondParentsChild = secondParentObject.embeddedObject; + RLMObject *secondParentObject = parentObjects[1]; + RLMObject *secondParentsChild = secondParentObject[@"childProperty"]; XCTAssertNil(secondParentsChild); - RLMResults *childObjects = RLMGetObjects(realm, EmbeddedChildObject.className, nil); + RLMResults *childObjects = RLMGetObjects(realm, @"ChildClass", nil); XCTAssertEqual(childObjects.count, 0); } - (void)testConvertToEmbeddedAddingMoreLinks { - RLMObjectSchema *parentSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedParentObject.class]; - RLMObjectSchema *childSchema = [RLMObjectSchema schemaForObjectClass:EmbeddedChildObject.class]; - [self createTestRealmWithSchema:@[parentSchema, childSchema] block:^(RLMRealm *realm) { - EmbeddedChildObject *childObject = (EmbeddedChildObject *)[realm createObject:EmbeddedChildObject.className withValue:@[@42]]; - [EmbeddedParentObject createInRealm:realm withValue:@[childObject]]; + RLMProperty *intProperty = [[RLMProperty alloc] initWithName:@"intProperty" + type:RLMPropertyTypeInt + objectClassName:nil + linkOriginPropertyName:nil + indexed:NO + optional:NO]; + RLMObjectSchema *childObjectSchema = [[RLMObjectSchema alloc] initWithClassName:@"ChildClass" + objectClass:RLMObject.class + properties:@[intProperty]]; + RLMProperty *childProperty = [[RLMProperty alloc] initWithName:@"childProperty" + type:RLMPropertyTypeObject + objectClassName:@"ChildClass" + linkOriginPropertyName:nil + indexed:NO + optional:YES]; + RLMObjectSchema *parentObjectSchema = [[RLMObjectSchema alloc] initWithClassName:@"ParentClass" + objectClass:RLMObject.class + properties:@[childProperty]]; + [self createTestRealmWithSchema:@[parentObjectSchema, childObjectSchema] block:^(RLMRealm *realm) { + id childObject1 = [realm createObject:@"ChildClass" withValue:@[@42]]; + [realm createObject:@"ParentClass" withValue:@[childObject1]]; + id childObject2 = [realm createObject:@"ChildClass" withValue:@[@43]]; + [realm createObject:@"ParentClass" withValue:@[childObject2]]; }]; - - childSchema.isEmbedded = YES; - + + childObjectSchema.isEmbedded = YES; + RLMRealmConfiguration *realmConfiguration = self.config; - realmConfiguration.customSchema = [self schemaWithObjects:@[parentSchema, childSchema]]; + realmConfiguration.customSchema = [self schemaWithObjects:@[parentObjectSchema, childObjectSchema]]; realmConfiguration.schemaVersion = 1; __block int parentEnumerateCalls = 0; realmConfiguration.migrationBlock = ^(RLMMigration *migration, uint64_t) { - [migration enumerateObjects:EmbeddedParentObject.className block:^(RLMObject *oldObject, RLMObject *newObject) { + [migration enumerateObjects:@"ParentClass" block:^(RLMObject *oldObject, RLMObject *newObject) { parentEnumerateCalls++; XCTAssertNotNil(oldObject); XCTAssertNotNil(newObject); - RLMObject *child = newObject[@"embeddedObject"]; - [migration createObject:EmbeddedParentObject.className withValue:@[child]]; + RLMObject *childObject = newObject[@"childProperty"]; + [migration createObject:@"ParentClass" withValue:@[childObject]]; }]; }; - XCTAssertFalse([RLMRealm performMigrationForConfiguration:realmConfiguration error:nil]); + NSError *error; + XCTAssertFalse([RLMRealm performMigrationForConfiguration:realmConfiguration error:&error]); + XCTAssertNotNil(error); + XCTAssertEqual(parentEnumerateCalls, 2); } #pragma mark - Property Rename