ThreadSafeReferenceTests.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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 "RLMTestCase.h"
  19. #import "RLMRealmConfiguration_Private.h"
  20. #import "RLMThreadSafeReference.h"
  21. @interface ThreadSafeReferenceTests : RLMTestCase
  22. @end
  23. @implementation ThreadSafeReferenceTests
  24. /// Resolve a thread-safe reference confirming that you can't resolve it a second time.
  25. - (id)assertResolve:(RLMRealm *)realm reference:(RLMThreadSafeReference *)reference {
  26. XCTAssertFalse(reference.isInvalidated);
  27. id object = [realm resolveThreadSafeReference:reference];
  28. XCTAssertTrue(reference.isInvalidated);
  29. RLMAssertThrowsWithReasonMatching([realm resolveThreadSafeReference:reference],
  30. @"Can only resolve a thread safe reference once");
  31. return object;
  32. }
  33. - (void)testInvalidThreadSafeReferenceConstruction {
  34. RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];
  35. configuration.cache = false;
  36. RLMRealm *realm = [RLMRealm realmWithConfiguration:configuration error:nil];
  37. StringObject *stringObject = [[StringObject alloc] init];
  38. ArrayPropertyObject *arrayParent = [[ArrayPropertyObject alloc] initWithValue:@[@"arrayObject", @[@[@"a"]], @[]]];
  39. RLMArray *arrayObject = arrayParent.array;
  40. RLMAssertThrowsWithReasonMatching([RLMThreadSafeReference referenceWithThreadConfined:stringObject],
  41. @"Cannot construct reference to unmanaged object");
  42. RLMAssertThrowsWithReasonMatching([RLMThreadSafeReference referenceWithThreadConfined:arrayObject],
  43. @"Cannot construct reference to unmanaged object");
  44. [realm beginWriteTransaction];
  45. [realm addObject:stringObject];
  46. [realm addObject:arrayParent];
  47. arrayObject = arrayParent.array;
  48. [realm deleteAllObjects];
  49. [realm commitWriteTransaction];
  50. RLMAssertThrowsWithReasonMatching([RLMThreadSafeReference referenceWithThreadConfined:stringObject],
  51. @"Cannot construct reference to invalidated object");
  52. RLMAssertThrowsWithReasonMatching([RLMThreadSafeReference referenceWithThreadConfined:arrayObject],
  53. @"Cannot construct reference to invalidated object");
  54. }
  55. - (void)testInvalidThreadSafeReferenceUsage {
  56. RLMRealm *realm = [RLMRealm defaultRealm];
  57. [realm beginWriteTransaction];
  58. StringObject *stringObject = [StringObject createInDefaultRealmWithValue:@{@"stringCol": @"hello"}];
  59. RLMAssertThrowsWithReasonMatching([RLMThreadSafeReference referenceWithThreadConfined:stringObject],
  60. @"Cannot obtain thread safe reference during a write transaction");
  61. [realm commitWriteTransaction];
  62. RLMThreadSafeReference *ref1 = [RLMThreadSafeReference referenceWithThreadConfined:stringObject];
  63. RLMThreadSafeReference *ref2 = [RLMThreadSafeReference referenceWithThreadConfined:stringObject];
  64. RLMThreadSafeReference *ref3 = [RLMThreadSafeReference referenceWithThreadConfined:stringObject];
  65. [self dispatchAsyncAndWait:^{
  66. RLMRealm *realm = [RLMRealm defaultRealm];
  67. RLMAssertThrowsWithReasonMatching([[self realmWithTestPath] resolveThreadSafeReference:ref1],
  68. @"Cannot resolve thread safe reference in Realm with different configuration than the source Realm");
  69. [realm beginWriteTransaction];
  70. RLMAssertThrowsWithReasonMatching([realm resolveThreadSafeReference:ref2],
  71. @"Cannot resolve thread safe reference during a write transaction");
  72. RLMAssertThrowsWithReasonMatching([realm resolveThreadSafeReference:ref2],
  73. @"Can only resolve a thread safe reference once");
  74. [realm cancelWriteTransaction];
  75. RLMAssertThrowsWithReasonMatching([realm resolveThreadSafeReference:ref2],
  76. @"Can only resolve a thread safe reference once");
  77. // Assert that we can resolve a different reference to the same object.
  78. XCTAssertEqualObjects([self assertResolve:realm reference:ref3][@"stringCol"], @"hello");
  79. }];
  80. }
  81. - (void)testPassThreadSafeReferenceToDeletedObject {
  82. RLMRealm *realm = [RLMRealm defaultRealm];
  83. IntObject *intObject = [[IntObject alloc] init];
  84. [realm transactionWithBlock:^{
  85. [realm addObject:intObject];
  86. }];
  87. RLMThreadSafeReference *ref1 = [RLMThreadSafeReference referenceWithThreadConfined:intObject];
  88. RLMThreadSafeReference *ref2 = [RLMThreadSafeReference referenceWithThreadConfined:intObject];
  89. XCTAssertEqual(0, intObject.intCol);
  90. [realm transactionWithBlock:^{
  91. [realm deleteObject:intObject];
  92. }];
  93. [self dispatchAsyncAndWait:^{
  94. RLMRealm *realm = [RLMRealm defaultRealm];
  95. XCTAssertEqualObjects([self assertResolve:realm reference:ref1][@"intCol"], @0);
  96. [realm refresh];
  97. XCTAssertNil([self assertResolve:realm reference:ref2]);
  98. }];
  99. }
  100. - (void)testPassThreadSafeReferencesToMultipleObjects {
  101. RLMRealm *realm = [RLMRealm defaultRealm];
  102. StringObject *stringObject = [[StringObject alloc] init];
  103. IntObject *intObject = [[IntObject alloc] init];
  104. [realm transactionWithBlock:^{
  105. [realm addObject:stringObject];
  106. [realm addObject:intObject];
  107. }];
  108. RLMThreadSafeReference *stringObjectRef = [RLMThreadSafeReference referenceWithThreadConfined:stringObject];
  109. RLMThreadSafeReference *intObjectRef = [RLMThreadSafeReference referenceWithThreadConfined:intObject];
  110. XCTAssertEqualObjects(nil, stringObject.stringCol);
  111. XCTAssertEqual(0, intObject.intCol);
  112. [self dispatchAsyncAndWait:^{
  113. RLMRealm *realm = [RLMRealm defaultRealm];
  114. StringObject *stringObject = [self assertResolve:realm reference:stringObjectRef];
  115. IntObject *intObject = [self assertResolve:realm reference:intObjectRef];
  116. [realm transactionWithBlock:^{
  117. stringObject.stringCol = @"the meaning of life";
  118. intObject.intCol = 42;
  119. }];
  120. }];
  121. XCTAssertEqualObjects(nil, stringObject.stringCol);
  122. XCTAssertEqual(0, intObject.intCol);
  123. [realm refresh];
  124. XCTAssertEqualObjects(@"the meaning of life", stringObject.stringCol);
  125. XCTAssertEqual(42, intObject.intCol);
  126. }
  127. - (void)testPassThreadSafeReferenceToArray {
  128. RLMRealm *realm = [RLMRealm defaultRealm];
  129. DogArrayObject *object = [[DogArrayObject alloc] init];
  130. [realm transactionWithBlock:^{
  131. [realm addObject:object];
  132. DogObject *friday = [DogObject createInDefaultRealmWithValue:@{@"dogName": @"Friday", @"age": @15}];
  133. [object.dogs addObject:friday];
  134. }];
  135. RLMThreadSafeReference *dogsArrayRef = [RLMThreadSafeReference referenceWithThreadConfined:object.dogs];
  136. XCTAssertEqual(1ul, object.dogs.count);
  137. XCTAssertEqualObjects(@"Friday", object.dogs[0].dogName);
  138. [self dispatchAsyncAndWait:^{
  139. RLMRealm *realm = [RLMRealm defaultRealm];
  140. RLMArray<DogObject *> *dogs = [self assertResolve:realm reference:dogsArrayRef];
  141. XCTAssertEqual(1ul, dogs.count);
  142. XCTAssertEqualObjects(@"Friday", dogs[0].dogName);
  143. [realm transactionWithBlock:^{
  144. [dogs removeAllObjects];
  145. DogObject *cookie = [DogObject createInDefaultRealmWithValue:@{@"dogName": @"Cookie", @"age": @8}];
  146. DogObject *breezy = [DogObject createInDefaultRealmWithValue:@{@"dogName": @"Breezy", @"age": @6}];
  147. [dogs addObjects:@[cookie, breezy]];
  148. }];
  149. XCTAssertEqual(2ul, dogs.count);
  150. XCTAssertEqualObjects(@"Cookie", dogs[0].dogName);
  151. XCTAssertEqualObjects(@"Breezy", dogs[1].dogName);
  152. }];
  153. XCTAssertEqual(1ul, object.dogs.count);
  154. XCTAssertEqualObjects(@"Friday", object.dogs[0].dogName);
  155. [realm refresh];
  156. XCTAssertEqual(2ul, object.dogs.count);
  157. XCTAssertEqualObjects(@"Cookie", object.dogs[0].dogName);
  158. XCTAssertEqualObjects(@"Breezy", object.dogs[1].dogName);
  159. }
  160. - (void)testPassThreadSafeReferenceToResults {
  161. RLMRealm *realm = [RLMRealm defaultRealm];
  162. RLMResults<StringObject *> *allObjects = [StringObject allObjects];
  163. RLMResults<StringObject *> *results = [[StringObject objectsWhere:@"stringCol != 'C'"]
  164. sortedResultsUsingKeyPath:@"stringCol" ascending:NO];
  165. RLMThreadSafeReference *resultsRef = [RLMThreadSafeReference referenceWithThreadConfined:results];
  166. [realm transactionWithBlock:^{
  167. [StringObject createInDefaultRealmWithValue:@[@"A"]];
  168. [StringObject createInDefaultRealmWithValue:@[@"B"]];
  169. [StringObject createInDefaultRealmWithValue:@[@"C"]];
  170. [StringObject createInDefaultRealmWithValue:@[@"D"]];
  171. }];
  172. XCTAssertEqual(4ul, allObjects.count);
  173. XCTAssertEqual(3ul, results.count);
  174. XCTAssertEqualObjects(@"D", results[0].stringCol);
  175. XCTAssertEqualObjects(@"B", results[1].stringCol);
  176. XCTAssertEqualObjects(@"A", results[2].stringCol);
  177. [self dispatchAsyncAndWait:^{
  178. RLMRealm *realm = [RLMRealm defaultRealm];
  179. RLMResults<StringObject *> *results = [self assertResolve:realm reference:resultsRef];
  180. RLMResults<StringObject *> *allObjects = [StringObject allObjects];
  181. XCTAssertEqual(0ul, [StringObject allObjects].count);
  182. XCTAssertEqual(0ul, results.count);
  183. [realm refresh];
  184. XCTAssertEqual(4ul, allObjects.count);
  185. XCTAssertEqual(3ul, results.count);
  186. XCTAssertEqualObjects(@"D", results[0].stringCol);
  187. XCTAssertEqualObjects(@"B", results[1].stringCol);
  188. XCTAssertEqualObjects(@"A", results[2].stringCol);
  189. [realm transactionWithBlock:^{
  190. [realm deleteObject:results[2]];
  191. [realm deleteObject:results[0]];
  192. [StringObject createInDefaultRealmWithValue:@[@"E"]];
  193. }];
  194. XCTAssertEqual(3ul, allObjects.count);
  195. XCTAssertEqual(2ul, results.count);
  196. XCTAssertEqualObjects(@"E", results[0].stringCol);
  197. XCTAssertEqualObjects(@"B", results[1].stringCol);
  198. }];
  199. XCTAssertEqual(4ul, allObjects.count);
  200. XCTAssertEqual(3ul, results.count);
  201. XCTAssertEqualObjects(@"D", results[0].stringCol);
  202. XCTAssertEqualObjects(@"B", results[1].stringCol);
  203. XCTAssertEqualObjects(@"A", results[2].stringCol);
  204. [realm refresh];
  205. XCTAssertEqual(3ul, allObjects.count);
  206. XCTAssertEqual(2ul, results.count);
  207. XCTAssertEqualObjects(@"E", results[0].stringCol);
  208. XCTAssertEqualObjects(@"B", results[1].stringCol);
  209. }
  210. - (void)testPassThreadSafeReferenceToLinkingObjects {
  211. RLMRealm *realm = [RLMRealm defaultRealm];
  212. DogObject *dogA = [[DogObject alloc] initWithValue:@{@"dogName": @"Cookie", @"age": @10}];
  213. DogObject *unaccessedDogB = [[DogObject alloc] initWithValue:@{@"dogName": @"Skipper", @"age": @7}];
  214. // Ensures that an `RLMLinkingObjects` without cached results can be handed over
  215. [realm transactionWithBlock:^{
  216. [realm addObject:[[OwnerObject alloc] initWithValue:@{@"name": @"Andrea", @"dog": dogA}]];
  217. [realm addObject:[[OwnerObject alloc] initWithValue:@{@"name": @"Mike", @"dog": unaccessedDogB}]];
  218. }];
  219. XCTAssertEqual(1ul, dogA.owners.count);
  220. XCTAssertEqualObjects(@"Andrea", ((OwnerObject *)dogA.owners[0]).name);
  221. RLMThreadSafeReference *ownersARef = [RLMThreadSafeReference referenceWithThreadConfined:dogA.owners];
  222. RLMThreadSafeReference *ownersBRef = [RLMThreadSafeReference referenceWithThreadConfined:unaccessedDogB.owners];
  223. [self dispatchAsyncAndWait:^{
  224. RLMRealm *realm = [RLMRealm defaultRealm];
  225. RLMLinkingObjects<OwnerObject *> *ownersA = [self assertResolve:realm reference:ownersARef];
  226. RLMLinkingObjects<OwnerObject *> *ownersB = [self assertResolve:realm reference:ownersBRef];
  227. XCTAssertEqual(1ul, ownersA.count);
  228. XCTAssertEqualObjects(@"Andrea", ((OwnerObject *)ownersA[0]).name);
  229. XCTAssertEqual(1ul, ownersB.count);
  230. XCTAssertEqualObjects(@"Mike", ((OwnerObject *)ownersB[0]).name);
  231. [realm transactionWithBlock:^{
  232. // Swap dogs
  233. OwnerObject *ownerA = ownersA[0];
  234. OwnerObject *ownerB = ownersB[0];
  235. DogObject *dogA = ownerA.dog;
  236. DogObject *dogB = ownerB.dog;
  237. ownerA.dog = dogB;
  238. ownerB.dog = dogA;
  239. }];
  240. XCTAssertEqual(1ul, ownersA.count);
  241. XCTAssertEqualObjects(@"Mike", ((OwnerObject *)ownersA[0]).name);
  242. XCTAssertEqual(1ul, ownersB.count);
  243. XCTAssertEqualObjects(@"Andrea", ((OwnerObject *)ownersB[0]).name);
  244. }];
  245. XCTAssertEqual(1ul, dogA.owners.count);
  246. XCTAssertEqualObjects(@"Andrea", ((OwnerObject *)dogA.owners[0]).name);
  247. XCTAssertEqual(1ul, unaccessedDogB.owners.count);
  248. XCTAssertEqualObjects(@"Mike", ((OwnerObject *)unaccessedDogB.owners[0]).name);
  249. [realm refresh];
  250. XCTAssertEqual(1ul, dogA.owners.count);
  251. XCTAssertEqualObjects(@"Mike", ((OwnerObject *)dogA.owners[0]).name);
  252. XCTAssertEqual(1ul, unaccessedDogB.owners.count);
  253. XCTAssertEqualObjects(@"Andrea", ((OwnerObject *)unaccessedDogB.owners[0]).name);
  254. }
  255. @end