MigrationTests.swift 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2014 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. import XCTest
  19. import RealmSwift
  20. import Realm
  21. import Realm.Dynamic
  22. import Foundation
  23. @discardableResult
  24. private func realmWithSingleClassProperties(_ fileURL: URL, className: String, properties: [AnyObject]) -> RLMRealm {
  25. let schema = RLMSchema()
  26. let objectSchema = RLMObjectSchema(className: className, objectClass: MigrationObject.self, properties: properties)
  27. schema.objectSchema = [objectSchema]
  28. let config = RLMRealmConfiguration()
  29. config.fileURL = fileURL
  30. config.customSchema = schema
  31. return try! RLMRealm(configuration: config)
  32. }
  33. private func dynamicRealm(_ fileURL: URL) -> RLMRealm {
  34. let config = RLMRealmConfiguration()
  35. config.fileURL = fileURL
  36. config.dynamic = true
  37. return try! RLMRealm(configuration: config)
  38. }
  39. class MigrationTests: TestCase {
  40. // MARK: Utility methods
  41. // create realm at path and test version is 0
  42. private func createAndTestRealmAtURL(_ fileURL: URL) {
  43. autoreleasepool {
  44. _ = try! Realm(fileURL: fileURL)
  45. return
  46. }
  47. XCTAssertEqual(0, try! schemaVersionAtURL(fileURL), "Initial version should be 0")
  48. }
  49. // migrate realm at path and ensure migration
  50. private func migrateAndTestRealm(_ fileURL: URL, shouldRun: Bool = true, schemaVersion: UInt64 = 1,
  51. autoMigration: Bool = false, block: MigrationBlock? = nil) {
  52. var didRun = false
  53. let config = Realm.Configuration(fileURL: fileURL, schemaVersion: schemaVersion,
  54. migrationBlock: { migration, oldSchemaVersion in
  55. if let block = block {
  56. block(migration, oldSchemaVersion)
  57. }
  58. didRun = true
  59. return
  60. })
  61. if autoMigration {
  62. autoreleasepool {
  63. _ = try! Realm(configuration: config)
  64. }
  65. } else {
  66. try! Realm.performMigration(for: config)
  67. }
  68. XCTAssertEqual(didRun, shouldRun)
  69. }
  70. private func migrateAndTestDefaultRealm(_ schemaVersion: UInt64 = 1, block: @escaping MigrationBlock) {
  71. migrateAndTestRealm(defaultRealmURL(), schemaVersion: schemaVersion, block: block)
  72. let config = Realm.Configuration(fileURL: defaultRealmURL(),
  73. schemaVersion: schemaVersion)
  74. Realm.Configuration.defaultConfiguration = config
  75. }
  76. // MARK: Test cases
  77. func testSetDefaultRealmSchemaVersion() {
  78. createAndTestRealmAtURL(defaultRealmURL())
  79. var didRun = false
  80. let config = Realm.Configuration(fileURL: defaultRealmURL(), schemaVersion: 1,
  81. migrationBlock: { _, _ in didRun = true })
  82. Realm.Configuration.defaultConfiguration = config
  83. try! Realm.performMigration()
  84. XCTAssertEqual(didRun, true)
  85. XCTAssertEqual(1, try! schemaVersionAtURL(defaultRealmURL()))
  86. }
  87. func testSetSchemaVersion() {
  88. createAndTestRealmAtURL(testRealmURL())
  89. migrateAndTestRealm(testRealmURL())
  90. XCTAssertEqual(1, try! schemaVersionAtURL(testRealmURL()))
  91. }
  92. func testSchemaVersionAtURL() {
  93. assertFails(.fail) {
  94. // Version should throw before Realm creation
  95. try schemaVersionAtURL(defaultRealmURL())
  96. }
  97. _ = try! Realm()
  98. XCTAssertEqual(0, try! schemaVersionAtURL(defaultRealmURL()),
  99. "Initial version should be 0")
  100. do {
  101. _ = try schemaVersionAtURL(URL(fileURLWithPath: "/dev/null"))
  102. XCTFail("Expected .filePermissionDenied or .fileAccess, but no error was raised")
  103. } catch Realm.Error.filePermissionDenied {
  104. // Success!
  105. } catch Realm.Error.fileAccess {
  106. // Success!
  107. } catch {
  108. XCTFail("Expected .filePermissionDenied or .fileAccess, got \(error)")
  109. }
  110. }
  111. func testMigrateRealm() {
  112. createAndTestRealmAtURL(testRealmURL())
  113. // manually migrate (autoMigration == false)
  114. migrateAndTestRealm(testRealmURL(), shouldRun: true, autoMigration: false)
  115. // calling again should be no-op
  116. migrateAndTestRealm(testRealmURL(), shouldRun: false, autoMigration: false)
  117. // test auto-migration
  118. migrateAndTestRealm(testRealmURL(), shouldRun: true, schemaVersion: 2, autoMigration: true)
  119. }
  120. func testMigrationProperties() {
  121. let prop = RLMProperty(name: "stringCol", type: RLMPropertyType.int, objectClassName: nil,
  122. linkOriginPropertyName: nil, indexed: false, optional: false)
  123. _ = autoreleasepool {
  124. realmWithSingleClassProperties(defaultRealmURL(), className: "SwiftStringObject", properties: [prop])
  125. }
  126. migrateAndTestDefaultRealm { migration, _ in
  127. XCTAssertEqual(migration.oldSchema.objectSchema.count, 1)
  128. XCTAssertGreaterThan(migration.newSchema.objectSchema.count, 1)
  129. XCTAssertEqual(migration.oldSchema.objectSchema[0].properties.count, 1)
  130. XCTAssertEqual(migration.newSchema["SwiftStringObject"]!.properties.count, 1)
  131. XCTAssertEqual(migration.oldSchema["SwiftStringObject"]!.properties[0].type, PropertyType.int)
  132. XCTAssertEqual(migration.newSchema["SwiftStringObject"]!["stringCol"]!.type, PropertyType.string)
  133. }
  134. }
  135. func testEnumerate() {
  136. autoreleasepool {
  137. _ = try! Realm()
  138. }
  139. migrateAndTestDefaultRealm { migration, _ in
  140. migration.enumerateObjects(ofType: "SwiftStringObject", { _, _ in
  141. XCTFail("No objects to enumerate")
  142. })
  143. migration.enumerateObjects(ofType: "NoSuchClass", { _, _ in }) // shouldn't throw
  144. }
  145. autoreleasepool {
  146. // add object
  147. try! Realm().write {
  148. try! Realm().create(SwiftStringObject.self, value: ["string"])
  149. return
  150. }
  151. }
  152. migrateAndTestDefaultRealm(2) { migration, _ in
  153. var count = 0
  154. migration.enumerateObjects(ofType: "SwiftStringObject", { oldObj, newObj in
  155. XCTAssertEqual(newObj!.objectSchema.className, "SwiftStringObject")
  156. XCTAssertEqual(oldObj!.objectSchema.className, "SwiftStringObject")
  157. XCTAssertEqual((newObj!["stringCol"] as! String), "string")
  158. XCTAssertEqual((oldObj!["stringCol"] as! String), "string")
  159. self.assertThrows(oldObj!["noSuchCol"] as! String)
  160. self.assertThrows(newObj!["noSuchCol"] as! String)
  161. count += 1
  162. })
  163. XCTAssertEqual(count, 1)
  164. }
  165. autoreleasepool {
  166. try! Realm().write {
  167. try! Realm().create(SwiftArrayPropertyObject.self, value: ["string", [["array"]], [[2]]])
  168. }
  169. }
  170. migrateAndTestDefaultRealm(3) { migration, _ in
  171. migration.enumerateObjects(ofType: "SwiftArrayPropertyObject") { oldObject, newObject in
  172. XCTAssertTrue(oldObject! as AnyObject is MigrationObject)
  173. XCTAssertTrue(newObject! as AnyObject is MigrationObject)
  174. XCTAssertTrue(oldObject!["array"]! is List<MigrationObject>)
  175. XCTAssertTrue(newObject!["array"]! is List<MigrationObject>)
  176. }
  177. }
  178. autoreleasepool {
  179. try! Realm().write {
  180. let soo = SwiftOptionalObject()
  181. soo.optNSStringCol = "NSString"
  182. soo.optStringCol = "String"
  183. soo.optBinaryCol = Data()
  184. soo.optDateCol = Date()
  185. soo.optIntCol.value = 1
  186. soo.optInt8Col.value = 2
  187. soo.optInt16Col.value = 3
  188. soo.optInt32Col.value = 4
  189. soo.optInt64Col.value = 5
  190. soo.optFloatCol.value = 6.1
  191. soo.optDoubleCol.value = 7.2
  192. soo.optBoolCol.value = true
  193. try! Realm().add(soo)
  194. }
  195. }
  196. migrateAndTestDefaultRealm(4) { migration, _ in
  197. migration.enumerateObjects(ofType: "SwiftOptionalObject") { oldObject, newObject in
  198. XCTAssertTrue(oldObject! as AnyObject is MigrationObject)
  199. XCTAssertTrue(newObject! as AnyObject is MigrationObject)
  200. XCTAssertTrue(oldObject!["optNSStringCol"]! is NSString)
  201. XCTAssertTrue(newObject!["optNSStringCol"]! is NSString)
  202. XCTAssertTrue(oldObject!["optStringCol"]! is String)
  203. XCTAssertTrue(newObject!["optStringCol"]! is String)
  204. XCTAssertTrue(oldObject!["optBinaryCol"]! is Data)
  205. XCTAssertTrue(newObject!["optBinaryCol"]! is Data)
  206. XCTAssertTrue(oldObject!["optDateCol"]! is Date)
  207. XCTAssertTrue(newObject!["optDateCol"]! is Date)
  208. XCTAssertTrue(oldObject!["optIntCol"]! is Int)
  209. XCTAssertTrue(newObject!["optIntCol"]! is Int)
  210. XCTAssertTrue(oldObject!["optInt8Col"]! is Int)
  211. XCTAssertTrue(newObject!["optInt8Col"]! is Int)
  212. XCTAssertTrue(oldObject!["optInt16Col"]! is Int)
  213. XCTAssertTrue(newObject!["optInt16Col"]! is Int)
  214. XCTAssertTrue(oldObject!["optInt32Col"]! is Int)
  215. XCTAssertTrue(newObject!["optInt32Col"]! is Int)
  216. XCTAssertTrue(oldObject!["optInt64Col"]! is Int)
  217. XCTAssertTrue(newObject!["optInt64Col"]! is Int)
  218. XCTAssertTrue(oldObject!["optFloatCol"]! is Float)
  219. XCTAssertTrue(newObject!["optFloatCol"]! is Float)
  220. XCTAssertTrue(oldObject!["optDoubleCol"]! is Double)
  221. XCTAssertTrue(newObject!["optDoubleCol"]! is Double)
  222. XCTAssertTrue(oldObject!["optBoolCol"]! is Bool)
  223. XCTAssertTrue(newObject!["optBoolCol"]! is Bool)
  224. }
  225. }
  226. }
  227. func testEnumerateObjectsAfterDeleteObjects() {
  228. autoreleasepool {
  229. // add object
  230. try! Realm().write {
  231. try! Realm().create(SwiftStringObject.self, value: ["1"])
  232. try! Realm().create(SwiftStringObject.self, value: ["2"])
  233. try! Realm().create(SwiftStringObject.self, value: ["3"])
  234. try! Realm().create(SwiftIntObject.self, value: [1])
  235. try! Realm().create(SwiftIntObject.self, value: [2])
  236. try! Realm().create(SwiftIntObject.self, value: [3])
  237. try! Realm().create(SwiftBoolObject.self, value: [true])
  238. try! Realm().create(SwiftBoolObject.self, value: [false])
  239. try! Realm().create(SwiftBoolObject.self, value: [true])
  240. }
  241. }
  242. migrateAndTestDefaultRealm(1) { migration, _ in
  243. var count = 0
  244. migration.enumerateObjects(ofType: "SwiftStringObject") { oldObj, newObj in
  245. XCTAssertEqual(newObj!["stringCol"] as! String, oldObj!["stringCol"] as! String)
  246. if oldObj!["stringCol"] as! String == "2" {
  247. migration.delete(newObj!)
  248. }
  249. }
  250. migration.enumerateObjects(ofType: "SwiftStringObject") { oldObj, newObj in
  251. XCTAssertEqual(newObj!["stringCol"] as! String, oldObj!["stringCol"] as! String)
  252. count += 1
  253. }
  254. XCTAssertEqual(count, 2)
  255. count = 0
  256. migration.enumerateObjects(ofType: "SwiftIntObject") { oldObj, newObj in
  257. XCTAssertEqual(newObj!["intCol"] as! Int, oldObj!["intCol"] as! Int)
  258. if oldObj!["intCol"] as! Int == 1 {
  259. migration.delete(newObj!)
  260. }
  261. }
  262. migration.enumerateObjects(ofType: "SwiftIntObject") { oldObj, newObj in
  263. XCTAssertEqual(newObj!["intCol"] as! Int, oldObj!["intCol"] as! Int)
  264. count += 1
  265. }
  266. XCTAssertEqual(count, 2)
  267. migration.enumerateObjects(ofType: "SwiftBoolObject") { oldObj, newObj in
  268. XCTAssertEqual(newObj!["boolCol"] as! Bool, oldObj!["boolCol"] as! Bool)
  269. migration.delete(newObj!)
  270. }
  271. migration.enumerateObjects(ofType: "SwiftBoolObject") { _, _ in
  272. XCTFail("This line should not executed since all objects have been deleted.")
  273. }
  274. }
  275. }
  276. func testEnumerateObjectsAfterDeleteInsertObjects() {
  277. autoreleasepool {
  278. // add object
  279. try! Realm().write {
  280. try! Realm().create(SwiftStringObject.self, value: ["1"])
  281. try! Realm().create(SwiftStringObject.self, value: ["2"])
  282. try! Realm().create(SwiftStringObject.self, value: ["3"])
  283. try! Realm().create(SwiftIntObject.self, value: [1])
  284. try! Realm().create(SwiftIntObject.self, value: [2])
  285. try! Realm().create(SwiftIntObject.self, value: [3])
  286. try! Realm().create(SwiftBoolObject.self, value: [true])
  287. try! Realm().create(SwiftBoolObject.self, value: [false])
  288. try! Realm().create(SwiftBoolObject.self, value: [true])
  289. }
  290. }
  291. migrateAndTestDefaultRealm(1) { migration, _ in
  292. var count = 0
  293. migration.enumerateObjects(ofType: "SwiftStringObject") { oldObj, newObj in
  294. XCTAssertEqual(newObj!["stringCol"] as! String, oldObj!["stringCol"] as! String)
  295. if oldObj!["stringCol"] as! String == "2" {
  296. migration.delete(newObj!)
  297. migration.create("SwiftStringObject", value: ["A"])
  298. }
  299. }
  300. migration.enumerateObjects(ofType: "SwiftStringObject") { oldObj, newObj in
  301. XCTAssertEqual(newObj!["stringCol"] as! String, oldObj!["stringCol"] as! String)
  302. count += 1
  303. }
  304. XCTAssertEqual(count, 2)
  305. count = 0
  306. migration.enumerateObjects(ofType: "SwiftIntObject") { oldObj, newObj in
  307. XCTAssertEqual(newObj!["intCol"] as! Int, oldObj!["intCol"] as! Int)
  308. if oldObj!["intCol"] as! Int == 1 {
  309. migration.delete(newObj!)
  310. migration.create("SwiftIntObject", value: [0])
  311. }
  312. }
  313. migration.enumerateObjects(ofType: "SwiftIntObject") { oldObj, newObj in
  314. XCTAssertEqual(newObj!["intCol"] as! Int, oldObj!["intCol"] as! Int)
  315. count += 1
  316. }
  317. XCTAssertEqual(count, 2)
  318. migration.enumerateObjects(ofType: "SwiftBoolObject") { oldObj, newObj in
  319. XCTAssertEqual(newObj!["boolCol"] as! Bool, oldObj!["boolCol"] as! Bool)
  320. migration.delete(newObj!)
  321. migration.create("SwiftBoolObject", value: [false])
  322. }
  323. migration.enumerateObjects(ofType: "SwiftBoolObject") { _, _ in
  324. XCTFail("This line should not executed since all objects have been deleted.")
  325. }
  326. }
  327. }
  328. func testEnumerateObjectsAfterDeleteData() {
  329. autoreleasepool {
  330. // add object
  331. try! Realm().write {
  332. try! Realm().create(SwiftStringObject.self, value: ["1"])
  333. try! Realm().create(SwiftStringObject.self, value: ["2"])
  334. try! Realm().create(SwiftStringObject.self, value: ["3"])
  335. }
  336. }
  337. migrateAndTestDefaultRealm(1) { migration, _ in
  338. var count = 0
  339. migration.enumerateObjects(ofType: "SwiftStringObject") { _, _ in
  340. count += 1
  341. }
  342. XCTAssertEqual(count, 3)
  343. migration.deleteData(forType: "SwiftStringObject")
  344. migration.create("SwiftStringObject", value: ["A"])
  345. count = 0
  346. migration.enumerateObjects(ofType: "SwiftStringObject") { _, _ in
  347. count += 1
  348. }
  349. XCTAssertEqual(count, 0)
  350. }
  351. }
  352. func testCreate() {
  353. autoreleasepool {
  354. _ = try! Realm()
  355. }
  356. migrateAndTestDefaultRealm { migration, _ in
  357. migration.create("SwiftStringObject", value: ["string"])
  358. migration.create("SwiftStringObject", value: ["stringCol": "string"])
  359. migration.create("SwiftStringObject")
  360. self.assertThrows(migration.create("NoSuchObject", value: []))
  361. }
  362. let objects = try! Realm().objects(SwiftStringObject.self)
  363. XCTAssertEqual(objects.count, 3)
  364. XCTAssertEqual(objects[0].stringCol, "string")
  365. XCTAssertEqual(objects[1].stringCol, "string")
  366. XCTAssertEqual(objects[2].stringCol, "")
  367. }
  368. func testDelete() {
  369. autoreleasepool {
  370. try! Realm().write {
  371. try! Realm().create(SwiftStringObject.self, value: ["string1"])
  372. try! Realm().create(SwiftStringObject.self, value: ["string2"])
  373. return
  374. }
  375. }
  376. migrateAndTestDefaultRealm { migration, _ in
  377. var deleted = false
  378. migration.enumerateObjects(ofType: "SwiftStringObject", { _, newObj in
  379. if deleted == false {
  380. migration.delete(newObj!)
  381. deleted = true
  382. }
  383. })
  384. }
  385. XCTAssertEqual(try! Realm().objects(SwiftStringObject.self).count, 1)
  386. }
  387. func testDeleteData() {
  388. autoreleasepool {
  389. let prop = RLMProperty(name: "id", type: .int, objectClassName: nil,
  390. linkOriginPropertyName: nil, indexed: false, optional: false)
  391. let realm = realmWithSingleClassProperties(defaultRealmURL(),
  392. className: "DeletedClass", properties: [prop])
  393. try! realm.transaction {
  394. realm.createObject("DeletedClass", withValue: [0])
  395. }
  396. }
  397. migrateAndTestDefaultRealm { migration, oldSchemaVersion in
  398. XCTAssertEqual(oldSchemaVersion, 0, "Initial schema version should be 0")
  399. XCTAssertTrue(migration.deleteData(forType: "DeletedClass"))
  400. XCTAssertFalse(migration.deleteData(forType: "NoSuchClass"))
  401. migration.create(SwiftStringObject.className(), value: ["migration"])
  402. XCTAssertTrue(migration.deleteData(forType: SwiftStringObject.className()))
  403. }
  404. let realm = dynamicRealm(defaultRealmURL())
  405. XCTAssertNil(realm.schema.schema(forClassName: "DeletedClass"))
  406. XCTAssertEqual(0, realm.allObjects("SwiftStringObject").count)
  407. }
  408. func testRenameProperty() {
  409. autoreleasepool {
  410. let prop = RLMProperty(name: "before_stringCol", type: .string, objectClassName: nil,
  411. linkOriginPropertyName: nil, indexed: false, optional: false)
  412. autoreleasepool {
  413. let realm = realmWithSingleClassProperties(defaultRealmURL(), className: "SwiftStringObject",
  414. properties: [prop])
  415. try! realm.transaction {
  416. realm.createObject("SwiftStringObject", withValue: ["a"])
  417. }
  418. }
  419. migrateAndTestDefaultRealm { migration, _ in
  420. XCTAssertEqual(migration.oldSchema.objectSchema[0].properties.count, 1)
  421. migration.renameProperty(onType: "SwiftStringObject", from: "before_stringCol",
  422. to: "stringCol")
  423. }
  424. let realm = dynamicRealm(defaultRealmURL())
  425. XCTAssertEqual(realm.schema.schema(forClassName: "SwiftStringObject")!.properties.count, 1)
  426. XCTAssertEqual(1, realm.allObjects("SwiftStringObject").count)
  427. XCTAssertEqual("a", realm.allObjects("SwiftStringObject").firstObject()?["stringCol"] as? String)
  428. }
  429. }
  430. // test getting/setting all property types
  431. func testMigrationObject() {
  432. autoreleasepool {
  433. try! Realm().write {
  434. let object = SwiftObject()
  435. object.boolCol = true
  436. object.objectCol = SwiftBoolObject(value: [true])
  437. object.arrayCol.append(SwiftBoolObject(value: [false]))
  438. try! Realm().add(object)
  439. return
  440. }
  441. }
  442. migrateAndTestDefaultRealm { migration, _ in
  443. var enumerated = false
  444. migration.enumerateObjects(ofType: "SwiftObject", { oldObj, newObj in
  445. XCTAssertEqual((oldObj!["boolCol"] as! Bool), true)
  446. XCTAssertEqual((newObj!["boolCol"] as! Bool), true)
  447. XCTAssertEqual((oldObj!["intCol"] as! Int), 123)
  448. XCTAssertEqual((newObj!["intCol"] as! Int), 123)
  449. XCTAssertEqual((oldObj!["floatCol"] as! Float), 1.23 as Float)
  450. XCTAssertEqual((newObj!["floatCol"] as! Float), 1.23 as Float)
  451. XCTAssertEqual((oldObj!["doubleCol"] as! Double), 12.3 as Double)
  452. XCTAssertEqual((newObj!["doubleCol"] as! Double), 12.3 as Double)
  453. let binaryCol = "a".data(using: String.Encoding.utf8)!
  454. XCTAssertEqual((oldObj!["binaryCol"] as! Data), binaryCol)
  455. XCTAssertEqual((newObj!["binaryCol"] as! Data), binaryCol)
  456. let dateCol = Date(timeIntervalSince1970: 1)
  457. XCTAssertEqual((oldObj!["dateCol"] as! Date), dateCol)
  458. XCTAssertEqual((newObj!["dateCol"] as! Date), dateCol)
  459. // FIXME - test that casting to SwiftBoolObject throws
  460. XCTAssertEqual(((oldObj!["objectCol"] as! MigrationObject)["boolCol"] as! Bool), true)
  461. XCTAssertEqual(((newObj!["objectCol"] as! MigrationObject)["boolCol"] as! Bool), true)
  462. XCTAssertEqual((oldObj!["arrayCol"] as! List<MigrationObject>).count, 1)
  463. XCTAssertEqual(((oldObj!["arrayCol"] as! List<MigrationObject>)[0]["boolCol"] as! Bool), false)
  464. XCTAssertEqual((newObj!["arrayCol"] as! List<MigrationObject>).count, 1)
  465. XCTAssertEqual(((newObj!["arrayCol"] as! List<MigrationObject>)[0]["boolCol"] as! Bool), false)
  466. // edit all values
  467. newObj!["boolCol"] = false
  468. newObj!["intCol"] = 1
  469. newObj!["floatCol"] = 1.0
  470. newObj!["doubleCol"] = 10.0
  471. newObj!["binaryCol"] = Data(bytes: "b", count: 1)
  472. newObj!["dateCol"] = Date(timeIntervalSince1970: 2)
  473. let falseObj = SwiftBoolObject(value: [false])
  474. newObj!["objectCol"] = falseObj
  475. var list = newObj!["arrayCol"] as! List<MigrationObject>
  476. list[0]["boolCol"] = true
  477. list.append(newObj!["objectCol"] as! MigrationObject)
  478. let trueObj = migration.create(SwiftBoolObject.className(), value: [true])
  479. list.append(trueObj)
  480. // verify list property
  481. list = newObj!["arrayCol"] as! List<MigrationObject>
  482. XCTAssertEqual(list.count, 3)
  483. XCTAssertEqual((list[0]["boolCol"] as! Bool), true)
  484. XCTAssertEqual((list[1]["boolCol"] as! Bool), false)
  485. XCTAssertEqual((list[2]["boolCol"] as! Bool), true)
  486. list = newObj!.dynamicList("arrayCol")
  487. XCTAssertEqual(list.count, 3)
  488. XCTAssertEqual((list[0]["boolCol"] as! Bool), true)
  489. XCTAssertEqual((list[1]["boolCol"] as! Bool), false)
  490. XCTAssertEqual((list[2]["boolCol"] as! Bool), true)
  491. self.assertThrows(newObj!.value(forKey: "noSuchKey"))
  492. self.assertThrows(newObj!.setValue(1, forKey: "noSuchKey"))
  493. // set it again
  494. newObj!["arrayCol"] = [falseObj, trueObj]
  495. XCTAssertEqual(list.count, 2)
  496. newObj!["arrayCol"] = [SwiftBoolObject(value: [false])]
  497. XCTAssertEqual(list.count, 1)
  498. XCTAssertEqual((list[0]["boolCol"] as! Bool), false)
  499. self.assertMatches(newObj!.description, "SwiftObject \\{\n\tboolCol = 0;\n\tintCol = 1;\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<SwiftBoolObject> <0x[0-9a-f]+> \\(\n\t\t\\[0\\] SwiftBoolObject \\{\n\t\t\tboolCol = 0;\n\t\t\\}\n\t\\);\n\\}")
  500. enumerated = true
  501. })
  502. XCTAssertEqual(enumerated, true)
  503. let newObj = migration.create(SwiftObject.className())
  504. // swiftlint:next:disable line_length
  505. self.assertMatches(newObj.description, "SwiftObject \\{\n\tboolCol = 0;\n\tintCol = 123;\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<SwiftBoolObject> <0x[0-9a-f]+> \\(\n\t\n\t\\);\n\\}")
  506. }
  507. // refresh to update realm
  508. try! Realm().refresh()
  509. // check edited values
  510. let object = try! Realm().objects(SwiftObject.self).first!
  511. XCTAssertEqual(object.boolCol, false)
  512. XCTAssertEqual(object.intCol, 1)
  513. XCTAssertEqual(object.floatCol, 1.0 as Float)
  514. XCTAssertEqual(object.doubleCol, 10.0)
  515. XCTAssertEqual(object.binaryCol, Data(bytes: "b", count: 1))
  516. XCTAssertEqual(object.dateCol, Date(timeIntervalSince1970: 2))
  517. XCTAssertEqual(object.objectCol!.boolCol, false)
  518. XCTAssertEqual(object.arrayCol.count, 1)
  519. XCTAssertEqual(object.arrayCol[0].boolCol, false)
  520. // make sure we added new bool objects as object property and in the list
  521. XCTAssertEqual(try! Realm().objects(SwiftBoolObject.self).count, 6)
  522. }
  523. func testFailOnSchemaMismatch() {
  524. let prop = RLMProperty(name: "name", type: RLMPropertyType.string, objectClassName: nil,
  525. linkOriginPropertyName: nil, indexed: false, optional: false)
  526. _ = autoreleasepool {
  527. realmWithSingleClassProperties(defaultRealmURL(), className: "SwiftEmployeeObject", properties: [prop])
  528. }
  529. let config = Realm.Configuration(fileURL: defaultRealmURL(), objectTypes: [SwiftEmployeeObject.self])
  530. autoreleasepool {
  531. assertFails(.schemaMismatch) {
  532. try Realm(configuration: config)
  533. }
  534. }
  535. }
  536. func testDeleteRealmIfMigrationNeededWithSetCustomSchema() {
  537. let prop = RLMProperty(name: "name", type: RLMPropertyType.string, objectClassName: nil,
  538. linkOriginPropertyName: nil, indexed: false, optional: false)
  539. _ = autoreleasepool {
  540. realmWithSingleClassProperties(defaultRealmURL(), className: "SwiftEmployeeObject", properties: [prop])
  541. }
  542. var config = Realm.Configuration(fileURL: defaultRealmURL(), objectTypes: [SwiftEmployeeObject.self])
  543. config.migrationBlock = { _, _ in
  544. XCTFail("Migration block should not be called")
  545. }
  546. config.deleteRealmIfMigrationNeeded = true
  547. autoreleasepool {
  548. assertSucceeds {
  549. _ = try Realm(configuration: config)
  550. }
  551. }
  552. }
  553. func testDeleteRealmIfMigrationNeeded() {
  554. autoreleasepool { _ = try! Realm(configuration: Realm.Configuration(fileURL: defaultRealmURL())) }
  555. let objectSchema = RLMObjectSchema(forObjectClass: SwiftEmployeeObject.self)
  556. objectSchema.properties = Array(objectSchema.properties[0..<1])
  557. let metaClass: AnyClass = objc_getMetaClass("RLMSchema") as! AnyClass
  558. let imp = imp_implementationWithBlock(unsafeBitCast({ () -> RLMSchema in
  559. let schema = RLMSchema()
  560. schema.objectSchema = [objectSchema]
  561. return schema
  562. } as @convention(block)() -> (RLMSchema), to: AnyObject.self))
  563. let originalImp = class_getMethodImplementation(metaClass, #selector(RLMObjectBase.sharedSchema))
  564. class_replaceMethod(metaClass, #selector(RLMObjectBase.sharedSchema), imp, "@@:")
  565. autoreleasepool {
  566. assertFails(.schemaMismatch) {
  567. try Realm()
  568. }
  569. }
  570. let migrationBlock: MigrationBlock = { _, _ in
  571. XCTFail("Migration block should not be called")
  572. }
  573. let config = Realm.Configuration(fileURL: defaultRealmURL(),
  574. migrationBlock: migrationBlock,
  575. deleteRealmIfMigrationNeeded: true)
  576. assertSucceeds {
  577. _ = try Realm(configuration: config)
  578. }
  579. class_replaceMethod(metaClass, #selector(RLMObjectBase.sharedSchema), originalImp!, "@@:")
  580. }
  581. }