You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1102 lines
47 KiB
1102 lines
47 KiB
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright 2014 Realm Inc.
|
|
//
|
|
// 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 <XCTest/XCTest.h>
|
|
|
|
#import "RLMMultiProcessTestCase.h"
|
|
|
|
#import "RLMAccessor.h"
|
|
#import "RLMObjectSchema_Private.hpp"
|
|
#import "RLMObject_Private.h"
|
|
#import "RLMProperty_Private.h"
|
|
#import "RLMRealmConfiguration_Private.hpp"
|
|
#import "RLMRealm_Dynamic.h"
|
|
#import "RLMRealm_Private.hpp"
|
|
#import "RLMSchema_Private.hpp"
|
|
#import "RLMUtil.hpp"
|
|
#import "schema.hpp"
|
|
|
|
#import <realm/table.hpp>
|
|
|
|
#import <algorithm>
|
|
#import <objc/runtime.h>
|
|
|
|
@interface SchemaTestClassBase : RLMObject
|
|
@property IntObject *baseCol;
|
|
@end
|
|
@implementation SchemaTestClassBase
|
|
@end
|
|
|
|
@interface SchemaTestClassFirstChild : SchemaTestClassBase
|
|
@property IntObject *firstChildCol;
|
|
@end
|
|
@implementation SchemaTestClassFirstChild
|
|
@end
|
|
|
|
@interface SchemaTestClassSecondChild : SchemaTestClassBase
|
|
@property IntObject *secondChildCol;
|
|
@end
|
|
@implementation SchemaTestClassSecondChild
|
|
@end
|
|
|
|
RLM_ARRAY_TYPE(SchemaTestClassBase)
|
|
RLM_ARRAY_TYPE(SchemaTestClassFirstChild)
|
|
RLM_ARRAY_TYPE(SchemaTestClassSecondChild)
|
|
|
|
@interface SchemaTestClassLink : RLMObject
|
|
@property SchemaTestClassBase *base;
|
|
@property SchemaTestClassFirstChild *child;
|
|
@property SchemaTestClassSecondChild *secondChild;
|
|
|
|
@property RLM_GENERIC_ARRAY(SchemaTestClassBase) *baseArray;
|
|
@property RLM_GENERIC_ARRAY(SchemaTestClassFirstChild) *childArray;
|
|
@property RLM_GENERIC_ARRAY(SchemaTestClassSecondChild) *secondChildArray;
|
|
@end
|
|
@implementation SchemaTestClassLink
|
|
@end
|
|
|
|
@interface NonDefaultObject : RLMObject
|
|
@property int intCol;
|
|
@end
|
|
|
|
@implementation NonDefaultObject
|
|
+ (BOOL)shouldIncludeInDefaultSchema {
|
|
return NO;
|
|
}
|
|
@end
|
|
|
|
RLM_ARRAY_TYPE(NonDefaultObject);
|
|
|
|
@interface NonDefaultArrayObject : RLMObject
|
|
@property RLM_GENERIC_ARRAY(NonDefaultObject) *array;
|
|
@end
|
|
|
|
@implementation NonDefaultArrayObject
|
|
+ (BOOL)shouldIncludeInDefaultSchema {
|
|
return NO;
|
|
}
|
|
@end
|
|
|
|
@class MutualLink2Object;
|
|
|
|
@interface MutualLink1Object : RLMObject
|
|
@property (nullable) MutualLink2Object *object;
|
|
@end
|
|
@implementation MutualLink1Object
|
|
+ (BOOL)shouldIncludeInDefaultSchema {
|
|
return NO;
|
|
}
|
|
@end
|
|
|
|
@interface MutualLink2Object : RLMObject
|
|
@property (nullable) MutualLink1Object *object;
|
|
@end
|
|
@implementation MutualLink2Object
|
|
+ (BOOL)shouldIncludeInDefaultSchema {
|
|
return NO;
|
|
}
|
|
@end
|
|
|
|
@interface SchemaTestClassWithSingleDuplicatePropertyBase : FakeObject
|
|
@property NSString *string;
|
|
@end
|
|
|
|
@implementation SchemaTestClassWithSingleDuplicatePropertyBase
|
|
@end
|
|
|
|
@interface SchemaTestClassWithSingleDuplicateProperty : SchemaTestClassWithSingleDuplicatePropertyBase
|
|
@property NSString *string;
|
|
@end
|
|
|
|
@implementation SchemaTestClassWithSingleDuplicateProperty
|
|
@dynamic string;
|
|
@end
|
|
|
|
@interface SchemaTestClassWithMultipleDuplicatePropertiesBase : FakeObject
|
|
@property NSString *string;
|
|
@property int integer;
|
|
@end
|
|
|
|
@implementation SchemaTestClassWithMultipleDuplicatePropertiesBase
|
|
@end
|
|
|
|
@interface SchemaTestClassWithMultipleDuplicateProperties : SchemaTestClassWithMultipleDuplicatePropertiesBase
|
|
@property NSString *string;
|
|
@property int integer;
|
|
@end
|
|
|
|
@implementation SchemaTestClassWithMultipleDuplicateProperties
|
|
@dynamic string;
|
|
@dynamic integer;
|
|
@end
|
|
|
|
@interface UnindexableProperty : FakeObject
|
|
@property double unindexable;
|
|
@end
|
|
@implementation UnindexableProperty
|
|
+ (NSArray *)indexedProperties {
|
|
return @[@"unindexable"];
|
|
}
|
|
@end
|
|
|
|
|
|
@interface InvalidPrimaryKeyType : FakeObject
|
|
@property double primaryKey;
|
|
@end
|
|
@implementation InvalidPrimaryKeyType
|
|
+ (NSString *)primaryKey {
|
|
return @"primaryKey";
|
|
}
|
|
@end
|
|
|
|
@interface RequiredLinkProperty : FakeObject
|
|
@property BoolObject *object;
|
|
@end
|
|
@implementation RequiredLinkProperty
|
|
+ (NSArray *)requiredProperties {
|
|
return @[@"object"];
|
|
}
|
|
@end
|
|
|
|
@interface InvalidNSNumberProtocolObject : FakeObject
|
|
@property NSNumber<NSFastEnumeration> *number;
|
|
@end
|
|
@implementation InvalidNSNumberProtocolObject
|
|
@end
|
|
|
|
@interface InvalidNSNumberNoProtocolObject : FakeObject
|
|
@property NSNumber *number;
|
|
@end
|
|
@implementation InvalidNSNumberNoProtocolObject
|
|
@end
|
|
|
|
@class InvalidReadWriteLinkingObjectsProperty;
|
|
@class InvalidLinkingObjectsPropertiesMethod;
|
|
@class ValidLinkingObjectsPropertyWithProtocol;
|
|
@class InvalidLinkingObjectsPropertyProtocol;
|
|
|
|
@interface SchemaTestsLinkSource : FakeObject
|
|
@property InvalidReadWriteLinkingObjectsProperty *irwlop;
|
|
@property InvalidLinkingObjectsPropertiesMethod *iloprm;
|
|
@property ValidLinkingObjectsPropertyWithProtocol *vlopwp;
|
|
@property InvalidLinkingObjectsPropertyProtocol *ilopp;
|
|
@end
|
|
@implementation SchemaTestsLinkSource
|
|
@end
|
|
|
|
@interface MixedProperty : FakeObject
|
|
@property id mixed;
|
|
@end
|
|
|
|
@implementation MixedProperty
|
|
@end
|
|
|
|
RLM_ARRAY_TYPE(SchemaTestsLinkSource)
|
|
|
|
@interface InvalidReadWriteLinkingObjectsProperty : FakeObject
|
|
@property RLMLinkingObjects *linkingObjects;
|
|
@end
|
|
|
|
@implementation InvalidReadWriteLinkingObjectsProperty
|
|
|
|
+ (NSDictionary *)linkingObjectsProperties {
|
|
return @{ @"linkingObjects": [RLMPropertyDescriptor descriptorWithClass:SchemaTestsLinkSource.class propertyName:@"irwlop"] };
|
|
}
|
|
|
|
@end
|
|
|
|
@interface InvalidLinkingObjectsPropertiesMethod : FakeObject
|
|
@property (readonly) RLMLinkingObjects *linkingObjects;
|
|
@end
|
|
|
|
@implementation InvalidLinkingObjectsPropertiesMethod
|
|
@end
|
|
|
|
|
|
@interface ValidLinkingObjectsPropertyWithProtocol : FakeObject
|
|
@property (readonly) RLMLinkingObjects<SchemaTestsLinkSource> *linkingObjects;
|
|
@end
|
|
|
|
@implementation ValidLinkingObjectsPropertyWithProtocol
|
|
|
|
+ (NSDictionary *)linkingObjectsProperties {
|
|
return @{ @"linkingObjects": [RLMPropertyDescriptor descriptorWithClass:SchemaTestsLinkSource.class propertyName:@"vlopwp"] };
|
|
}
|
|
|
|
@end
|
|
|
|
RLM_ARRAY_TYPE(NotARealClass)
|
|
|
|
@interface InvalidLinkingObjectsPropertyProtocol : FakeObject
|
|
@property (readonly) RLMLinkingObjects<NotARealClass> *linkingObjects;
|
|
@end
|
|
|
|
@implementation InvalidLinkingObjectsPropertyProtocol
|
|
|
|
+ (NSDictionary *)linkingObjectsProperties {
|
|
return @{ @"linkingObjects": [RLMPropertyDescriptor descriptorWithClass:SchemaTestsLinkSource.class propertyName:@"ilopp"] };
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@interface InvalidLinkingObjectsPropertyMissingSourcePropertyOfLink : FakeObject
|
|
@property (readonly) RLMLinkingObjects *linkingObjects;
|
|
@property InvalidLinkingObjectsPropertyMissingSourcePropertyOfLink *link;
|
|
@end
|
|
|
|
@implementation InvalidLinkingObjectsPropertyMissingSourcePropertyOfLink
|
|
|
|
+ (NSDictionary *)linkingObjectsProperties {
|
|
return @{ @"linkingObjects": [RLMPropertyDescriptor descriptorWithClass:InvalidLinkingObjectsPropertyMissingSourcePropertyOfLink.class
|
|
propertyName:@"nosuchproperty"] };
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@interface InvalidLinkingObjectsPropertySourcePropertyNotALink : FakeObject
|
|
@property (readonly) RLMLinkingObjects *linkingObjects;
|
|
@property int64_t integer;
|
|
@end
|
|
|
|
@implementation InvalidLinkingObjectsPropertySourcePropertyNotALink
|
|
|
|
+ (NSDictionary *)linkingObjectsProperties {
|
|
return @{ @"linkingObjects": [RLMPropertyDescriptor descriptorWithClass:InvalidLinkingObjectsPropertySourcePropertyNotALink.class
|
|
propertyName:@"integer"] };
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@interface InvalidLinkingObjectsPropertySourcePropertyLinksElsewhere : FakeObject
|
|
@property (readonly) RLMLinkingObjects *linkingObjects;
|
|
@property IntObject *link;
|
|
@end
|
|
|
|
@implementation InvalidLinkingObjectsPropertySourcePropertyLinksElsewhere
|
|
|
|
+ (NSDictionary *)linkingObjectsProperties {
|
|
return @{ @"linkingObjects": [RLMPropertyDescriptor descriptorWithClass:InvalidLinkingObjectsPropertySourcePropertyLinksElsewhere.class
|
|
propertyName:@"link"] };
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@interface SchemaTests : RLMMultiProcessTestCase
|
|
@end
|
|
|
|
@implementation SchemaTests
|
|
|
|
- (void)tearDown {
|
|
RLMSetTreatFakeObjectAsRLMObject(NO);
|
|
[super tearDown];
|
|
}
|
|
|
|
- (void)testNoSchemaForUnmanagedObjectClasses {
|
|
RLMSchema *schema = [RLMSchema sharedSchema];
|
|
XCTAssertNil([schema schemaForClassName:@"RLMObject"]);
|
|
XCTAssertNil([schema schemaForClassName:@"RLMObjectBase"]);
|
|
XCTAssertNil([schema schemaForClassName:@"RLMDynamicObject"]);
|
|
}
|
|
|
|
- (void)testSchemaWithObjectClasses {
|
|
RLMSchema *schema = [RLMSchema schemaWithObjectClasses:@[RLMDynamicObject.class, StringObject.class]];
|
|
XCTAssertEqualObjects((@[@"RLMDynamicObject", @"StringObject"]),
|
|
[[schema.objectSchema valueForKey:@"className"] sortedArrayUsingSelector:@selector(compare:)]);
|
|
XCTAssertNil([RLMSchema.sharedSchema schemaForClassName:@"RLMDynamicObject"]);
|
|
}
|
|
|
|
- (void)testInheritanceInitialization
|
|
{
|
|
Class testClasses[] = {
|
|
[SchemaTestClassBase class],
|
|
[SchemaTestClassFirstChild class],
|
|
[SchemaTestClassSecondChild class],
|
|
[SchemaTestClassLink class],
|
|
[IntObject class]
|
|
};
|
|
|
|
auto pred = ^(Class lft, Class rgt) {
|
|
return (uintptr_t)lft < (uintptr_t)rgt;
|
|
};
|
|
|
|
auto checkSchema = ^(RLMSchema *schema, NSString *className, NSDictionary *properties) {
|
|
RLMObjectSchema *objectSchema = schema[className];
|
|
XCTAssertEqualObjects(className, objectSchema.className);
|
|
XCTAssertEqualObjects(className, [objectSchema.unmanagedClass className]);
|
|
XCTAssertEqualObjects(className, [objectSchema.accessorClass className]);
|
|
|
|
XCTAssertEqual(objectSchema.properties.count, properties.count);
|
|
for (NSString *propName in properties) {
|
|
XCTAssertEqualObjects(properties[propName], [objectSchema[propName] objectClassName]);
|
|
}
|
|
};
|
|
|
|
// Test each permutation of loading orders and verify that all properties
|
|
// are initialized correctly
|
|
std::sort(testClasses, std::end(testClasses), pred);
|
|
unsigned long iteration = 0;
|
|
do @autoreleasepool {
|
|
// Clean up any existing overridden things
|
|
for (Class cls : testClasses) {
|
|
// Ensure that the className method isn't used during schema init
|
|
// as it may not be overriden yet
|
|
Class metaClass = object_getClass(cls);
|
|
IMP imp = imp_implementationWithBlock(^{ return nil; });
|
|
class_replaceMethod(metaClass, @selector(className), imp, "@:");
|
|
}
|
|
|
|
NSMutableArray *objectSchemas = [NSMutableArray arrayWithCapacity:4U];
|
|
for (Class cls : testClasses) {
|
|
[objectSchemas addObject:[RLMObjectSchema schemaForObjectClass:cls]];
|
|
}
|
|
|
|
RLMSchema *schema = [[RLMSchema alloc] init];
|
|
schema.objectSchema = objectSchemas;
|
|
|
|
for (RLMObjectSchema *objectSchema in objectSchemas) {
|
|
NSString *name = [NSString stringWithFormat:@"RLMTestAccessor_%lu_%@", iteration, objectSchema.className];
|
|
objectSchema.accessorClass = RLMManagedAccessorClassForObjectClass(objectSchema.objectClass, objectSchema, name.UTF8String);
|
|
objectSchema.unmanagedClass = RLMUnmanagedAccessorClassForObjectClass(objectSchema.objectClass, objectSchema);
|
|
}
|
|
++iteration;
|
|
|
|
for (Class cls : testClasses) {
|
|
NSString *className = NSStringFromClass(cls);
|
|
|
|
// Restore the className method
|
|
Class metaClass = object_getClass(cls);
|
|
IMP imp = imp_implementationWithBlock(^{ return className; });
|
|
class_replaceMethod(metaClass, @selector(className), imp, "@:");
|
|
}
|
|
|
|
// Verify that each class has the correct properties and className
|
|
// for generated subclasses
|
|
checkSchema(schema, @"SchemaTestClassBase", @{@"baseCol": @"IntObject"});
|
|
checkSchema(schema, @"SchemaTestClassFirstChild", @{@"baseCol": @"IntObject",
|
|
@"firstChildCol": @"IntObject"});
|
|
checkSchema(schema, @"SchemaTestClassSecondChild", @{@"baseCol": @"IntObject",
|
|
@"secondChildCol": @"IntObject"});
|
|
checkSchema(schema, @"SchemaTestClassLink", @{@"base": @"SchemaTestClassBase",
|
|
@"baseArray": @"SchemaTestClassBase",
|
|
@"child": @"SchemaTestClassFirstChild",
|
|
@"childArray": @"SchemaTestClassFirstChild",
|
|
@"secondChild": @"SchemaTestClassSecondChild",
|
|
@"secondChildArray": @"SchemaTestClassSecondChild"});
|
|
|
|
|
|
// Test creating objects of each class
|
|
[self deleteFiles];
|
|
RLMRealm *realm = [self realmWithTestPathAndSchema:schema];
|
|
[realm beginWriteTransaction];
|
|
[realm createObject:@"SchemaTestClassBase" withValue:@{@"baseCol": @[@0]}];
|
|
[realm createObject:@"SchemaTestClassFirstChild" withValue:@{@"baseCol": @[@0], @"firstChildCol": @[@0]}];
|
|
[realm createObject:@"SchemaTestClassSecondChild" withValue:@{@"baseCol": @[@0], @"secondChildCol": @[@0]}];
|
|
[realm commitWriteTransaction];
|
|
} while (std::next_permutation(testClasses, std::end(testClasses), pred));
|
|
}
|
|
|
|
- (void)testObjectSchema
|
|
{
|
|
// Setting up some test data
|
|
|
|
// Due to the automatic initialization of a new realm with all visible classes inheriting from
|
|
// RLMObject, it's difficult to define test cases that verify the absolute correctness of a
|
|
// realm's current type catalogue unless its expected configuration is known at compile time
|
|
// (requires that the set of expected types is always up-to-date). Instead, only a partial
|
|
// verification is performed, which only requires the availability of a well-defined subset of
|
|
// types and ignores any other types that may be included in the realm's type catalogue.
|
|
// If a more fine-grained control with the realm's type inclusion mechanism is introduced later
|
|
// on, these tests should be altered to verify all types.
|
|
|
|
NSArray *expectedTypes = @[@"AllTypesObject",
|
|
@"LinkToAllTypesObject",
|
|
@"StringObject",
|
|
@"IntObject"];
|
|
|
|
NSString *unexpectedType = @"__$ThisTypeShouldNotOccur$__";
|
|
|
|
// Getting the test realm
|
|
NSMutableArray *objectSchema = [NSMutableArray array];
|
|
for (NSString *className in expectedTypes) {
|
|
[objectSchema addObject:[RLMObjectSchema schemaForObjectClass:NSClassFromString(className)]];
|
|
}
|
|
|
|
RLMSchema *schema = [[RLMSchema alloc] init];
|
|
schema.objectSchema = objectSchema;
|
|
|
|
// create realm with schema
|
|
@autoreleasepool { [self realmWithTestPathAndSchema:schema]; }
|
|
|
|
// get dynamic realm and extract schema
|
|
RLMRealm *realm = [self realmWithTestPathAndSchema:nil];
|
|
schema = realm.schema;
|
|
|
|
// Test 1: Does the objectSchema return the right number of object schemas?
|
|
NSArray *objectSchemas = schema.objectSchema;
|
|
|
|
XCTAssertTrue(objectSchemas.count >= expectedTypes.count, @"Expecting %lu object schemas in database found %lu", (unsigned long)expectedTypes.count, (unsigned long)objectSchemas.count);
|
|
|
|
// Test 2: Does the object schema array contain the expected schemas?
|
|
NSUInteger identifiedTypesCount = 0;
|
|
for (NSString *expectedType in expectedTypes) {
|
|
NSUInteger occurrenceCount = 0;
|
|
|
|
for (RLMObjectSchema *objectSchema in objectSchemas) {
|
|
if ([objectSchema.className isEqualToString:expectedType]) {
|
|
occurrenceCount++;
|
|
}
|
|
}
|
|
|
|
XCTAssertEqual(occurrenceCount, (NSUInteger)1, @"Expecting single occurrence of object schema for type %@ found %lu", expectedType, (unsigned long)occurrenceCount);
|
|
|
|
if (occurrenceCount > 0) {
|
|
identifiedTypesCount++;
|
|
}
|
|
}
|
|
|
|
// Test 3: Does the object schema array contains at least the expected classes
|
|
XCTAssertTrue(identifiedTypesCount >= expectedTypes.count, @"Unexpected object schemas in database. Found %lu out of %lu expected", (unsigned long)identifiedTypesCount, (unsigned long)expectedTypes.count);
|
|
|
|
// Test 4: Test querying object schemas using schemaForClassName: for expected types
|
|
for (NSString *expectedType in expectedTypes) {
|
|
XCTAssertNotNil([schema schemaForClassName:expectedType], @"Expecting to find object schema for type %@ in realm using query, found none", expectedType);
|
|
}
|
|
|
|
// Test 5: Test querying object schemas using schemaForClassName: for unexpected types
|
|
XCTAssertNil([schema schemaForClassName:unexpectedType], @"Expecting not to find object schema for type %@ in realm using query, did find", unexpectedType);
|
|
|
|
// Test 6: Test querying object schemas using subscription for unexpected types
|
|
for (NSString *expectedType in expectedTypes) {
|
|
XCTAssertNotNil(schema[expectedType], @"Expecting to find object schema for type %@ in realm using subscription, found none", expectedType);
|
|
}
|
|
|
|
// Test 7: Test querying object schemas using subscription for unexpected types
|
|
XCTAssertThrows(schema[unexpectedType], @"Expecting asking schema for type %@ in realm using subscription to throw", unexpectedType);
|
|
|
|
// Test 8: RLMObject should not appear in the shared object schema
|
|
XCTAssertThrows(RLMSchema.sharedSchema[@"RLMObject"]);
|
|
}
|
|
|
|
- (void)testDescription {
|
|
NSArray *expectedTypes = @[@"AllTypesObject",
|
|
@"StringObject",
|
|
@"IntObject"];
|
|
|
|
NSMutableArray *objectSchema = [NSMutableArray array];
|
|
for (NSString *className in expectedTypes) {
|
|
[objectSchema addObject:[RLMObjectSchema schemaForObjectClass:NSClassFromString(className)]];
|
|
}
|
|
|
|
RLMSchema *schema = [[RLMSchema alloc] init];
|
|
schema.objectSchema = objectSchema;
|
|
|
|
XCTAssertEqualObjects(schema.description, @"Schema {\n"
|
|
@"\tAllTypesObject {\n"
|
|
@"\t\tboolCol {\n"
|
|
@"\t\t\ttype = bool;\n"
|
|
@"\t\t\tobjectClassName = (null);\n"
|
|
@"\t\t\tlinkOriginPropertyName = (null);\n"
|
|
@"\t\t\tindexed = NO;\n"
|
|
@"\t\t\tisPrimary = NO;\n"
|
|
@"\t\t\tarray = NO;\n"
|
|
@"\t\t\toptional = NO;\n"
|
|
@"\t\t}\n"
|
|
@"\t\tintCol {\n"
|
|
@"\t\t\ttype = int;\n"
|
|
@"\t\t\tobjectClassName = (null);\n"
|
|
@"\t\t\tlinkOriginPropertyName = (null);\n"
|
|
@"\t\t\tindexed = NO;\n"
|
|
@"\t\t\tisPrimary = NO;\n"
|
|
@"\t\t\tarray = NO;\n"
|
|
@"\t\t\toptional = NO;\n"
|
|
@"\t\t}\n"
|
|
@"\t\tfloatCol {\n"
|
|
@"\t\t\ttype = float;\n"
|
|
@"\t\t\tobjectClassName = (null);\n"
|
|
@"\t\t\tlinkOriginPropertyName = (null);\n"
|
|
@"\t\t\tindexed = NO;\n"
|
|
@"\t\t\tisPrimary = NO;\n"
|
|
@"\t\t\tarray = NO;\n"
|
|
@"\t\t\toptional = NO;\n"
|
|
@"\t\t}\n"
|
|
@"\t\tdoubleCol {\n"
|
|
@"\t\t\ttype = double;\n"
|
|
@"\t\t\tobjectClassName = (null);\n"
|
|
@"\t\t\tlinkOriginPropertyName = (null);\n"
|
|
@"\t\t\tindexed = NO;\n"
|
|
@"\t\t\tisPrimary = NO;\n"
|
|
@"\t\t\tarray = NO;\n"
|
|
@"\t\t\toptional = NO;\n"
|
|
@"\t\t}\n"
|
|
@"\t\tstringCol {\n"
|
|
@"\t\t\ttype = string;\n"
|
|
@"\t\t\tobjectClassName = (null);\n"
|
|
@"\t\t\tlinkOriginPropertyName = (null);\n"
|
|
@"\t\t\tindexed = NO;\n"
|
|
@"\t\t\tisPrimary = NO;\n"
|
|
@"\t\t\tarray = NO;\n"
|
|
@"\t\t\toptional = NO;\n"
|
|
@"\t\t}\n"
|
|
@"\t\tbinaryCol {\n"
|
|
@"\t\t\ttype = data;\n"
|
|
@"\t\t\tobjectClassName = (null);\n"
|
|
@"\t\t\tlinkOriginPropertyName = (null);\n"
|
|
@"\t\t\tindexed = NO;\n"
|
|
@"\t\t\tisPrimary = NO;\n"
|
|
@"\t\t\tarray = NO;\n"
|
|
@"\t\t\toptional = NO;\n"
|
|
@"\t\t}\n"
|
|
@"\t\tdateCol {\n"
|
|
@"\t\t\ttype = date;\n"
|
|
@"\t\t\tobjectClassName = (null);\n"
|
|
@"\t\t\tlinkOriginPropertyName = (null);\n"
|
|
@"\t\t\tindexed = NO;\n"
|
|
@"\t\t\tisPrimary = NO;\n"
|
|
@"\t\t\tarray = NO;\n"
|
|
@"\t\t\toptional = NO;\n"
|
|
@"\t\t}\n"
|
|
@"\t\tcBoolCol {\n"
|
|
@"\t\t\ttype = bool;\n"
|
|
@"\t\t\tobjectClassName = (null);\n"
|
|
@"\t\t\tlinkOriginPropertyName = (null);\n"
|
|
@"\t\t\tindexed = NO;\n"
|
|
@"\t\t\tisPrimary = NO;\n"
|
|
@"\t\t\tarray = NO;\n"
|
|
@"\t\t\toptional = NO;\n"
|
|
@"\t\t}\n"
|
|
@"\t\tlongCol {\n"
|
|
@"\t\t\ttype = int;\n"
|
|
@"\t\t\tobjectClassName = (null);\n"
|
|
@"\t\t\tlinkOriginPropertyName = (null);\n"
|
|
@"\t\t\tindexed = NO;\n"
|
|
@"\t\t\tisPrimary = NO;\n"
|
|
@"\t\t\tarray = NO;\n"
|
|
@"\t\t\toptional = NO;\n"
|
|
@"\t\t}\n"
|
|
@"\t\tobjectCol {\n"
|
|
@"\t\t\ttype = object;\n"
|
|
@"\t\t\tobjectClassName = StringObject;\n"
|
|
@"\t\t\tlinkOriginPropertyName = (null);\n"
|
|
@"\t\t\tindexed = NO;\n"
|
|
@"\t\t\tisPrimary = NO;\n"
|
|
@"\t\t\tarray = NO;\n"
|
|
@"\t\t\toptional = YES;\n"
|
|
@"\t\t}\n"
|
|
@"\t\tlinkingObjectsCol {\n"
|
|
@"\t\t\ttype = linking objects;\n"
|
|
@"\t\t\tobjectClassName = LinkToAllTypesObject;\n"
|
|
@"\t\t\tlinkOriginPropertyName = allTypesCol;\n"
|
|
@"\t\t\tindexed = NO;\n"
|
|
@"\t\t\tisPrimary = NO;\n"
|
|
@"\t\t\tarray = YES;\n"
|
|
@"\t\t\toptional = NO;\n"
|
|
@"\t\t}\n"
|
|
@"\t}\n"
|
|
@"\tIntObject {\n"
|
|
@"\t\tintCol {\n"
|
|
@"\t\t\ttype = int;\n"
|
|
@"\t\t\tobjectClassName = (null);\n"
|
|
@"\t\t\tlinkOriginPropertyName = (null);\n"
|
|
@"\t\t\tindexed = NO;\n"
|
|
@"\t\t\tisPrimary = NO;\n"
|
|
@"\t\t\tarray = NO;\n"
|
|
@"\t\t\toptional = NO;\n"
|
|
@"\t\t}\n"
|
|
@"\t}\n"
|
|
@"\tStringObject {\n"
|
|
@"\t\tstringCol {\n"
|
|
@"\t\t\ttype = string;\n"
|
|
@"\t\t\tobjectClassName = (null);\n"
|
|
@"\t\t\tlinkOriginPropertyName = (null);\n"
|
|
@"\t\t\tindexed = NO;\n"
|
|
@"\t\t\tisPrimary = NO;\n"
|
|
@"\t\t\tarray = NO;\n"
|
|
@"\t\t\toptional = YES;\n"
|
|
@"\t\t}\n"
|
|
@"\t}\n"
|
|
@"}");
|
|
|
|
}
|
|
|
|
- (void)testClassWithDuplicateProperties
|
|
{
|
|
// If a property is overriden in a child class it should not be picked up more than once.
|
|
RLMObjectSchema *firstSchema = [RLMObjectSchema schemaForObjectClass:SchemaTestClassWithSingleDuplicateProperty.class];
|
|
XCTAssertEqual((int)firstSchema.properties.count, 1);
|
|
RLMObjectSchema *secondSchema = [RLMObjectSchema schemaForObjectClass:SchemaTestClassWithMultipleDuplicateProperties.class];
|
|
XCTAssertEqual((int)secondSchema.properties.count, 2);
|
|
}
|
|
|
|
- (void)testClassWithInvalidPrimaryKey {
|
|
XCTAssertThrows([RLMObjectSchema schemaForObjectClass:InvalidPrimaryKeyType.class]);
|
|
}
|
|
|
|
- (void)testClassWithUnindexableProperty {
|
|
RLMObjectSchema *objectSchema = [RLMObjectSchema schemaForObjectClass:UnindexableProperty.class];
|
|
RLMSchema *schema = [[RLMSchema alloc] init];
|
|
schema.objectSchema = @[objectSchema];
|
|
RLMAssertThrowsWithReasonMatching([self realmWithTestPathAndSchema:schema],
|
|
@"Property 'UnindexableProperty.unindexable' of type 'double' cannot be indexed");
|
|
}
|
|
|
|
- (void)testClassWithRequiredNullableProperties {
|
|
RLMObjectSchema *objectSchema = [RLMObjectSchema schemaForObjectClass:RequiredPropertiesObject.class];
|
|
XCTAssertFalse([objectSchema[@"stringCol"] optional]);
|
|
XCTAssertFalse([objectSchema[@"binaryCol"] optional]);
|
|
}
|
|
|
|
- (void)testClassWithRequiredPrimitiveArrayProperties {
|
|
RLMObjectSchema *objectSchema = [RLMObjectSchema schemaForObjectClass:AllPrimitiveArrays.class];
|
|
XCTAssertFalse(objectSchema[@"intObj"].optional);
|
|
XCTAssertFalse(objectSchema[@"boolObj"].optional);
|
|
XCTAssertFalse(objectSchema[@"floatObj"].optional);
|
|
XCTAssertFalse(objectSchema[@"doubleObj"].optional);
|
|
XCTAssertFalse(objectSchema[@"stringObj"].optional);
|
|
XCTAssertFalse(objectSchema[@"dateObj"].optional);
|
|
XCTAssertFalse(objectSchema[@"dataObj"].optional);
|
|
}
|
|
|
|
- (void)testClassWithOptionalPrimitiveArrayProperties {
|
|
RLMObjectSchema *objectSchema = [RLMObjectSchema schemaForObjectClass:AllOptionalPrimitiveArrays.class];
|
|
XCTAssertTrue(objectSchema[@"intObj"].optional);
|
|
XCTAssertTrue(objectSchema[@"boolObj"].optional);
|
|
XCTAssertTrue(objectSchema[@"floatObj"].optional);
|
|
XCTAssertTrue(objectSchema[@"doubleObj"].optional);
|
|
XCTAssertTrue(objectSchema[@"stringObj"].optional);
|
|
XCTAssertTrue(objectSchema[@"dateObj"].optional);
|
|
XCTAssertTrue(objectSchema[@"dataObj"].optional);
|
|
}
|
|
|
|
- (void)testClassWithRequiredLinkProperty {
|
|
RLMAssertThrowsWithReasonMatching([RLMObjectSchema schemaForObjectClass:RequiredLinkProperty.class], @"cannot be made required.*'object'");
|
|
}
|
|
|
|
- (void)testClassWithInvalidNSNumberProtocolProperty {
|
|
RLMAssertThrowsWithReasonMatching([RLMObjectSchema schemaForObjectClass:InvalidNSNumberProtocolObject.class],
|
|
@"Property 'number' is of type \"NSNumber<NSFastEnumeration>\" which is not a supported NSNumber object type.");
|
|
}
|
|
|
|
- (void)testClassWithInvalidNSNumberNoProtocolProperty {
|
|
RLMAssertThrowsWithReasonMatching([RLMObjectSchema schemaForObjectClass:InvalidNSNumberNoProtocolObject.class], @"Property 'number' requires a protocol defining the contained type");
|
|
}
|
|
|
|
- (void)testClassWithReadWriteLinkingObjectsProperty {
|
|
RLMAssertThrowsWithReasonMatching([RLMObjectSchema schemaForObjectClass:InvalidReadWriteLinkingObjectsProperty.class],
|
|
@"Property 'linkingObjects' must be declared as readonly .* linking objects");
|
|
}
|
|
|
|
- (void)testClassWithInvalidLinkingObjectsPropertiesMethod {
|
|
RLMAssertThrowsWithReasonMatching([RLMObjectSchema schemaForObjectClass:InvalidLinkingObjectsPropertiesMethod.class],
|
|
@"Property 'linkingObjects' .* but \\+linkingObjectsProperties .* class or property");
|
|
}
|
|
|
|
- (void)testClassWithLinkingObjectsPropertyWithProtocol {
|
|
RLMObjectSchema *objectSchema = [RLMObjectSchema schemaForObjectClass:ValidLinkingObjectsPropertyWithProtocol.class];
|
|
XCTAssertEqual(objectSchema[@"linkingObjects"].type, RLMPropertyTypeLinkingObjects);
|
|
}
|
|
|
|
- (void)testClassWithInvalidLinkingObjectsPropertyProtocol {
|
|
RLMAssertThrowsWithReasonMatching([RLMObjectSchema schemaForObjectClass:InvalidLinkingObjectsPropertyProtocol.class],
|
|
@"Property 'linkingObjects' .* type RLMLinkingObjects<NotARealClass>.*conflicting class name.*'SchemaTestsLinkSource'");
|
|
}
|
|
|
|
- (void)testClassWithInvalidLinkingObjectsPropertyMissingSourcePropertyOfLink {
|
|
RLMSetTreatFakeObjectAsRLMObject(YES);
|
|
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
|
|
config.customSchema = [RLMSchema schemaWithObjectClasses:@[ InvalidLinkingObjectsPropertyMissingSourcePropertyOfLink.class ]];
|
|
RLMAssertThrowsWithReasonMatching([RLMRealm realmWithConfiguration:config error:nil],
|
|
@"Property 'InvalidLinkingObjectsPropertyMissingSourcePropertyOfLink.nosuchproperty' declared as origin of linking objects property 'InvalidLinkingObjectsPropertyMissingSourcePropertyOfLink.linkingObjects' does not exist");
|
|
}
|
|
|
|
- (void)testClassWithInvalidLinkingObjectsPropertySourcePropertyNotALink {
|
|
RLMSetTreatFakeObjectAsRLMObject(YES);
|
|
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
|
|
config.customSchema = [RLMSchema schemaWithObjectClasses:@[ InvalidLinkingObjectsPropertySourcePropertyNotALink.class ]];
|
|
RLMAssertThrowsWithReasonMatching([RLMRealm realmWithConfiguration:config error:nil],
|
|
@"Property 'InvalidLinkingObjectsPropertySourcePropertyNotALink.integer' declared as origin of linking objects property 'InvalidLinkingObjectsPropertySourcePropertyNotALink.linkingObjects' is not a link");
|
|
}
|
|
|
|
- (void)testClassWithInvalidLinkingObjectsPropertySourcePropertysLinkElsewhere {
|
|
RLMSetTreatFakeObjectAsRLMObject(YES);
|
|
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
|
|
config.customSchema = [RLMSchema schemaWithObjectClasses:@[ InvalidLinkingObjectsPropertySourcePropertyLinksElsewhere.class, IntObject.class ]];
|
|
RLMAssertThrowsWithReasonMatching([RLMRealm realmWithConfiguration:config error:nil],
|
|
@"Property 'InvalidLinkingObjectsPropertySourcePropertyLinksElsewhere.link' declared as origin of linking objects property 'InvalidLinkingObjectsPropertySourcePropertyLinksElsewhere.linkingObjects' links to type 'IntObject'");
|
|
}
|
|
|
|
- (void)testMixedIsRejected {
|
|
RLMSetTreatFakeObjectAsRLMObject(YES);
|
|
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
|
|
RLMAssertThrowsWithReasonMatching(config.objectClasses = @[[MixedProperty class]],
|
|
@"Property 'mixed' is declared as 'id'.*");
|
|
}
|
|
|
|
// Can't spawn child processes on iOS
|
|
#if !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
|
|
- (void)testPartialSharedSchemaInit {
|
|
if (self.isParent) {
|
|
RLMRunChildAndWait();
|
|
return;
|
|
}
|
|
|
|
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
|
|
|
|
// Verify that opening with class subsets without the shared schema being
|
|
// initialized works
|
|
config.objectClasses = @[IntObject.class];
|
|
@autoreleasepool {
|
|
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
|
|
XCTAssertEqual(1U, realm.schema.objectSchema.count);
|
|
XCTAssertNoThrow(realm.schema[@"IntObject"]);
|
|
}
|
|
|
|
config.objectClasses = @[IntObject.class, StringObject.class];
|
|
@autoreleasepool {
|
|
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
|
|
XCTAssertEqual(2U, realm.schema.objectSchema.count);
|
|
XCTAssertNoThrow(realm.schema[@"IntObject"]);
|
|
XCTAssertNoThrow(realm.schema[@"StringObject"]);
|
|
}
|
|
|
|
// Verify that the shared schema generated afterwards is valid
|
|
config.objectClasses = nil;
|
|
@autoreleasepool {
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
|
|
// Shared schema shouldn't have accessor classes
|
|
for (RLMObjectSchema *objectSchema in realm.schema.objectSchema) {
|
|
const char *actualClassName = class_getName(objectSchema.objectClass);
|
|
XCTAssertEqual(nullptr, strstr(actualClassName, "RLMAccessor"));
|
|
XCTAssertEqual(nullptr, strstr(actualClassName, "RLMUnmanaged"));
|
|
}
|
|
|
|
// Shared schema shouldn't have duplicate entries
|
|
XCTAssertEqual(realm.schema.objectSchema.count,
|
|
[NSSet setWithArray:[realm.schema.objectSchema valueForKey:@"className"]].count);
|
|
|
|
// Shared schema should have the ones that were used in the subsets
|
|
XCTAssertNoThrow(realm.schema[@"IntObject"]);
|
|
XCTAssertNoThrow(realm.schema[@"StringObject"]);
|
|
}
|
|
}
|
|
|
|
- (void)testPartialSharedSchemaInitInheritance {
|
|
if (self.isParent) {
|
|
RLMRunChildAndWait();
|
|
return;
|
|
}
|
|
|
|
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
|
|
config.objectClasses = @[NumberObject.class];
|
|
|
|
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
|
|
XCTAssertEqual(1U, realm.schema.objectSchema.count);
|
|
XCTAssertEqualObjects(@"NumberObject", [[[[NumberObject alloc] init] objectSchema] className]);
|
|
// Verify that child class doesn't use the parent class's schema
|
|
XCTAssertEqualObjects(@"NumberDefaultsObject", [[[[NumberDefaultsObject alloc] init] objectSchema] className]);
|
|
}
|
|
|
|
- (void)testCreateUnmanagedObjectWithUninitializedSchema {
|
|
if (self.isParent) {
|
|
RLMRunChildAndWait();
|
|
return;
|
|
}
|
|
XCTAssertTrue(RLMSchema.partialSharedSchema.objectSchema.count == 0);
|
|
XCTAssertNoThrow([[IntObject alloc] initWithValue:@[@0]]);
|
|
XCTAssertNoThrow([[NonDefaultObject alloc] initWithValue:@[@0]]);
|
|
}
|
|
|
|
- (void)testCreateUnmanagedObjectWithNestedObjectWithUninitializedSchema {
|
|
if (self.isParent) {
|
|
RLMRunChildAndWait();
|
|
return;
|
|
}
|
|
XCTAssertTrue(RLMSchema.partialSharedSchema.objectSchema.count == 0);
|
|
XCTAssertNoThrow([[IntegerArrayPropertyObject alloc] initWithValue:(@[@0, @[@[@0]]])]);
|
|
XCTAssertNoThrow([[NonDefaultArrayObject alloc] initWithValue:@[@[@[@0]]]]);
|
|
XCTAssertNoThrow([[MutualLink1Object alloc] initWithValue:@[@[@{}]]]);
|
|
}
|
|
|
|
- (void)testKVOSubclassesDoNotAppearInSchema {
|
|
if (self.isParent) {
|
|
RLMRunChildAndWait();
|
|
return;
|
|
}
|
|
|
|
auto obj = [IntObject new];
|
|
[obj addObserver:self forKeyPath:@"intCol" options:0 context:0];
|
|
for (RLMObjectSchema *objectSchema in RLMRealm.defaultRealm.schema.objectSchema) {
|
|
XCTAssertEqual([objectSchema.className rangeOfString:@"RLM:"].location, NSNotFound);
|
|
}
|
|
[obj removeObserver:self forKeyPath:@"intCol" context:0];
|
|
}
|
|
|
|
#if !DEBUG
|
|
- (void)testMultipleProcessesTryingToInitializeSchema {
|
|
RLMRealm *syncRealm = [self realmWithTestPath];
|
|
|
|
if (!self.isParent) {
|
|
RLMSchema *schema = [RLMSchema schemaWithObjectClasses:@[IntObject.class]];
|
|
RLMProperty *prop = ((NSArray *)[schema.objectSchema[0] properties])[0];
|
|
prop.type = RLMPropertyTypeFloat;
|
|
|
|
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
|
|
config.customSchema = schema;
|
|
config.schemaVersion = 1;
|
|
|
|
[syncRealm transactionWithBlock:^{
|
|
[StringObject createInRealm:syncRealm withValue:@[@""]];
|
|
}];
|
|
|
|
[RLMRealm realmWithConfiguration:config error:nil];
|
|
return;
|
|
}
|
|
|
|
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
|
|
config.objectClasses = @[IntObject.class];
|
|
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
|
|
|
|
// Hold a write transaction to prevent the child processes from performing
|
|
// the migration immediately
|
|
[realm beginWriteTransaction];
|
|
|
|
// Spawn a bunch of child processes which will all try to perform the migration
|
|
dispatch_group_t group = dispatch_group_create();
|
|
for (int i = 0; i < 5; ++i) {
|
|
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
|
|
RLMRunChildAndWait();
|
|
});
|
|
}
|
|
|
|
// Wait for all five to be immediately before the point where they will try
|
|
// to perform the migration. There's inherently a race condition here in
|
|
// as in theory all but one process could be suspended immediately after
|
|
// committing the signalling commit and then not get woken up until after
|
|
// the migration is complete, but in practice it won't happen and we can't
|
|
// wait for someone to be waiting on a mutex.
|
|
XCTestExpectation *notificationFired = [self expectationWithDescription:@"notification fired"];
|
|
RLMNotificationToken *token = [syncRealm addNotificationBlock:^(NSString *, RLMRealm *) {
|
|
if ([StringObject allObjectsInRealm:syncRealm].count == 5) {
|
|
[notificationFired fulfill];
|
|
}
|
|
}];
|
|
[self waitForExpectationsWithTimeout:10.0 handler:nil];
|
|
[token invalidate];
|
|
|
|
// Release the write transaction and let them run
|
|
[realm cancelWriteTransaction];
|
|
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
|
|
}
|
|
#endif
|
|
|
|
- (void)testOpeningFileWithDifferentClassSubsetsInDifferentProcesses {
|
|
if (!self.isParent) {
|
|
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
|
|
config.objectClasses = @[StringObject.class];
|
|
|
|
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
|
|
XCTAssertEqual(1U, realm.schema.objectSchema.count);
|
|
|
|
// Verify that the StringObject table actually exists
|
|
[realm beginWriteTransaction];
|
|
[StringObject createInRealm:realm withValue:@[@""]];
|
|
[realm commitWriteTransaction];
|
|
return;
|
|
}
|
|
|
|
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
|
|
config.objectClasses = @[IntObject.class];
|
|
|
|
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
|
|
realm.autorefresh = false;
|
|
XCTAssertEqual(1U, realm.schema.objectSchema.count);
|
|
|
|
[realm beginWriteTransaction];
|
|
[IntObject createInRealm:realm withValue:@[@1]];
|
|
[realm commitWriteTransaction];
|
|
|
|
RLMRunChildAndWait();
|
|
|
|
// Should be able to advance over the transaction creating a new table and
|
|
// inserting a row into it
|
|
XCTAssertNoThrow([realm refresh]);
|
|
|
|
// Verify that the IntObject table didn't break
|
|
XCTAssertEqual(1, [[IntObject allObjectsInRealm:realm].firstObject intCol]);
|
|
[realm beginWriteTransaction];
|
|
[IntObject createInRealm:realm withValue:@[@2]];
|
|
|
|
// StringObject still isn't usable in this process since it isn't in the
|
|
// class subset
|
|
XCTAssertThrows([StringObject createInRealm:realm withValue:@[@""]]);
|
|
[realm commitWriteTransaction];
|
|
}
|
|
|
|
- (void)testAddingIndexToExistingColumnInBackgroundProcess {
|
|
if (!self.isParent) {
|
|
RLMSchema *schema = [RLMSchema schemaWithObjectClasses:@[IntObject.class]];
|
|
RLMObjectSchema *objectSchema = schema.objectSchema[0];
|
|
RLMProperty *prop = objectSchema.properties[0];
|
|
prop.indexed = YES;
|
|
|
|
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
|
|
config.customSchema = schema;
|
|
[RLMRealm realmWithConfiguration:config error:nil];
|
|
return;
|
|
}
|
|
|
|
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
|
|
config.objectClasses = @[IntObject.class];
|
|
|
|
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
|
|
realm.autorefresh = false;
|
|
XCTAssertEqual(1U, realm.schema.objectSchema.count);
|
|
|
|
// Insert a value to ensure stuff actually happens when the index is added/removed
|
|
[realm beginWriteTransaction];
|
|
[IntObject createInRealm:realm withValue:@[@1]];
|
|
[realm commitWriteTransaction];
|
|
|
|
RLMRunChildAndWait();
|
|
|
|
// Should accept the index change
|
|
XCTAssertNoThrow([realm refresh]);
|
|
}
|
|
|
|
- (void)testRemovingIndexFromExistingColumnInBackgroundProcess {
|
|
if (!self.isParent) {
|
|
RLMSchema *schema = [RLMSchema schemaWithObjectClasses:@[IndexedStringObject.class]];
|
|
RLMObjectSchema *objectSchema = schema.objectSchema[0];
|
|
RLMProperty *prop = objectSchema.properties[0];
|
|
prop.indexed = NO;
|
|
|
|
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
|
|
config.customSchema = schema;
|
|
[RLMRealm realmWithConfiguration:config error:nil];
|
|
return;
|
|
}
|
|
|
|
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
|
|
config.objectClasses = @[IndexedStringObject.class];
|
|
|
|
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
|
|
realm.autorefresh = false;
|
|
XCTAssertEqual(1U, realm.schema.objectSchema.count);
|
|
|
|
// Insert a value to ensure stuff actually happens when the index is added/removed
|
|
[realm beginWriteTransaction];
|
|
[IndexedStringObject createInRealm:realm withValue:@[@"1"]];
|
|
[realm commitWriteTransaction];
|
|
|
|
RLMRunChildAndWait();
|
|
|
|
// Should accept the index change
|
|
XCTAssertNoThrow([realm refresh]);
|
|
}
|
|
|
|
- (void)testMigratingToLaterVersionInBackgroundProcess {
|
|
if (!self.isParent) {
|
|
RLMSchema *schema = [RLMSchema schemaWithObjectClasses:@[IntObject.class]];
|
|
RLMObjectSchema *objectSchema = schema.objectSchema[0];
|
|
RLMProperty *prop = objectSchema.properties[0];
|
|
prop.type = RLMPropertyTypeFloat;
|
|
|
|
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
|
|
config.customSchema = schema;
|
|
config.schemaVersion = 1;
|
|
[RLMRealm realmWithConfiguration:config error:nil];
|
|
return;
|
|
}
|
|
|
|
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
|
|
config.objectClasses = @[IntObject.class];
|
|
|
|
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
|
|
realm.autorefresh = false;
|
|
[realm beginWriteTransaction];
|
|
[IntObject createInRealm:realm withValue:@[@1]];
|
|
[realm commitWriteTransaction];
|
|
|
|
RLMRunChildAndWait();
|
|
|
|
// Should fail to refresh since we can't use later versions of the file due
|
|
// to the schema change
|
|
XCTAssertThrows([realm refresh]);
|
|
XCTAssertThrows([realm beginWriteTransaction]);
|
|
|
|
// Should have been left in a sensible state after the errors
|
|
XCTAssertEqual(1, [[IntObject allObjectsInRealm:realm].firstObject intCol]);
|
|
}
|
|
|
|
- (void)testInsertingColumnsInBackgroundProcess {
|
|
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
|
|
config.schemaMode = realm::SchemaMode::Additive;
|
|
if (!self.isParent) {
|
|
config.dynamic = true;
|
|
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
|
|
[realm beginWriteTransaction];
|
|
realm->_info[@"IntObject"].table()->insert_column(0, realm::type_String, "col");
|
|
[realm commitWriteTransaction];
|
|
return;
|
|
}
|
|
|
|
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
|
|
[realm transactionWithBlock:^{
|
|
[IntObject createInRealm:realm withValue:@[@5]];
|
|
}];
|
|
|
|
RLMRunChildAndWait();
|
|
XCTAssertEqual(5, [[IntObject allObjectsInRealm:realm].firstObject intCol]);
|
|
XCTAssertEqual(1U, [IntObject objectsInRealm:realm where:@"intCol = 5"].count);
|
|
|
|
__block IntObject *io = [IntObject new];
|
|
io.intCol = 6;
|
|
[realm transactionWithBlock:^{ [realm addObject:io]; }];
|
|
XCTAssertEqual(io.intCol, 6);
|
|
XCTAssertEqualObjects(io[@"intCol"], @6);
|
|
|
|
[realm transactionWithBlock:^{ io = [IntObject createInRealm:realm withValue:@[@7]]; }];
|
|
XCTAssertEqual(io.intCol, 7);
|
|
|
|
[realm transactionWithBlock:^{ io = [IntObject createInRealm:realm withValue:@{@"intCol": @8}]; }];
|
|
XCTAssertEqual(io.intCol, 8);
|
|
|
|
[realm transactionWithBlock:^{ io.intCol = 9; }];
|
|
XCTAssertEqual(io.intCol, 9);
|
|
|
|
[realm transactionWithBlock:^{ io[@"intCol"] = @10; }];
|
|
XCTAssertEqual(io.intCol, 10);
|
|
|
|
// Create query, add column, run query
|
|
RLMResults *query = [IntObject objectsInRealm:realm where:@"intCol > 5"];
|
|
RLMRunChildAndWait();
|
|
XCTAssertEqual(query.count, 3U);
|
|
|
|
// Create query, create TV, add column, reevaluate query
|
|
query = [IntObject objectsInRealm:realm where:@"intCol > 5"];
|
|
(void)[query lastObject];
|
|
RLMRunChildAndWait();
|
|
XCTAssertEqual(query.count, 3U);
|
|
}
|
|
#endif
|
|
|
|
@end
|
|
|