//////////////////////////////////////////////////////////////////////////// // // 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 import RealmSwift import Realm import Realm.Dynamic import Foundation @discardableResult private func realmWithSingleClassProperties(_ fileURL: URL, className: String, properties: [AnyObject]) -> RLMRealm { let schema = RLMSchema() let objectSchema = RLMObjectSchema(className: className, objectClass: MigrationObject.self, properties: properties) schema.objectSchema = [objectSchema] let config = RLMRealmConfiguration() config.fileURL = fileURL config.customSchema = schema return try! RLMRealm(configuration: config) } private func dynamicRealm(_ fileURL: URL) -> RLMRealm { let config = RLMRealmConfiguration() config.fileURL = fileURL config.dynamic = true return try! RLMRealm(configuration: config) } class MigrationTests: TestCase { // MARK: Utility methods // create realm at path and test version is 0 private func createAndTestRealmAtURL(_ fileURL: URL) { autoreleasepool { _ = try! Realm(fileURL: fileURL) return } XCTAssertEqual(0, try! schemaVersionAtURL(fileURL), "Initial version should be 0") } // migrate realm at path and ensure migration private func migrateAndTestRealm(_ fileURL: URL, shouldRun: Bool = true, schemaVersion: UInt64 = 1, autoMigration: Bool = false, block: MigrationBlock? = nil) { var didRun = false let config = Realm.Configuration(fileURL: fileURL, schemaVersion: schemaVersion, migrationBlock: { migration, oldSchemaVersion in if let block = block { block(migration, oldSchemaVersion) } didRun = true return }) if autoMigration { autoreleasepool { _ = try! Realm(configuration: config) } } else { try! Realm.performMigration(for: config) } XCTAssertEqual(didRun, shouldRun) } private func migrateAndTestDefaultRealm(_ schemaVersion: UInt64 = 1, block: @escaping MigrationBlock) { migrateAndTestRealm(defaultRealmURL(), schemaVersion: schemaVersion, block: block) let config = Realm.Configuration(fileURL: defaultRealmURL(), schemaVersion: schemaVersion) Realm.Configuration.defaultConfiguration = config } // MARK: Test cases func testSetDefaultRealmSchemaVersion() { createAndTestRealmAtURL(defaultRealmURL()) var didRun = false let config = Realm.Configuration(fileURL: defaultRealmURL(), schemaVersion: 1, migrationBlock: { _, _ in didRun = true }) Realm.Configuration.defaultConfiguration = config try! Realm.performMigration() XCTAssertEqual(didRun, true) XCTAssertEqual(1, try! schemaVersionAtURL(defaultRealmURL())) } func testSetSchemaVersion() { createAndTestRealmAtURL(testRealmURL()) migrateAndTestRealm(testRealmURL()) XCTAssertEqual(1, try! schemaVersionAtURL(testRealmURL())) } func testSchemaVersionAtURL() { assertFails(.fail) { // Version should throw before Realm creation try schemaVersionAtURL(defaultRealmURL()) } _ = try! Realm() XCTAssertEqual(0, try! schemaVersionAtURL(defaultRealmURL()), "Initial version should be 0") do { _ = try schemaVersionAtURL(URL(fileURLWithPath: "/dev/null")) XCTFail("Expected .filePermissionDenied or .fileAccess, but no error was raised") } catch Realm.Error.filePermissionDenied { // Success! } catch Realm.Error.fileAccess { // Success! } catch { XCTFail("Expected .filePermissionDenied or .fileAccess, got \(error)") } } func testMigrateRealm() { createAndTestRealmAtURL(testRealmURL()) // manually migrate (autoMigration == false) migrateAndTestRealm(testRealmURL(), shouldRun: true, autoMigration: false) // calling again should be no-op migrateAndTestRealm(testRealmURL(), shouldRun: false, autoMigration: false) // test auto-migration migrateAndTestRealm(testRealmURL(), shouldRun: true, schemaVersion: 2, autoMigration: true) } func testMigrationProperties() { let prop = RLMProperty(name: "stringCol", type: RLMPropertyType.int, objectClassName: nil, linkOriginPropertyName: nil, indexed: false, optional: false) _ = autoreleasepool { realmWithSingleClassProperties(defaultRealmURL(), className: "SwiftStringObject", properties: [prop]) } migrateAndTestDefaultRealm { migration, _ in XCTAssertEqual(migration.oldSchema.objectSchema.count, 1) XCTAssertGreaterThan(migration.newSchema.objectSchema.count, 1) XCTAssertEqual(migration.oldSchema.objectSchema[0].properties.count, 1) XCTAssertEqual(migration.newSchema["SwiftStringObject"]!.properties.count, 1) XCTAssertEqual(migration.oldSchema["SwiftStringObject"]!.properties[0].type, PropertyType.int) XCTAssertEqual(migration.newSchema["SwiftStringObject"]!["stringCol"]!.type, PropertyType.string) } } func testEnumerate() { autoreleasepool { _ = try! Realm() } migrateAndTestDefaultRealm { migration, _ in migration.enumerateObjects(ofType: "SwiftStringObject", { _, _ in XCTFail("No objects to enumerate") }) migration.enumerateObjects(ofType: "NoSuchClass", { _, _ in }) // shouldn't throw } autoreleasepool { // add object try! Realm().write { try! Realm().create(SwiftStringObject.self, value: ["string"]) return } } migrateAndTestDefaultRealm(2) { migration, _ in var count = 0 migration.enumerateObjects(ofType: "SwiftStringObject", { oldObj, newObj in XCTAssertEqual(newObj!.objectSchema.className, "SwiftStringObject") XCTAssertEqual(oldObj!.objectSchema.className, "SwiftStringObject") XCTAssertEqual((newObj!["stringCol"] as! String), "string") XCTAssertEqual((oldObj!["stringCol"] as! String), "string") self.assertThrows(oldObj!["noSuchCol"] as! String) self.assertThrows(newObj!["noSuchCol"] as! String) count += 1 }) XCTAssertEqual(count, 1) } _ = autoreleasepool { try! Realm().write { try! Realm().create(SwiftArrayPropertyObject.self, value: ["string", [["array"]], [[2]]]) } } migrateAndTestDefaultRealm(3) { migration, _ in migration.enumerateObjects(ofType: "SwiftArrayPropertyObject") { oldObject, newObject in XCTAssertTrue(oldObject! as AnyObject is MigrationObject) XCTAssertTrue(newObject! as AnyObject is MigrationObject) XCTAssertTrue(oldObject!["array"]! is List) XCTAssertTrue(newObject!["array"]! is List) } } autoreleasepool { try! Realm().write { let soo = SwiftOptionalObject() soo.optNSStringCol = "NSString" soo.optStringCol = "String" soo.optBinaryCol = Data() soo.optDateCol = Date() soo.optIntCol.value = 1 soo.optInt8Col.value = 2 soo.optInt16Col.value = 3 soo.optInt32Col.value = 4 soo.optInt64Col.value = 5 soo.optFloatCol.value = 6.1 soo.optDoubleCol.value = 7.2 soo.optBoolCol.value = true try! Realm().add(soo) } } migrateAndTestDefaultRealm(4) { migration, _ in migration.enumerateObjects(ofType: "SwiftOptionalObject") { oldObject, newObject in XCTAssertTrue(oldObject! as AnyObject is MigrationObject) XCTAssertTrue(newObject! as AnyObject is MigrationObject) XCTAssertTrue(oldObject!["optNSStringCol"]! is NSString) XCTAssertTrue(newObject!["optNSStringCol"]! is NSString) XCTAssertTrue(oldObject!["optStringCol"]! is String) XCTAssertTrue(newObject!["optStringCol"]! is String) XCTAssertTrue(oldObject!["optBinaryCol"]! is Data) XCTAssertTrue(newObject!["optBinaryCol"]! is Data) XCTAssertTrue(oldObject!["optDateCol"]! is Date) XCTAssertTrue(newObject!["optDateCol"]! is Date) XCTAssertTrue(oldObject!["optIntCol"]! is Int) XCTAssertTrue(newObject!["optIntCol"]! is Int) XCTAssertTrue(oldObject!["optInt8Col"]! is Int) XCTAssertTrue(newObject!["optInt8Col"]! is Int) XCTAssertTrue(oldObject!["optInt16Col"]! is Int) XCTAssertTrue(newObject!["optInt16Col"]! is Int) XCTAssertTrue(oldObject!["optInt32Col"]! is Int) XCTAssertTrue(newObject!["optInt32Col"]! is Int) XCTAssertTrue(oldObject!["optInt64Col"]! is Int) XCTAssertTrue(newObject!["optInt64Col"]! is Int) XCTAssertTrue(oldObject!["optFloatCol"]! is Float) XCTAssertTrue(newObject!["optFloatCol"]! is Float) XCTAssertTrue(oldObject!["optDoubleCol"]! is Double) XCTAssertTrue(newObject!["optDoubleCol"]! is Double) XCTAssertTrue(oldObject!["optBoolCol"]! is Bool) XCTAssertTrue(newObject!["optBoolCol"]! is Bool) } } } func testEnumerateObjectsAfterDeleteObjects() { autoreleasepool { // add object try! Realm().write { try! Realm().create(SwiftStringObject.self, value: ["1"]) try! Realm().create(SwiftStringObject.self, value: ["2"]) try! Realm().create(SwiftStringObject.self, value: ["3"]) try! Realm().create(SwiftIntObject.self, value: [1]) try! Realm().create(SwiftIntObject.self, value: [2]) try! Realm().create(SwiftIntObject.self, value: [3]) try! Realm().create(SwiftBoolObject.self, value: [true]) try! Realm().create(SwiftBoolObject.self, value: [false]) try! Realm().create(SwiftBoolObject.self, value: [true]) } } migrateAndTestDefaultRealm(1) { migration, _ in var count = 0 migration.enumerateObjects(ofType: "SwiftStringObject") { oldObj, newObj in XCTAssertEqual(newObj!["stringCol"] as! String, oldObj!["stringCol"] as! String) if oldObj!["stringCol"] as! String == "2" { migration.delete(newObj!) } } migration.enumerateObjects(ofType: "SwiftStringObject") { oldObj, newObj in XCTAssertEqual(newObj!["stringCol"] as! String, oldObj!["stringCol"] as! String) count += 1 } XCTAssertEqual(count, 2) count = 0 migration.enumerateObjects(ofType: "SwiftIntObject") { oldObj, newObj in XCTAssertEqual(newObj!["intCol"] as! Int, oldObj!["intCol"] as! Int) if oldObj!["intCol"] as! Int == 1 { migration.delete(newObj!) } } migration.enumerateObjects(ofType: "SwiftIntObject") { oldObj, newObj in XCTAssertEqual(newObj!["intCol"] as! Int, oldObj!["intCol"] as! Int) count += 1 } XCTAssertEqual(count, 2) migration.enumerateObjects(ofType: "SwiftBoolObject") { oldObj, newObj in XCTAssertEqual(newObj!["boolCol"] as! Bool, oldObj!["boolCol"] as! Bool) migration.delete(newObj!) } migration.enumerateObjects(ofType: "SwiftBoolObject") { _, _ in XCTFail("This line should not executed since all objects have been deleted.") } } } func testEnumerateObjectsAfterDeleteInsertObjects() { autoreleasepool { // add object try! Realm().write { try! Realm().create(SwiftStringObject.self, value: ["1"]) try! Realm().create(SwiftStringObject.self, value: ["2"]) try! Realm().create(SwiftStringObject.self, value: ["3"]) try! Realm().create(SwiftIntObject.self, value: [1]) try! Realm().create(SwiftIntObject.self, value: [2]) try! Realm().create(SwiftIntObject.self, value: [3]) try! Realm().create(SwiftBoolObject.self, value: [true]) try! Realm().create(SwiftBoolObject.self, value: [false]) try! Realm().create(SwiftBoolObject.self, value: [true]) } } migrateAndTestDefaultRealm(1) { migration, _ in var count = 0 migration.enumerateObjects(ofType: "SwiftStringObject") { oldObj, newObj in XCTAssertEqual(newObj!["stringCol"] as! String, oldObj!["stringCol"] as! String) if oldObj!["stringCol"] as! String == "2" { migration.delete(newObj!) migration.create("SwiftStringObject", value: ["A"]) } } migration.enumerateObjects(ofType: "SwiftStringObject") { oldObj, newObj in XCTAssertEqual(newObj!["stringCol"] as! String, oldObj!["stringCol"] as! String) count += 1 } XCTAssertEqual(count, 2) count = 0 migration.enumerateObjects(ofType: "SwiftIntObject") { oldObj, newObj in XCTAssertEqual(newObj!["intCol"] as! Int, oldObj!["intCol"] as! Int) if oldObj!["intCol"] as! Int == 1 { migration.delete(newObj!) migration.create("SwiftIntObject", value: [0]) } } migration.enumerateObjects(ofType: "SwiftIntObject") { oldObj, newObj in XCTAssertEqual(newObj!["intCol"] as! Int, oldObj!["intCol"] as! Int) count += 1 } XCTAssertEqual(count, 2) migration.enumerateObjects(ofType: "SwiftBoolObject") { oldObj, newObj in XCTAssertEqual(newObj!["boolCol"] as! Bool, oldObj!["boolCol"] as! Bool) migration.delete(newObj!) migration.create("SwiftBoolObject", value: [false]) } migration.enumerateObjects(ofType: "SwiftBoolObject") { _, _ in XCTFail("This line should not executed since all objects have been deleted.") } } } func testEnumerateObjectsAfterDeleteData() { autoreleasepool { // add object try! Realm().write { try! Realm().create(SwiftStringObject.self, value: ["1"]) try! Realm().create(SwiftStringObject.self, value: ["2"]) try! Realm().create(SwiftStringObject.self, value: ["3"]) } } migrateAndTestDefaultRealm(1) { migration, _ in var count = 0 migration.enumerateObjects(ofType: "SwiftStringObject") { _, _ in count += 1 } XCTAssertEqual(count, 3) migration.deleteData(forType: "SwiftStringObject") migration.create("SwiftStringObject", value: ["A"]) count = 0 migration.enumerateObjects(ofType: "SwiftStringObject") { _, _ in count += 1 } XCTAssertEqual(count, 0) } } func testCreate() { autoreleasepool { _ = try! Realm() } migrateAndTestDefaultRealm { migration, _ in migration.create("SwiftStringObject", value: ["string"]) migration.create("SwiftStringObject", value: ["stringCol": "string"]) migration.create("SwiftStringObject") self.assertThrows(migration.create("NoSuchObject", value: [])) } let objects = try! Realm().objects(SwiftStringObject.self) XCTAssertEqual(objects.count, 3) XCTAssertEqual(objects[0].stringCol, "string") XCTAssertEqual(objects[1].stringCol, "string") XCTAssertEqual(objects[2].stringCol, "") } func testDelete() { autoreleasepool { try! Realm().write { try! Realm().create(SwiftStringObject.self, value: ["string1"]) try! Realm().create(SwiftStringObject.self, value: ["string2"]) return } } migrateAndTestDefaultRealm { migration, _ in var deleted = false migration.enumerateObjects(ofType: "SwiftStringObject", { _, newObj in if deleted == false { migration.delete(newObj!) deleted = true } }) } XCTAssertEqual(try! Realm().objects(SwiftStringObject.self).count, 1) } func testDeleteData() { autoreleasepool { let prop = RLMProperty(name: "id", type: .int, objectClassName: nil, linkOriginPropertyName: nil, indexed: false, optional: false) let realm = realmWithSingleClassProperties(defaultRealmURL(), className: "DeletedClass", properties: [prop]) try! realm.transaction { realm.createObject("DeletedClass", withValue: [0]) } } migrateAndTestDefaultRealm { migration, oldSchemaVersion in XCTAssertEqual(oldSchemaVersion, 0, "Initial schema version should be 0") XCTAssertTrue(migration.deleteData(forType: "DeletedClass")) XCTAssertFalse(migration.deleteData(forType: "NoSuchClass")) migration.create(SwiftStringObject.className(), value: ["migration"]) XCTAssertTrue(migration.deleteData(forType: SwiftStringObject.className())) } let realm = dynamicRealm(defaultRealmURL()) XCTAssertNil(realm.schema.schema(forClassName: "DeletedClass")) XCTAssertEqual(0, realm.allObjects("SwiftStringObject").count) } func testRenameProperty() { autoreleasepool { let prop = RLMProperty(name: "before_stringCol", type: .string, objectClassName: nil, linkOriginPropertyName: nil, indexed: false, optional: false) autoreleasepool { let realm = realmWithSingleClassProperties(defaultRealmURL(), className: "SwiftStringObject", properties: [prop]) try! realm.transaction { realm.createObject("SwiftStringObject", withValue: ["a"]) } } migrateAndTestDefaultRealm { migration, _ in XCTAssertEqual(migration.oldSchema.objectSchema[0].properties.count, 1) migration.renameProperty(onType: "SwiftStringObject", from: "before_stringCol", to: "stringCol") } let realm = dynamicRealm(defaultRealmURL()) XCTAssertEqual(realm.schema.schema(forClassName: "SwiftStringObject")!.properties.count, 1) XCTAssertEqual(1, realm.allObjects("SwiftStringObject").count) XCTAssertEqual("a", realm.allObjects("SwiftStringObject").firstObject()?["stringCol"] as? String) } } // test getting/setting all property types func testMigrationObject() { autoreleasepool { try! Realm().write { let object = SwiftObject() object.boolCol = true object.objectCol = SwiftBoolObject(value: [true]) object.arrayCol.append(SwiftBoolObject(value: [false])) try! Realm().add(object) return } } migrateAndTestDefaultRealm { migration, _ in var enumerated = false migration.enumerateObjects(ofType: "SwiftObject", { oldObj, newObj in XCTAssertEqual((oldObj!["boolCol"] as! Bool), true) XCTAssertEqual((newObj!["boolCol"] as! Bool), true) XCTAssertEqual((oldObj!["intCol"] as! Int), 123) XCTAssertEqual((newObj!["intCol"] as! Int), 123) XCTAssertEqual((oldObj!["intEnumCol"] as! Int), 1) XCTAssertEqual((newObj!["intEnumCol"] as! Int), 1) XCTAssertEqual((oldObj!["floatCol"] as! Float), 1.23 as Float) XCTAssertEqual((newObj!["floatCol"] as! Float), 1.23 as Float) XCTAssertEqual((oldObj!["doubleCol"] as! Double), 12.3 as Double) XCTAssertEqual((newObj!["doubleCol"] as! Double), 12.3 as Double) let binaryCol = "a".data(using: String.Encoding.utf8)! XCTAssertEqual((oldObj!["binaryCol"] as! Data), binaryCol) XCTAssertEqual((newObj!["binaryCol"] as! Data), binaryCol) let dateCol = Date(timeIntervalSince1970: 1) XCTAssertEqual((oldObj!["dateCol"] as! Date), dateCol) XCTAssertEqual((newObj!["dateCol"] as! Date), dateCol) // FIXME - test that casting to SwiftBoolObject throws XCTAssertEqual(((oldObj!["objectCol"] as! MigrationObject)["boolCol"] as! Bool), true) XCTAssertEqual(((newObj!["objectCol"] as! MigrationObject)["boolCol"] as! Bool), true) XCTAssertEqual((oldObj!["arrayCol"] as! List).count, 1) XCTAssertEqual(((oldObj!["arrayCol"] as! List)[0]["boolCol"] as! Bool), false) XCTAssertEqual((newObj!["arrayCol"] as! List).count, 1) XCTAssertEqual(((newObj!["arrayCol"] as! List)[0]["boolCol"] as! Bool), false) // edit all values newObj!["boolCol"] = false newObj!["intCol"] = 1 newObj!["intEnumCol"] = IntEnum.value2.rawValue newObj!["floatCol"] = 1.0 newObj!["doubleCol"] = 10.0 newObj!["binaryCol"] = Data(bytes: "b", count: 1) newObj!["dateCol"] = Date(timeIntervalSince1970: 2) let falseObj = SwiftBoolObject(value: [false]) newObj!["objectCol"] = falseObj var list = newObj!["arrayCol"] as! List list[0]["boolCol"] = true list.append(newObj!["objectCol"] as! MigrationObject) let trueObj = migration.create(SwiftBoolObject.className(), value: [true]) list.append(trueObj) // verify list property list = newObj!["arrayCol"] as! List XCTAssertEqual(list.count, 3) XCTAssertEqual((list[0]["boolCol"] as! Bool), true) XCTAssertEqual((list[1]["boolCol"] as! Bool), false) XCTAssertEqual((list[2]["boolCol"] as! Bool), true) list = newObj!.dynamicList("arrayCol") XCTAssertEqual(list.count, 3) XCTAssertEqual((list[0]["boolCol"] as! Bool), true) XCTAssertEqual((list[1]["boolCol"] as! Bool), false) XCTAssertEqual((list[2]["boolCol"] as! Bool), true) self.assertThrows(newObj!.value(forKey: "noSuchKey")) self.assertThrows(newObj!.setValue(1, forKey: "noSuchKey")) // set it again newObj!["arrayCol"] = [falseObj, trueObj] XCTAssertEqual(list.count, 2) newObj!["arrayCol"] = [SwiftBoolObject(value: [false])] XCTAssertEqual(list.count, 1) XCTAssertEqual((list[0]["boolCol"] as! Bool), false) self.assertMatches(newObj!.description, "SwiftObject \\{\n\tboolCol = 0;\n\tintCol = 1;\n\tintEnumCol = 3;\n\tfloatCol = 1;\n\tdoubleCol = 10;\n\tstringCol = a;\n\tbinaryCol = <.*62.*>;\n\tdateCol = 1970-01-01 00:00:02 \\+0000;\n\tobjectCol = SwiftBoolObject \\{\n\t\tboolCol = 0;\n\t\\};\n\tarrayCol = List <0x[0-9a-f]+> \\(\n\t\t\\[0\\] SwiftBoolObject \\{\n\t\t\tboolCol = 0;\n\t\t\\}\n\t\\);\n\\}") enumerated = true }) XCTAssertEqual(enumerated, true) let newObj = migration.create(SwiftObject.className()) // swiftlint:next:disable line_length self.assertMatches(newObj.description, "SwiftObject \\{\n\tboolCol = 0;\n\tintCol = 123;\n\tintEnumCol = 1;\n\tfloatCol = 1\\.23;\n\tdoubleCol = 12\\.3;\n\tstringCol = a;\n\tbinaryCol = <.*61.*>;\n\tdateCol = 1970-01-01 00:00:01 \\+0000;\n\tobjectCol = SwiftBoolObject \\{\n\t\tboolCol = 0;\n\t\\};\n\tarrayCol = List <0x[0-9a-f]+> \\(\n\t\n\t\\);\n\\}") } // refresh to update realm try! Realm().refresh() // check edited values let object = try! Realm().objects(SwiftObject.self).first! XCTAssertEqual(object.boolCol, false) XCTAssertEqual(object.intCol, 1) XCTAssertEqual(object.floatCol, 1.0 as Float) XCTAssertEqual(object.doubleCol, 10.0) XCTAssertEqual(object.binaryCol, Data(bytes: "b", count: 1)) XCTAssertEqual(object.dateCol, Date(timeIntervalSince1970: 2)) XCTAssertEqual(object.objectCol!.boolCol, false) XCTAssertEqual(object.arrayCol.count, 1) XCTAssertEqual(object.arrayCol[0].boolCol, false) // make sure we added new bool objects as object property and in the list XCTAssertEqual(try! Realm().objects(SwiftBoolObject.self).count, 6) } func testFailOnSchemaMismatch() { let prop = RLMProperty(name: "name", type: RLMPropertyType.string, objectClassName: nil, linkOriginPropertyName: nil, indexed: false, optional: false) _ = autoreleasepool { realmWithSingleClassProperties(defaultRealmURL(), className: "SwiftEmployeeObject", properties: [prop]) } let config = Realm.Configuration(fileURL: defaultRealmURL(), objectTypes: [SwiftEmployeeObject.self]) autoreleasepool { assertFails(.schemaMismatch) { try Realm(configuration: config) } } } func testDeleteRealmIfMigrationNeededWithSetCustomSchema() { let prop = RLMProperty(name: "name", type: RLMPropertyType.string, objectClassName: nil, linkOriginPropertyName: nil, indexed: false, optional: false) _ = autoreleasepool { realmWithSingleClassProperties(defaultRealmURL(), className: "SwiftEmployeeObject", properties: [prop]) } var config = Realm.Configuration(fileURL: defaultRealmURL(), objectTypes: [SwiftEmployeeObject.self]) config.migrationBlock = { _, _ in XCTFail("Migration block should not be called") } config.deleteRealmIfMigrationNeeded = true autoreleasepool { assertSucceeds { _ = try Realm(configuration: config) } } } func testDeleteRealmIfMigrationNeeded() { autoreleasepool { _ = try! Realm(configuration: Realm.Configuration(fileURL: defaultRealmURL())) } let objectSchema = RLMObjectSchema(forObjectClass: SwiftEmployeeObject.self) objectSchema.properties = Array(objectSchema.properties[0..<1]) let metaClass: AnyClass = objc_getMetaClass("RLMSchema") as! AnyClass let imp = imp_implementationWithBlock(unsafeBitCast({ () -> RLMSchema in let schema = RLMSchema() schema.objectSchema = [objectSchema] return schema } as @convention(block)() -> (RLMSchema), to: AnyObject.self)) let originalImp = class_getMethodImplementation(metaClass, #selector(RLMObjectBase.sharedSchema)) class_replaceMethod(metaClass, #selector(RLMObjectBase.sharedSchema), imp, "@@:") autoreleasepool { assertFails(.schemaMismatch) { try Realm() } } let migrationBlock: MigrationBlock = { _, _ in XCTFail("Migration block should not be called") } let config = Realm.Configuration(fileURL: defaultRealmURL(), migrationBlock: migrationBlock, deleteRealmIfMigrationNeeded: true) assertSucceeds { _ = try Realm(configuration: config) } class_replaceMethod(metaClass, #selector(RLMObjectBase.sharedSchema), originalImp!, "@@:") } }