// // DynamicModelTests.swift // CoreStore // // Copyright © 2017 John Rommel Estropia // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // import XCTest @testable import CoreStore #if os(OSX) typealias Color = NSColor #else typealias Color = UIColor #endif class Animal: CoreStoreObject { let species = Value.Required("species", default: "Swift") let master = Relationship.ToOne("master") let color = Transformable.Optional("color") } class Dog: Animal { let nickname = Value.Optional("nickname") let age = Value.Required("age", default: 1) let friends = Relationship.ToManyOrdered("friends") let friendedBy = Relationship.ToManyUnordered("friendedBy", inverse: { $0.friends }) } class Person: CoreStoreObject { let title = Value.Required("title", default: "Mr.") let name = Value.Required( "name", customGetter: { (`self`, getValue) in return "\(self.title.value) \(getValue())" } ) let pets = Relationship.ToManyUnordered("pets", inverse: { $0.master }) } // MARK: - DynamicModelTests class DynamicModelTests: BaseTestDataTestCase { func testDynamicModels_CanBeDeclaredCorrectly() { let dataStack = DataStack( CoreStoreSchema( modelVersion: "V1", entities: [ Entity("Animal"), Entity("Dog"), Entity("Person") ], versionLock: [ "Animal": [0x1b59d511019695cf, 0xdeb97e86c5eff179, 0x1cfd80745646cb3, 0x4ff99416175b5b9a], "Dog": [0xe3f0afeb109b283a, 0x29998d292938eb61, 0x6aab788333cfc2a3, 0x492ff1d295910ea7], "Person": [0x66d8bbfd8b21561f, 0xcecec69ecae3570f, 0xc4b73d71256214ef, 0x89b99bfe3e013e8b] ] ) ) self.prepareStack(dataStack, configurations: [nil]) { (stack) in let k1 = Animal.keyPath({ $0.species }) XCTAssertEqual(k1, "species") let k2 = Dog.keyPath({ $0.species }) XCTAssertEqual(k2, "species") let k3 = Dog.keyPath({ $0.nickname }) XCTAssertEqual(k3, "nickname") let updateDone = self.expectation(description: "update-done") let fetchDone = self.expectation(description: "fetch-done") stack.perform( asynchronous: { (transaction) in let animal = transaction.create(Into()) XCTAssertEqual(animal.species.value, "Swift") XCTAssertTrue(type(of: animal.species.value) == String.self) animal.species .= "Sparrow" XCTAssertEqual(animal.species.value, "Sparrow") animal.color .= .yellow XCTAssertEqual(animal.color.value, Color.yellow) let dog = transaction.create(Into()) XCTAssertEqual(dog.species.value, "Swift") XCTAssertEqual(dog.nickname.value, nil) XCTAssertEqual(dog.age.value, 1) dog.species .= "Dog" XCTAssertEqual(dog.species.value, "Dog") dog.nickname .= "Spot" XCTAssertEqual(dog.nickname.value, "Spot") let person = transaction.create(Into()) XCTAssertTrue(person.pets.value.isEmpty) person.name .= "John" XCTAssertEqual(person.name.value, "Mr. John") // Custom getter person.title .= "Sir" XCTAssertEqual(person.name.value, "Sir John") person.pets.value.insert(dog) XCTAssertEqual(person.pets.count, 1) XCTAssertEqual(person.pets.value.first, dog) XCTAssertEqual(person.pets.value.first?.master.value, person) XCTAssertEqual(dog.master.value, person) XCTAssertEqual(dog.master.value?.pets.value.first, dog) }, success: { updateDone.fulfill() }, failure: { _ in XCTFail() } ) stack.perform( asynchronous: { (transaction) in let p1 = Animal.where({ $0.species == "Sparrow" }) XCTAssertEqual(p1.predicate, NSPredicate(format: "%K == %@", "species", "Sparrow")) let bird = transaction.fetchOne(From(), p1) XCTAssertNotNil(bird) XCTAssertEqual(bird!.species.value, "Sparrow") let p2 = Dog.where({ $0.nickname == "Spot" }) XCTAssertEqual(p2.predicate, NSPredicate(format: "%K == %@", "nickname", "Spot")) let dog = transaction.fetchOne(From(), p2) XCTAssertNotNil(dog) XCTAssertEqual(dog!.nickname.value, "Spot") XCTAssertEqual(dog!.species.value, "Dog") let person = transaction.fetchOne(From()) XCTAssertNotNil(person) XCTAssertEqual(person!.pets.value.first, dog) let p3 = Dog.where({ $0.age == 10 }) XCTAssertEqual(p3.predicate, NSPredicate(format: "%K == %d", "age", 10)) }, success: { fetchDone.fulfill() }, failure: { _ in XCTFail() } ) self.waitAndCheckExpectations() } } @nonobjc func prepareStack(_ dataStack: DataStack, configurations: [ModelConfiguration] = [nil], _ closure: (_ dataStack: DataStack) -> Void) { do { try configurations.forEach { (configuration) in try dataStack.addStorageAndWait( SQLiteStore( fileURL: SQLiteStore.defaultRootDirectory .appendingPathComponent(UUID().uuidString) .appendingPathComponent("\(type(of: self))_\((configuration ?? "-null-")).sqlite"), configuration: configuration, localStorageOptions: .recreateStoreOnModelMismatch ) ) } } catch let error as NSError { XCTFail(error.coreStoreDumpString) } closure(dataStack) } }