ThreadSafeReferenceTests.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2016 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. class ThreadSafeReferenceTests: TestCase {
  21. /// Resolve a thread-safe reference confirming that you can't resolve it a second time.
  22. func assertResolve<T>(_ realm: Realm, _ reference: ThreadSafeReference<T>) -> T? {
  23. XCTAssertFalse(reference.isInvalidated)
  24. let object = realm.resolve(reference)
  25. XCTAssert(reference.isInvalidated)
  26. assertThrows(realm.resolve(reference), reason: "Can only resolve a thread safe reference once")
  27. return object
  28. }
  29. func testInvalidThreadSafeReferenceConstruction() {
  30. let stringObject = SwiftStringObject()
  31. let arrayParent = SwiftArrayPropertyObject(value: ["arrayObject", [["a"]], []])
  32. let arrayObject = arrayParent.array
  33. assertThrows(ThreadSafeReference(to: stringObject), reason: "Cannot construct reference to unmanaged object")
  34. assertThrows(ThreadSafeReference(to: arrayObject), reason: "Cannot construct reference to unmanaged object")
  35. let realm = try! Realm()
  36. realm.beginWrite()
  37. realm.add(stringObject)
  38. realm.add(arrayParent)
  39. realm.deleteAll()
  40. try! realm.commitWrite()
  41. assertThrows(ThreadSafeReference(to: stringObject), reason: "Cannot construct reference to invalidated object")
  42. assertThrows(ThreadSafeReference(to: arrayObject), reason: "Cannot construct reference to invalidated object")
  43. }
  44. func testInvalidThreadSafeReferenceUsage() {
  45. let realm = try! Realm()
  46. realm.beginWrite()
  47. let stringObject = realm.create(SwiftStringObject.self, value: ["stringCol": "hello"])
  48. let ref1 = ThreadSafeReference(to: stringObject)
  49. try! realm.commitWrite()
  50. let ref2 = ThreadSafeReference(to: stringObject)
  51. let ref3 = ThreadSafeReference(to: stringObject)
  52. dispatchSyncNewThread {
  53. XCTAssertNil(self.realmWithTestPath().resolve(ref1))
  54. let realm = try! Realm()
  55. _ = realm.resolve(ref2)
  56. self.assertThrows(realm.resolve(ref2),
  57. reason: "Can only resolve a thread safe reference once")
  58. // Assert that we can resolve a different reference to the same object.
  59. XCTAssertEqual(self.assertResolve(realm, ref3)!.stringCol, "hello")
  60. }
  61. }
  62. func testPassThreadSafeReferenceToDeletedObject() {
  63. let realm = try! Realm()
  64. let intObject = SwiftIntObject()
  65. try! realm.write {
  66. realm.add(intObject)
  67. }
  68. let ref1 = ThreadSafeReference(to: intObject)
  69. let ref2 = ThreadSafeReference(to: intObject)
  70. XCTAssertEqual(0, intObject.intCol)
  71. try! realm.write {
  72. realm.delete(intObject)
  73. }
  74. dispatchSyncNewThread {
  75. let realm = try! Realm()
  76. XCTAssertEqual(self.assertResolve(realm, ref1)!.intCol, 0)
  77. realm.refresh()
  78. XCTAssertNil(self.assertResolve(realm, ref2))
  79. }
  80. }
  81. func testPassThreadSafeReferencesToMultipleObjects() {
  82. let realm = try! Realm()
  83. let (stringObject, intObject) = (SwiftStringObject(), SwiftIntObject())
  84. try! realm.write {
  85. realm.add(stringObject)
  86. realm.add(intObject)
  87. }
  88. let stringObjectRef = ThreadSafeReference(to: stringObject)
  89. let intObjectRef = ThreadSafeReference(to: intObject)
  90. XCTAssertEqual("", stringObject.stringCol)
  91. XCTAssertEqual(0, intObject.intCol)
  92. dispatchSyncNewThread {
  93. let realm = try! Realm()
  94. let stringObject = self.assertResolve(realm, stringObjectRef)!
  95. let intObject = self.assertResolve(realm, intObjectRef)!
  96. try! realm.write {
  97. stringObject.stringCol = "the meaning of life"
  98. intObject.intCol = 42
  99. }
  100. }
  101. XCTAssertEqual("", stringObject.stringCol)
  102. XCTAssertEqual(0, intObject.intCol)
  103. realm.refresh()
  104. XCTAssertEqual("the meaning of life", stringObject.stringCol)
  105. XCTAssertEqual(42, intObject.intCol)
  106. }
  107. func testPassThreadSafeReferenceToList() {
  108. let realm = try! Realm()
  109. let company = SwiftCompanyObject()
  110. try! realm.write {
  111. realm.add(company)
  112. company.employees.append(SwiftEmployeeObject(value: ["name": "jg"]))
  113. }
  114. XCTAssertEqual(1, company.employees.count)
  115. XCTAssertEqual("jg", company.employees[0].name)
  116. let listRef = ThreadSafeReference(to: company.employees)
  117. dispatchSyncNewThread {
  118. let realm = try! Realm()
  119. let employees = self.assertResolve(realm, listRef)!
  120. XCTAssertEqual(1, employees.count)
  121. XCTAssertEqual("jg", employees[0].name)
  122. try! realm.write {
  123. employees.removeAll()
  124. employees.append(SwiftEmployeeObject(value: ["name": "jp"]))
  125. employees.append(SwiftEmployeeObject(value: ["name": "az"]))
  126. }
  127. XCTAssertEqual(2, employees.count)
  128. XCTAssertEqual("jp", employees[0].name)
  129. XCTAssertEqual("az", employees[1].name)
  130. }
  131. XCTAssertEqual(1, company.employees.count)
  132. XCTAssertEqual("jg", company.employees[0].name)
  133. realm.refresh()
  134. XCTAssertEqual(2, company.employees.count)
  135. XCTAssertEqual("jp", company.employees[0].name)
  136. XCTAssertEqual("az", company.employees[1].name)
  137. }
  138. func testPassThreadSafeReferenceToResults() {
  139. let realm = try! Realm()
  140. let allObjects = realm.objects(SwiftStringObject.self)
  141. let results = allObjects
  142. .filter("stringCol != 'C'")
  143. .sorted(byKeyPath: "stringCol", ascending: false)
  144. let resultsRef = ThreadSafeReference(to: results)
  145. try! realm.write {
  146. realm.create(SwiftStringObject.self, value: ["A"])
  147. realm.create(SwiftStringObject.self, value: ["B"])
  148. realm.create(SwiftStringObject.self, value: ["C"])
  149. realm.create(SwiftStringObject.self, value: ["D"])
  150. }
  151. XCTAssertEqual(4, allObjects.count)
  152. XCTAssertEqual(3, results.count)
  153. XCTAssertEqual("D", results[0].stringCol)
  154. XCTAssertEqual("B", results[1].stringCol)
  155. XCTAssertEqual("A", results[2].stringCol)
  156. dispatchSyncNewThread {
  157. let realm = try! Realm()
  158. let results = self.assertResolve(realm, resultsRef)!
  159. let allObjects = realm.objects(SwiftStringObject.self)
  160. XCTAssertEqual(0, allObjects.count)
  161. XCTAssertEqual(0, results.count)
  162. realm.refresh()
  163. XCTAssertEqual(4, allObjects.count)
  164. XCTAssertEqual(3, results.count)
  165. XCTAssertEqual("D", results[0].stringCol)
  166. XCTAssertEqual("B", results[1].stringCol)
  167. XCTAssertEqual("A", results[2].stringCol)
  168. try! realm.write {
  169. realm.delete(results[2])
  170. realm.delete(results[0])
  171. realm.create(SwiftStringObject.self, value: ["E"])
  172. }
  173. XCTAssertEqual(3, allObjects.count)
  174. XCTAssertEqual(2, results.count)
  175. XCTAssertEqual("E", results[0].stringCol)
  176. XCTAssertEqual("B", results[1].stringCol)
  177. }
  178. XCTAssertEqual(4, allObjects.count)
  179. XCTAssertEqual(3, results.count)
  180. XCTAssertEqual("D", results[0].stringCol)
  181. XCTAssertEqual("B", results[1].stringCol)
  182. XCTAssertEqual("A", results[2].stringCol)
  183. realm.refresh()
  184. XCTAssertEqual(3, allObjects.count)
  185. XCTAssertEqual(2, results.count)
  186. XCTAssertEqual("E", results[0].stringCol)
  187. XCTAssertEqual("B", results[1].stringCol)
  188. }
  189. func testPassThreadSafeReferenceToLinkingObjects() {
  190. let realm = try! Realm()
  191. let dogA = SwiftDogObject(value: ["dogName": "Cookie", "age": 10])
  192. let unaccessedDogB = SwiftDogObject(value: ["dogName": "Skipper", "age": 7])
  193. // Ensures that a `LinkingObjects` without cached results can be handed over
  194. try! realm.write {
  195. realm.add(SwiftOwnerObject(value: ["name": "Andrea", "dog": dogA]))
  196. realm.add(SwiftOwnerObject(value: ["name": "Mike", "dog": unaccessedDogB]))
  197. }
  198. XCTAssertEqual(1, dogA.owners.count)
  199. XCTAssertEqual("Andrea", dogA.owners[0].name)
  200. let ownersARef = ThreadSafeReference(to: dogA.owners)
  201. let ownersBRef = ThreadSafeReference(to: unaccessedDogB.owners)
  202. dispatchSyncNewThread {
  203. let realm = try! Realm()
  204. let ownersA = self.assertResolve(realm, ownersARef)!
  205. let ownersB = self.assertResolve(realm, ownersBRef)!
  206. XCTAssertEqual(1, ownersA.count)
  207. XCTAssertEqual("Andrea", ownersA[0].name)
  208. XCTAssertEqual(1, ownersB.count)
  209. XCTAssertEqual("Mike", ownersB[0].name)
  210. try! realm.write {
  211. (ownersA[0].dog, ownersB[0].dog) = (ownersB[0].dog, ownersA[0].dog)
  212. }
  213. XCTAssertEqual(1, ownersA.count)
  214. XCTAssertEqual("Mike", ownersA[0].name)
  215. XCTAssertEqual(1, ownersB.count)
  216. XCTAssertEqual("Andrea", ownersB[0].name)
  217. }
  218. XCTAssertEqual(1, dogA.owners.count)
  219. XCTAssertEqual("Andrea", dogA.owners[0].name)
  220. XCTAssertEqual(1, unaccessedDogB.owners.count)
  221. XCTAssertEqual("Mike", unaccessedDogB.owners[0].name)
  222. realm.refresh()
  223. XCTAssertEqual(1, dogA.owners.count)
  224. XCTAssertEqual("Mike", dogA.owners[0].name)
  225. XCTAssertEqual(1, unaccessedDogB.owners.count)
  226. XCTAssertEqual("Andrea", unaccessedDogB.owners[0].name)
  227. }
  228. func testPassThreadSafeReferenceToAnyRealmCollection() {
  229. let realm = try! Realm()
  230. let company = SwiftCompanyObject()
  231. try! realm.write {
  232. realm.add(company)
  233. company.employees.append(SwiftEmployeeObject(value: ["name": "A"]))
  234. company.employees.append(SwiftEmployeeObject(value: ["name": "B"]))
  235. company.employees.append(SwiftEmployeeObject(value: ["name": "C"]))
  236. company.employees.append(SwiftEmployeeObject(value: ["name": "D"]))
  237. }
  238. let results = AnyRealmCollection(realm.objects(SwiftEmployeeObject.self)
  239. .filter("name != 'C'")
  240. .sorted(byKeyPath: "name", ascending: false))
  241. let list = AnyRealmCollection(company.employees)
  242. XCTAssertEqual(3, results.count)
  243. XCTAssertEqual("D", results[0].name)
  244. XCTAssertEqual("B", results[1].name)
  245. XCTAssertEqual("A", results[2].name)
  246. XCTAssertEqual(4, list.count)
  247. XCTAssertEqual("A", list[0].name)
  248. XCTAssertEqual("B", list[1].name)
  249. XCTAssertEqual("C", list[2].name)
  250. XCTAssertEqual("D", list[3].name)
  251. let resultsRef = ThreadSafeReference(to: results)
  252. let listRef = ThreadSafeReference(to: list)
  253. dispatchSyncNewThread {
  254. let realm = try! Realm()
  255. let results = self.assertResolve(realm, resultsRef)!
  256. let list = self.assertResolve(realm, listRef)!
  257. XCTAssertEqual(3, results.count)
  258. XCTAssertEqual("D", results[0].name)
  259. XCTAssertEqual("B", results[1].name)
  260. XCTAssertEqual("A", results[2].name)
  261. XCTAssertEqual(4, list.count)
  262. XCTAssertEqual("A", list[0].name)
  263. XCTAssertEqual("B", list[1].name)
  264. XCTAssertEqual("C", list[2].name)
  265. XCTAssertEqual("D", list[3].name)
  266. }
  267. }
  268. }