ThreadSafeReferenceTests.swift 13 KB

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