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.
293 lines
13 KiB
293 lines
13 KiB
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright 2016 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
|
|
|
|
class ThreadSafeReferenceTests: TestCase {
|
|
/// Resolve a thread-safe reference confirming that you can't resolve it a second time.
|
|
func assertResolve<T>(_ realm: Realm, _ reference: ThreadSafeReference<T>) -> T? {
|
|
XCTAssertFalse(reference.isInvalidated)
|
|
let object = realm.resolve(reference)
|
|
XCTAssert(reference.isInvalidated)
|
|
assertThrows(realm.resolve(reference), reason: "Can only resolve a thread safe reference once")
|
|
return object
|
|
}
|
|
|
|
func testInvalidThreadSafeReferenceConstruction() {
|
|
let stringObject = SwiftStringObject()
|
|
let arrayParent = SwiftArrayPropertyObject(value: ["arrayObject", [["a"]], []])
|
|
let arrayObject = arrayParent.array
|
|
|
|
assertThrows(ThreadSafeReference(to: stringObject), reason: "Cannot construct reference to unmanaged object")
|
|
assertThrows(ThreadSafeReference(to: arrayObject), reason: "Cannot construct reference to unmanaged object")
|
|
|
|
let realm = try! Realm()
|
|
realm.beginWrite()
|
|
realm.add(stringObject)
|
|
realm.add(arrayParent)
|
|
realm.deleteAll()
|
|
try! realm.commitWrite()
|
|
|
|
assertThrows(ThreadSafeReference(to: stringObject), reason: "Cannot construct reference to invalidated object")
|
|
assertThrows(ThreadSafeReference(to: arrayObject), reason: "Cannot construct reference to invalidated object")
|
|
}
|
|
|
|
func testInvalidThreadSafeReferenceUsage() {
|
|
let realm = try! Realm()
|
|
realm.beginWrite()
|
|
let stringObject = realm.create(SwiftStringObject.self, value: ["stringCol": "hello"])
|
|
assertThrows(ThreadSafeReference(to: stringObject),
|
|
reason: "Cannot obtain thread safe reference during a write transaction")
|
|
try! realm.commitWrite()
|
|
let ref1 = ThreadSafeReference(to: stringObject)
|
|
let ref2 = ThreadSafeReference(to: stringObject)
|
|
let ref3 = ThreadSafeReference(to: stringObject)
|
|
dispatchSyncNewThread {
|
|
self.assertThrows(self.realmWithTestPath().resolve(ref1),
|
|
reason: "Cannot resolve thread safe reference in Realm with different configuration than the source Realm")
|
|
let realm = try! Realm()
|
|
realm.beginWrite()
|
|
self.assertThrows(realm.resolve(ref2),
|
|
reason: "Cannot resolve thread safe reference during a write transaction")
|
|
self.assertThrows(realm.resolve(ref2),
|
|
reason: "Can only resolve a thread safe reference once")
|
|
realm.cancelWrite()
|
|
self.assertThrows(realm.resolve(ref2),
|
|
reason: "Can only resolve a thread safe reference once")
|
|
// Assert that we can resolve a different reference to the same object.
|
|
XCTAssertEqual(self.assertResolve(realm, ref3)!.stringCol, "hello")
|
|
}
|
|
}
|
|
|
|
func testPassThreadSafeReferenceToDeletedObject() {
|
|
let realm = try! Realm()
|
|
let intObject = SwiftIntObject()
|
|
try! realm.write {
|
|
realm.add(intObject)
|
|
}
|
|
let ref1 = ThreadSafeReference(to: intObject)
|
|
let ref2 = ThreadSafeReference(to: intObject)
|
|
XCTAssertEqual(0, intObject.intCol)
|
|
try! realm.write {
|
|
realm.delete(intObject)
|
|
}
|
|
dispatchSyncNewThread {
|
|
let realm = try! Realm()
|
|
XCTAssertEqual(self.assertResolve(realm, ref1)!.intCol, 0)
|
|
realm.refresh()
|
|
XCTAssertNil(self.assertResolve(realm, ref2))
|
|
}
|
|
}
|
|
|
|
func testPassThreadSafeReferencesToMultipleObjects() {
|
|
let realm = try! Realm()
|
|
let (stringObject, intObject) = (SwiftStringObject(), SwiftIntObject())
|
|
try! realm.write {
|
|
realm.add(stringObject)
|
|
realm.add(intObject)
|
|
}
|
|
let stringObjectRef = ThreadSafeReference(to: stringObject)
|
|
let intObjectRef = ThreadSafeReference(to: intObject)
|
|
XCTAssertEqual("", stringObject.stringCol)
|
|
XCTAssertEqual(0, intObject.intCol)
|
|
dispatchSyncNewThread {
|
|
let realm = try! Realm()
|
|
let stringObject = self.assertResolve(realm, stringObjectRef)!
|
|
let intObject = self.assertResolve(realm, intObjectRef)!
|
|
try! realm.write {
|
|
stringObject.stringCol = "the meaning of life"
|
|
intObject.intCol = 42
|
|
}
|
|
}
|
|
XCTAssertEqual("", stringObject.stringCol)
|
|
XCTAssertEqual(0, intObject.intCol)
|
|
realm.refresh()
|
|
XCTAssertEqual("the meaning of life", stringObject.stringCol)
|
|
XCTAssertEqual(42, intObject.intCol)
|
|
}
|
|
|
|
func testPassThreadSafeReferenceToList() {
|
|
let realm = try! Realm()
|
|
let company = SwiftCompanyObject()
|
|
try! realm.write {
|
|
realm.add(company)
|
|
company.employees.append(SwiftEmployeeObject(value: ["name": "jg"]))
|
|
}
|
|
XCTAssertEqual(1, company.employees.count)
|
|
XCTAssertEqual("jg", company.employees[0].name)
|
|
let listRef = ThreadSafeReference(to: company.employees)
|
|
dispatchSyncNewThread {
|
|
let realm = try! Realm()
|
|
let employees = self.assertResolve(realm, listRef)!
|
|
XCTAssertEqual(1, employees.count)
|
|
XCTAssertEqual("jg", employees[0].name)
|
|
|
|
try! realm.write {
|
|
employees.removeAll()
|
|
employees.append(SwiftEmployeeObject(value: ["name": "jp"]))
|
|
employees.append(SwiftEmployeeObject(value: ["name": "az"]))
|
|
}
|
|
XCTAssertEqual(2, employees.count)
|
|
XCTAssertEqual("jp", employees[0].name)
|
|
XCTAssertEqual("az", employees[1].name)
|
|
}
|
|
XCTAssertEqual(1, company.employees.count)
|
|
XCTAssertEqual("jg", company.employees[0].name)
|
|
realm.refresh()
|
|
XCTAssertEqual(2, company.employees.count)
|
|
XCTAssertEqual("jp", company.employees[0].name)
|
|
XCTAssertEqual("az", company.employees[1].name)
|
|
}
|
|
|
|
func testPassThreadSafeReferenceToResults() {
|
|
let realm = try! Realm()
|
|
let allObjects = realm.objects(SwiftStringObject.self)
|
|
let results = allObjects
|
|
.filter("stringCol != 'C'")
|
|
.sorted(byKeyPath: "stringCol", ascending: false)
|
|
let resultsRef = ThreadSafeReference(to: results)
|
|
try! realm.write {
|
|
realm.create(SwiftStringObject.self, value: ["A"])
|
|
realm.create(SwiftStringObject.self, value: ["B"])
|
|
realm.create(SwiftStringObject.self, value: ["C"])
|
|
realm.create(SwiftStringObject.self, value: ["D"])
|
|
}
|
|
XCTAssertEqual(4, allObjects.count)
|
|
XCTAssertEqual(3, results.count)
|
|
XCTAssertEqual("D", results[0].stringCol)
|
|
XCTAssertEqual("B", results[1].stringCol)
|
|
XCTAssertEqual("A", results[2].stringCol)
|
|
dispatchSyncNewThread {
|
|
let realm = try! Realm()
|
|
let results = self.assertResolve(realm, resultsRef)!
|
|
let allObjects = realm.objects(SwiftStringObject.self)
|
|
XCTAssertEqual(0, allObjects.count)
|
|
XCTAssertEqual(0, results.count)
|
|
realm.refresh()
|
|
XCTAssertEqual(4, allObjects.count)
|
|
XCTAssertEqual(3, results.count)
|
|
XCTAssertEqual("D", results[0].stringCol)
|
|
XCTAssertEqual("B", results[1].stringCol)
|
|
XCTAssertEqual("A", results[2].stringCol)
|
|
try! realm.write {
|
|
realm.delete(results[2])
|
|
realm.delete(results[0])
|
|
realm.create(SwiftStringObject.self, value: ["E"])
|
|
}
|
|
XCTAssertEqual(3, allObjects.count)
|
|
XCTAssertEqual(2, results.count)
|
|
XCTAssertEqual("E", results[0].stringCol)
|
|
XCTAssertEqual("B", results[1].stringCol)
|
|
}
|
|
XCTAssertEqual(4, allObjects.count)
|
|
XCTAssertEqual(3, results.count)
|
|
XCTAssertEqual("D", results[0].stringCol)
|
|
XCTAssertEqual("B", results[1].stringCol)
|
|
XCTAssertEqual("A", results[2].stringCol)
|
|
realm.refresh()
|
|
XCTAssertEqual(3, allObjects.count)
|
|
XCTAssertEqual(2, results.count)
|
|
XCTAssertEqual("E", results[0].stringCol)
|
|
XCTAssertEqual("B", results[1].stringCol)
|
|
}
|
|
|
|
func testPassThreadSafeReferenceToLinkingObjects() {
|
|
let realm = try! Realm()
|
|
let dogA = SwiftDogObject(value: ["dogName": "Cookie", "age": 10])
|
|
let unaccessedDogB = SwiftDogObject(value: ["dogName": "Skipper", "age": 7])
|
|
// Ensures that a `LinkingObjects` without cached results can be handed over
|
|
|
|
try! realm.write {
|
|
realm.add(SwiftOwnerObject(value: ["name": "Andrea", "dog": dogA]))
|
|
realm.add(SwiftOwnerObject(value: ["name": "Mike", "dog": unaccessedDogB]))
|
|
}
|
|
XCTAssertEqual(1, dogA.owners.count)
|
|
XCTAssertEqual("Andrea", dogA.owners[0].name)
|
|
let ownersARef = ThreadSafeReference(to: dogA.owners)
|
|
let ownersBRef = ThreadSafeReference(to: unaccessedDogB.owners)
|
|
dispatchSyncNewThread {
|
|
let realm = try! Realm()
|
|
let ownersA = self.assertResolve(realm, ownersARef)!
|
|
let ownersB = self.assertResolve(realm, ownersBRef)!
|
|
|
|
XCTAssertEqual(1, ownersA.count)
|
|
XCTAssertEqual("Andrea", ownersA[0].name)
|
|
XCTAssertEqual(1, ownersB.count)
|
|
XCTAssertEqual("Mike", ownersB[0].name)
|
|
|
|
try! realm.write {
|
|
(ownersA[0].dog, ownersB[0].dog) = (ownersB[0].dog, ownersA[0].dog)
|
|
}
|
|
XCTAssertEqual(1, ownersA.count)
|
|
XCTAssertEqual("Mike", ownersA[0].name)
|
|
XCTAssertEqual(1, ownersB.count)
|
|
XCTAssertEqual("Andrea", ownersB[0].name)
|
|
}
|
|
XCTAssertEqual(1, dogA.owners.count)
|
|
XCTAssertEqual("Andrea", dogA.owners[0].name)
|
|
XCTAssertEqual(1, unaccessedDogB.owners.count)
|
|
XCTAssertEqual("Mike", unaccessedDogB.owners[0].name)
|
|
realm.refresh()
|
|
XCTAssertEqual(1, dogA.owners.count)
|
|
XCTAssertEqual("Mike", dogA.owners[0].name)
|
|
XCTAssertEqual(1, unaccessedDogB.owners.count)
|
|
XCTAssertEqual("Andrea", unaccessedDogB.owners[0].name)
|
|
}
|
|
|
|
func testPassThreadSafeReferenceToAnyRealmCollection() {
|
|
let realm = try! Realm()
|
|
let company = SwiftCompanyObject()
|
|
try! realm.write {
|
|
realm.add(company)
|
|
company.employees.append(SwiftEmployeeObject(value: ["name": "A"]))
|
|
company.employees.append(SwiftEmployeeObject(value: ["name": "B"]))
|
|
company.employees.append(SwiftEmployeeObject(value: ["name": "C"]))
|
|
company.employees.append(SwiftEmployeeObject(value: ["name": "D"]))
|
|
}
|
|
let results = AnyRealmCollection(realm.objects(SwiftEmployeeObject.self)
|
|
.filter("name != 'C'")
|
|
.sorted(byKeyPath: "name", ascending: false))
|
|
let list = AnyRealmCollection(company.employees)
|
|
XCTAssertEqual(3, results.count)
|
|
XCTAssertEqual("D", results[0].name)
|
|
XCTAssertEqual("B", results[1].name)
|
|
XCTAssertEqual("A", results[2].name)
|
|
XCTAssertEqual(4, list.count)
|
|
XCTAssertEqual("A", list[0].name)
|
|
XCTAssertEqual("B", list[1].name)
|
|
XCTAssertEqual("C", list[2].name)
|
|
XCTAssertEqual("D", list[3].name)
|
|
let resultsRef = ThreadSafeReference(to: results)
|
|
let listRef = ThreadSafeReference(to: list)
|
|
dispatchSyncNewThread {
|
|
let realm = try! Realm()
|
|
let results = self.assertResolve(realm, resultsRef)!
|
|
let list = self.assertResolve(realm, listRef)!
|
|
XCTAssertEqual(3, results.count)
|
|
XCTAssertEqual("D", results[0].name)
|
|
XCTAssertEqual("B", results[1].name)
|
|
XCTAssertEqual("A", results[2].name)
|
|
XCTAssertEqual(4, list.count)
|
|
XCTAssertEqual("A", list[0].name)
|
|
XCTAssertEqual("B", list[1].name)
|
|
XCTAssertEqual("C", list[2].name)
|
|
XCTAssertEqual("D", list[3].name)
|
|
}
|
|
}
|
|
}
|
|
|