ThreadSafeReferenceTests.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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. RLMThreadSafeReference *ref1 = [RLMThreadSafeReference referenceWithThreadConfined:stringObject];
  60. [realm commitWriteTransaction];
  61. RLMThreadSafeReference *ref2 = [RLMThreadSafeReference referenceWithThreadConfined:stringObject];
  62. RLMThreadSafeReference *ref3 = [RLMThreadSafeReference referenceWithThreadConfined:stringObject];
  63. [self dispatchAsyncAndWait:^{
  64. RLMRealm *realm = [RLMRealm defaultRealm];
  65. XCTAssertNil([[self realmWithTestPath] resolveThreadSafeReference:ref1]);
  66. XCTAssertNoThrow([realm resolveThreadSafeReference:ref2]);
  67. RLMAssertThrowsWithReasonMatching([realm resolveThreadSafeReference:ref2],
  68. @"Can only resolve a thread safe reference once");
  69. // Assert that we can resolve a different reference to the same object.
  70. XCTAssertEqualObjects([self assertResolve:realm reference:ref3][@"stringCol"], @"hello");
  71. }];
  72. }
  73. - (void)testPassThreadSafeReferenceToDeletedObject {
  74. RLMRealm *realm = [RLMRealm defaultRealm];
  75. IntObject *intObject = [[IntObject alloc] init];
  76. [realm transactionWithBlock:^{
  77. [realm addObject:intObject];
  78. }];
  79. RLMThreadSafeReference *ref1 = [RLMThreadSafeReference referenceWithThreadConfined:intObject];
  80. RLMThreadSafeReference *ref2 = [RLMThreadSafeReference referenceWithThreadConfined:intObject];
  81. XCTAssertEqual(0, intObject.intCol);
  82. [realm transactionWithBlock:^{
  83. [realm deleteObject:intObject];
  84. }];
  85. [self dispatchAsyncAndWait:^{
  86. RLMRealm *realm = [RLMRealm defaultRealm];
  87. XCTAssertEqualObjects([self assertResolve:realm reference:ref1][@"intCol"], @0);
  88. [realm refresh];
  89. XCTAssertNil([self assertResolve:realm reference:ref2]);
  90. }];
  91. }
  92. - (void)testPassThreadSafeReferencesToMultipleObjects {
  93. RLMRealm *realm = [RLMRealm defaultRealm];
  94. StringObject *stringObject = [[StringObject alloc] init];
  95. IntObject *intObject = [[IntObject alloc] init];
  96. [realm transactionWithBlock:^{
  97. [realm addObject:stringObject];
  98. [realm addObject:intObject];
  99. }];
  100. RLMThreadSafeReference *stringObjectRef = [RLMThreadSafeReference referenceWithThreadConfined:stringObject];
  101. RLMThreadSafeReference *intObjectRef = [RLMThreadSafeReference referenceWithThreadConfined:intObject];
  102. XCTAssertEqualObjects(nil, stringObject.stringCol);
  103. XCTAssertEqual(0, intObject.intCol);
  104. [self dispatchAsyncAndWait:^{
  105. RLMRealm *realm = [RLMRealm defaultRealm];
  106. StringObject *stringObject = [self assertResolve:realm reference:stringObjectRef];
  107. IntObject *intObject = [self assertResolve:realm reference:intObjectRef];
  108. [realm transactionWithBlock:^{
  109. stringObject.stringCol = @"the meaning of life";
  110. intObject.intCol = 42;
  111. }];
  112. }];
  113. XCTAssertEqualObjects(nil, stringObject.stringCol);
  114. XCTAssertEqual(0, intObject.intCol);
  115. [realm refresh];
  116. XCTAssertEqualObjects(@"the meaning of life", stringObject.stringCol);
  117. XCTAssertEqual(42, intObject.intCol);
  118. }
  119. - (void)testPassThreadSafeReferenceToArray {
  120. RLMRealm *realm = [RLMRealm defaultRealm];
  121. DogArrayObject *object = [[DogArrayObject alloc] init];
  122. [realm transactionWithBlock:^{
  123. [realm addObject:object];
  124. DogObject *friday = [DogObject createInDefaultRealmWithValue:@{@"dogName": @"Friday", @"age": @15}];
  125. [object.dogs addObject:friday];
  126. }];
  127. RLMThreadSafeReference *dogsArrayRef = [RLMThreadSafeReference referenceWithThreadConfined:object.dogs];
  128. XCTAssertEqual(1ul, object.dogs.count);
  129. XCTAssertEqualObjects(@"Friday", object.dogs[0].dogName);
  130. [self dispatchAsyncAndWait:^{
  131. RLMRealm *realm = [RLMRealm defaultRealm];
  132. RLMArray<DogObject *> *dogs = [self assertResolve:realm reference:dogsArrayRef];
  133. XCTAssertEqual(1ul, dogs.count);
  134. XCTAssertEqualObjects(@"Friday", dogs[0].dogName);
  135. [realm transactionWithBlock:^{
  136. [dogs removeAllObjects];
  137. DogObject *cookie = [DogObject createInDefaultRealmWithValue:@{@"dogName": @"Cookie", @"age": @8}];
  138. DogObject *breezy = [DogObject createInDefaultRealmWithValue:@{@"dogName": @"Breezy", @"age": @6}];
  139. [dogs addObjects:@[cookie, breezy]];
  140. }];
  141. XCTAssertEqual(2ul, dogs.count);
  142. XCTAssertEqualObjects(@"Cookie", dogs[0].dogName);
  143. XCTAssertEqualObjects(@"Breezy", dogs[1].dogName);
  144. }];
  145. XCTAssertEqual(1ul, object.dogs.count);
  146. XCTAssertEqualObjects(@"Friday", object.dogs[0].dogName);
  147. [realm refresh];
  148. XCTAssertEqual(2ul, object.dogs.count);
  149. XCTAssertEqualObjects(@"Cookie", object.dogs[0].dogName);
  150. XCTAssertEqualObjects(@"Breezy", object.dogs[1].dogName);
  151. }
  152. - (void)testPassThreadSafeReferenceToResults {
  153. RLMRealm *realm = [RLMRealm defaultRealm];
  154. RLMResults<StringObject *> *allObjects = [StringObject allObjects];
  155. RLMResults<StringObject *> *results = [[StringObject objectsWhere:@"stringCol != 'C'"]
  156. sortedResultsUsingKeyPath:@"stringCol" ascending:NO];
  157. RLMThreadSafeReference *resultsRef = [RLMThreadSafeReference referenceWithThreadConfined:results];
  158. [realm transactionWithBlock:^{
  159. [StringObject createInDefaultRealmWithValue:@[@"A"]];
  160. [StringObject createInDefaultRealmWithValue:@[@"B"]];
  161. [StringObject createInDefaultRealmWithValue:@[@"C"]];
  162. [StringObject createInDefaultRealmWithValue:@[@"D"]];
  163. }];
  164. XCTAssertEqual(4ul, allObjects.count);
  165. XCTAssertEqual(3ul, results.count);
  166. XCTAssertEqualObjects(@"D", results[0].stringCol);
  167. XCTAssertEqualObjects(@"B", results[1].stringCol);
  168. XCTAssertEqualObjects(@"A", results[2].stringCol);
  169. [self dispatchAsyncAndWait:^{
  170. RLMRealm *realm = [RLMRealm defaultRealm];
  171. RLMResults<StringObject *> *results = [self assertResolve:realm reference:resultsRef];
  172. RLMResults<StringObject *> *allObjects = [StringObject allObjects];
  173. XCTAssertEqual(0ul, [StringObject allObjects].count);
  174. XCTAssertEqual(0ul, results.count);
  175. [realm refresh];
  176. XCTAssertEqual(4ul, allObjects.count);
  177. XCTAssertEqual(3ul, results.count);
  178. XCTAssertEqualObjects(@"D", results[0].stringCol);
  179. XCTAssertEqualObjects(@"B", results[1].stringCol);
  180. XCTAssertEqualObjects(@"A", results[2].stringCol);
  181. [realm transactionWithBlock:^{
  182. [realm deleteObject:results[2]];
  183. [realm deleteObject:results[0]];
  184. [StringObject createInDefaultRealmWithValue:@[@"E"]];
  185. }];
  186. XCTAssertEqual(3ul, allObjects.count);
  187. XCTAssertEqual(2ul, results.count);
  188. XCTAssertEqualObjects(@"E", results[0].stringCol);
  189. XCTAssertEqualObjects(@"B", results[1].stringCol);
  190. }];
  191. XCTAssertEqual(4ul, allObjects.count);
  192. XCTAssertEqual(3ul, results.count);
  193. XCTAssertEqualObjects(@"D", results[0].stringCol);
  194. XCTAssertEqualObjects(@"B", results[1].stringCol);
  195. XCTAssertEqualObjects(@"A", results[2].stringCol);
  196. [realm refresh];
  197. XCTAssertEqual(3ul, allObjects.count);
  198. XCTAssertEqual(2ul, results.count);
  199. XCTAssertEqualObjects(@"E", results[0].stringCol);
  200. XCTAssertEqualObjects(@"B", results[1].stringCol);
  201. }
  202. - (void)testPassThreadSafeReferenceToLinkingObjects {
  203. RLMRealm *realm = [RLMRealm defaultRealm];
  204. DogObject *dogA = [[DogObject alloc] initWithValue:@{@"dogName": @"Cookie", @"age": @10}];
  205. DogObject *unaccessedDogB = [[DogObject alloc] initWithValue:@{@"dogName": @"Skipper", @"age": @7}];
  206. // Ensures that an `RLMLinkingObjects` without cached results can be handed over
  207. [realm transactionWithBlock:^{
  208. [realm addObject:[[OwnerObject alloc] initWithValue:@{@"name": @"Andrea", @"dog": dogA}]];
  209. [realm addObject:[[OwnerObject alloc] initWithValue:@{@"name": @"Mike", @"dog": unaccessedDogB}]];
  210. }];
  211. XCTAssertEqual(1ul, dogA.owners.count);
  212. XCTAssertEqualObjects(@"Andrea", ((OwnerObject *)dogA.owners[0]).name);
  213. RLMThreadSafeReference *ownersARef = [RLMThreadSafeReference referenceWithThreadConfined:dogA.owners];
  214. RLMThreadSafeReference *ownersBRef = [RLMThreadSafeReference referenceWithThreadConfined:unaccessedDogB.owners];
  215. [self dispatchAsyncAndWait:^{
  216. RLMRealm *realm = [RLMRealm defaultRealm];
  217. RLMLinkingObjects<OwnerObject *> *ownersA = [self assertResolve:realm reference:ownersARef];
  218. RLMLinkingObjects<OwnerObject *> *ownersB = [self assertResolve:realm reference:ownersBRef];
  219. XCTAssertEqual(1ul, ownersA.count);
  220. XCTAssertEqualObjects(@"Andrea", ((OwnerObject *)ownersA[0]).name);
  221. XCTAssertEqual(1ul, ownersB.count);
  222. XCTAssertEqualObjects(@"Mike", ((OwnerObject *)ownersB[0]).name);
  223. [realm transactionWithBlock:^{
  224. // Swap dogs
  225. OwnerObject *ownerA = ownersA[0];
  226. OwnerObject *ownerB = ownersB[0];
  227. DogObject *dogA = ownerA.dog;
  228. DogObject *dogB = ownerB.dog;
  229. ownerA.dog = dogB;
  230. ownerB.dog = dogA;
  231. }];
  232. XCTAssertEqual(1ul, ownersA.count);
  233. XCTAssertEqualObjects(@"Mike", ((OwnerObject *)ownersA[0]).name);
  234. XCTAssertEqual(1ul, ownersB.count);
  235. XCTAssertEqualObjects(@"Andrea", ((OwnerObject *)ownersB[0]).name);
  236. }];
  237. XCTAssertEqual(1ul, dogA.owners.count);
  238. XCTAssertEqualObjects(@"Andrea", ((OwnerObject *)dogA.owners[0]).name);
  239. XCTAssertEqual(1ul, unaccessedDogB.owners.count);
  240. XCTAssertEqualObjects(@"Mike", ((OwnerObject *)unaccessedDogB.owners[0]).name);
  241. [realm refresh];
  242. XCTAssertEqual(1ul, dogA.owners.count);
  243. XCTAssertEqualObjects(@"Mike", ((OwnerObject *)dogA.owners[0]).name);
  244. XCTAssertEqual(1ul, unaccessedDogB.owners.count);
  245. XCTAssertEqualObjects(@"Andrea", ((OwnerObject *)unaccessedDogB.owners[0]).name);
  246. }
  247. @end