FSTLocalStoreTests.mm 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "Firestore/Source/Local/FSTLocalStore.h"
  17. #import <FirebaseFirestore/FIRTimestamp.h>
  18. #import <XCTest/XCTest.h>
  19. #import "Firestore/Source/Core/FSTQuery.h"
  20. #import "Firestore/Source/Local/FSTEagerGarbageCollector.h"
  21. #import "Firestore/Source/Local/FSTLocalWriteResult.h"
  22. #import "Firestore/Source/Local/FSTNoOpGarbageCollector.h"
  23. #import "Firestore/Source/Local/FSTPersistence.h"
  24. #import "Firestore/Source/Local/FSTQueryData.h"
  25. #import "Firestore/Source/Model/FSTDocument.h"
  26. #import "Firestore/Source/Model/FSTDocumentKey.h"
  27. #import "Firestore/Source/Model/FSTDocumentSet.h"
  28. #import "Firestore/Source/Model/FSTMutation.h"
  29. #import "Firestore/Source/Model/FSTMutationBatch.h"
  30. #import "Firestore/Source/Remote/FSTRemoteEvent.h"
  31. #import "Firestore/Source/Remote/FSTWatchChange.h"
  32. #import "Firestore/Source/Util/FSTClasses.h"
  33. #import "Firestore/Example/Tests/Local/FSTLocalStoreTests.h"
  34. #import "Firestore/Example/Tests/Remote/FSTWatchChange+Testing.h"
  35. #import "Firestore/Example/Tests/Util/FSTHelpers.h"
  36. #import "Firestore/third_party/Immutable/Tests/FSTImmutableSortedDictionary+Testing.h"
  37. #import "Firestore/third_party/Immutable/Tests/FSTImmutableSortedSet+Testing.h"
  38. #include "Firestore/core/src/firebase/firestore/auth/user.h"
  39. using firebase::firestore::auth::User;
  40. NS_ASSUME_NONNULL_BEGIN
  41. /** Creates a document version dictionary mapping the document in @a mutation to @a version. */
  42. FSTDocumentVersionDictionary *FSTVersionDictionary(FSTMutation *mutation,
  43. FSTTestSnapshotVersion version) {
  44. FSTDocumentVersionDictionary *result = [FSTDocumentVersionDictionary documentVersionDictionary];
  45. result = [result dictionaryBySettingObject:FSTTestVersion(version) forKey:mutation.key];
  46. return result;
  47. }
  48. @interface FSTLocalStoreTests ()
  49. @property(nonatomic, strong, readwrite) id<FSTPersistence> localStorePersistence;
  50. @property(nonatomic, strong, readwrite) FSTLocalStore *localStore;
  51. @property(nonatomic, strong, readonly) NSMutableArray<FSTMutationBatch *> *batches;
  52. @property(nonatomic, strong, readwrite, nullable) FSTMaybeDocumentDictionary *lastChanges;
  53. @property(nonatomic, assign, readwrite) FSTTargetID lastTargetID;
  54. @end
  55. @implementation FSTLocalStoreTests
  56. - (void)setUp {
  57. [super setUp];
  58. if ([self isTestBaseClass]) {
  59. return;
  60. }
  61. id<FSTPersistence> persistence = [self persistence];
  62. self.localStorePersistence = persistence;
  63. id<FSTGarbageCollector> garbageCollector = [[FSTEagerGarbageCollector alloc] init];
  64. self.localStore = [[FSTLocalStore alloc] initWithPersistence:persistence
  65. garbageCollector:garbageCollector
  66. initialUser:User::Unauthenticated()];
  67. [self.localStore start];
  68. _batches = [NSMutableArray array];
  69. _lastChanges = nil;
  70. _lastTargetID = 0;
  71. }
  72. - (void)tearDown {
  73. [self.localStorePersistence shutdown];
  74. [super tearDown];
  75. }
  76. - (id<FSTPersistence>)persistence {
  77. @throw FSTAbstractMethodException(); // NOLINT
  78. }
  79. /**
  80. * Xcode will run tests from any class that extends XCTestCase, but this doesn't work for
  81. * FSTLocalStoreTests since it is incomplete without the implementations supplied by its
  82. * subclasses.
  83. */
  84. - (BOOL)isTestBaseClass {
  85. return [self class] == [FSTLocalStoreTests class];
  86. }
  87. /** Restarts the local store using the FSTNoOpGarbageCollector instead of the default. */
  88. - (void)restartWithNoopGarbageCollector {
  89. id<FSTGarbageCollector> garbageCollector = [[FSTNoOpGarbageCollector alloc] init];
  90. self.localStore = [[FSTLocalStore alloc] initWithPersistence:self.localStorePersistence
  91. garbageCollector:garbageCollector
  92. initialUser:User::Unauthenticated()];
  93. [self.localStore start];
  94. }
  95. - (void)writeMutation:(FSTMutation *)mutation {
  96. [self writeMutations:@[ mutation ]];
  97. }
  98. - (void)writeMutations:(NSArray<FSTMutation *> *)mutations {
  99. FSTLocalWriteResult *result = [self.localStore locallyWriteMutations:mutations];
  100. XCTAssertNotNil(result);
  101. [self.batches addObject:[[FSTMutationBatch alloc] initWithBatchID:result.batchID
  102. localWriteTime:[FIRTimestamp timestamp]
  103. mutations:mutations]];
  104. self.lastChanges = result.changes;
  105. }
  106. - (void)applyRemoteEvent:(FSTRemoteEvent *)event {
  107. self.lastChanges = [self.localStore applyRemoteEvent:event];
  108. }
  109. - (void)notifyLocalViewChanges:(FSTLocalViewChanges *)changes {
  110. [self.localStore notifyLocalViewChanges:@[ changes ]];
  111. }
  112. - (void)acknowledgeMutationWithVersion:(FSTTestSnapshotVersion)documentVersion {
  113. FSTMutationBatch *batch = [self.batches firstObject];
  114. [self.batches removeObjectAtIndex:0];
  115. XCTAssertEqual(batch.mutations.count, 1, @"Acknowledging more than one mutation not supported.");
  116. FSTSnapshotVersion *version = FSTTestVersion(documentVersion);
  117. FSTMutationResult *mutationResult =
  118. [[FSTMutationResult alloc] initWithVersion:version transformResults:nil];
  119. FSTMutationBatchResult *result = [FSTMutationBatchResult resultWithBatch:batch
  120. commitVersion:version
  121. mutationResults:@[ mutationResult ]
  122. streamToken:nil];
  123. self.lastChanges = [self.localStore acknowledgeBatchWithResult:result];
  124. }
  125. - (void)rejectMutation {
  126. FSTMutationBatch *batch = [self.batches firstObject];
  127. [self.batches removeObjectAtIndex:0];
  128. self.lastChanges = [self.localStore rejectBatchID:batch.batchID];
  129. }
  130. - (void)allocateQuery:(FSTQuery *)query {
  131. FSTQueryData *queryData = [self.localStore allocateQuery:query];
  132. self.lastTargetID = queryData.targetID;
  133. }
  134. - (void)collectGarbage {
  135. [self.localStore collectGarbage];
  136. }
  137. /** Asserts that the last target ID is the given number. */
  138. #define FSTAssertTargetID(targetID) \
  139. do { \
  140. XCTAssertEqual(self.lastTargetID, targetID); \
  141. } while (0)
  142. /** Asserts that a the lastChanges contain the docs in the given array. */
  143. #define FSTAssertChanged(documents) \
  144. XCTAssertNotNil(self.lastChanges); \
  145. do { \
  146. FSTMaybeDocumentDictionary *actual = self.lastChanges; \
  147. NSArray<FSTMaybeDocument *> *expected = (documents); \
  148. XCTAssertEqual(actual.count, expected.count); \
  149. NSEnumerator<FSTMaybeDocument *> *enumerator = expected.objectEnumerator; \
  150. [actual enumerateKeysAndObjectsUsingBlock:^(FSTDocumentKey * key, FSTMaybeDocument * value, \
  151. BOOL * stop) { \
  152. XCTAssertEqualObjects(value, [enumerator nextObject]); \
  153. }]; \
  154. self.lastChanges = nil; \
  155. } while (0)
  156. /** Asserts that the given keys were removed. */
  157. #define FSTAssertRemoved(keyPaths) \
  158. XCTAssertNotNil(self.lastChanges); \
  159. do { \
  160. FSTMaybeDocumentDictionary *actual = self.lastChanges; \
  161. XCTAssertEqual(actual.count, keyPaths.count); \
  162. NSEnumerator<NSString *> *keyPathEnumerator = keyPaths.objectEnumerator; \
  163. [actual enumerateKeysAndObjectsUsingBlock:^(FSTDocumentKey * actualKey, \
  164. FSTMaybeDocument * value, BOOL * stop) { \
  165. FSTDocumentKey *expectedKey = FSTTestDocKey([keyPathEnumerator nextObject]); \
  166. XCTAssertEqualObjects(actualKey, expectedKey); \
  167. XCTAssertTrue([value isKindOfClass:[FSTDeletedDocument class]]); \
  168. }]; \
  169. self.lastChanges = nil; \
  170. } while (0)
  171. /** Asserts that the given local store contains the given document. */
  172. #define FSTAssertContains(document) \
  173. do { \
  174. FSTMaybeDocument *expected = (document); \
  175. FSTMaybeDocument *actual = [self.localStore readDocument:expected.key]; \
  176. XCTAssertEqualObjects(actual, expected); \
  177. } while (0)
  178. /** Asserts that the given local store does not contain the given document. */
  179. #define FSTAssertNotContains(keyPathString) \
  180. do { \
  181. FSTDocumentKey *key = FSTTestDocKey(keyPathString); \
  182. FSTMaybeDocument *actual = [self.localStore readDocument:key]; \
  183. XCTAssertNil(actual); \
  184. } while (0)
  185. - (void)testMutationBatchKeys {
  186. if ([self isTestBaseClass]) return;
  187. FSTMutation *set1 = FSTTestSetMutation(@"foo/bar", @{@"foo" : @"bar"});
  188. FSTMutation *set2 = FSTTestSetMutation(@"bar/baz", @{@"bar" : @"baz"});
  189. FSTMutationBatch *batch = [[FSTMutationBatch alloc] initWithBatchID:1
  190. localWriteTime:[FIRTimestamp timestamp]
  191. mutations:@[ set1, set2 ]];
  192. FSTDocumentKeySet *keys = [batch keys];
  193. XCTAssertEqual(keys.count, 2);
  194. }
  195. - (void)testHandlesSetMutation {
  196. if ([self isTestBaseClass]) return;
  197. [self writeMutation:FSTTestSetMutation(@"foo/bar", @{@"foo" : @"bar"})];
  198. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES) ]);
  199. FSTAssertContains(FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES));
  200. [self acknowledgeMutationWithVersion:0];
  201. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, NO) ]);
  202. FSTAssertContains(FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, NO));
  203. }
  204. - (void)testHandlesSetMutationThenDocument {
  205. if ([self isTestBaseClass]) return;
  206. [self writeMutation:FSTTestSetMutation(@"foo/bar", @{@"foo" : @"bar"})];
  207. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES) ]);
  208. FSTAssertContains(FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES));
  209. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(
  210. FSTTestDoc("foo/bar", 2, @{@"it" : @"changed"}, NO), @[ @1 ], @[])];
  211. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 2, @{@"foo" : @"bar"}, YES) ]);
  212. FSTAssertContains(FSTTestDoc("foo/bar", 2, @{@"foo" : @"bar"}, YES));
  213. }
  214. - (void)testHandlesAckThenRejectThenRemoteEvent {
  215. if ([self isTestBaseClass]) return;
  216. // Start a query that requires acks to be held.
  217. FSTQuery *query = FSTTestQuery("foo");
  218. [self allocateQuery:query];
  219. [self writeMutation:FSTTestSetMutation(@"foo/bar", @{@"foo" : @"bar"})];
  220. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES) ]);
  221. FSTAssertContains(FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES));
  222. // The last seen version is zero, so this ack must be held.
  223. [self acknowledgeMutationWithVersion:1];
  224. FSTAssertChanged(@[]);
  225. FSTAssertContains(FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES));
  226. [self writeMutation:FSTTestSetMutation(@"bar/baz", @{@"bar" : @"baz"})];
  227. FSTAssertChanged(@[ FSTTestDoc("bar/baz", 0, @{@"bar" : @"baz"}, YES) ]);
  228. FSTAssertContains(FSTTestDoc("bar/baz", 0, @{@"bar" : @"baz"}, YES));
  229. [self rejectMutation];
  230. FSTAssertRemoved(@[ @"bar/baz" ]);
  231. FSTAssertNotContains(@"bar/baz");
  232. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(
  233. FSTTestDoc("foo/bar", 2, @{@"it" : @"changed"}, NO), @[ @1 ], @[])];
  234. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 2, @{@"it" : @"changed"}, NO) ]);
  235. FSTAssertContains(FSTTestDoc("foo/bar", 2, @{@"it" : @"changed"}, NO));
  236. FSTAssertNotContains(@"bar/baz");
  237. }
  238. - (void)testHandlesDeletedDocumentThenSetMutationThenAck {
  239. if ([self isTestBaseClass]) return;
  240. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDeletedDoc("foo/bar", 2), @[ @1 ], @[])];
  241. FSTAssertRemoved(@[ @"foo/bar" ]);
  242. FSTAssertContains(FSTTestDeletedDoc("foo/bar", 2));
  243. [self writeMutation:FSTTestSetMutation(@"foo/bar", @{@"foo" : @"bar"})];
  244. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES) ]);
  245. FSTAssertContains(FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES));
  246. [self acknowledgeMutationWithVersion:3];
  247. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, NO) ]);
  248. FSTAssertContains(FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, NO));
  249. }
  250. - (void)testHandlesSetMutationThenDeletedDocument {
  251. if ([self isTestBaseClass]) return;
  252. [self writeMutation:FSTTestSetMutation(@"foo/bar", @{@"foo" : @"bar"})];
  253. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES) ]);
  254. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDeletedDoc("foo/bar", 2), @[ @1 ], @[])];
  255. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES) ]);
  256. FSTAssertContains(FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES));
  257. }
  258. - (void)testHandlesDocumentThenSetMutationThenAckThenDocument {
  259. if ([self isTestBaseClass]) return;
  260. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/bar", 2, @{@"it" : @"base"}, NO),
  261. @[ @1 ], @[])];
  262. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 2, @{@"it" : @"base"}, NO) ]);
  263. FSTAssertContains(FSTTestDoc("foo/bar", 2, @{@"it" : @"base"}, NO));
  264. [self writeMutation:FSTTestSetMutation(@"foo/bar", @{@"foo" : @"bar"})];
  265. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 2, @{@"foo" : @"bar"}, YES) ]);
  266. FSTAssertContains(FSTTestDoc("foo/bar", 2, @{@"foo" : @"bar"}, YES));
  267. [self acknowledgeMutationWithVersion:3];
  268. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 2, @{@"foo" : @"bar"}, NO) ]);
  269. FSTAssertContains(FSTTestDoc("foo/bar", 2, @{@"foo" : @"bar"}, NO));
  270. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(
  271. FSTTestDoc("foo/bar", 3, @{@"it" : @"changed"}, NO), @[ @1 ], @[])];
  272. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 3, @{@"it" : @"changed"}, NO) ]);
  273. FSTAssertContains(FSTTestDoc("foo/bar", 3, @{@"it" : @"changed"}, NO));
  274. }
  275. - (void)testHandlesPatchWithoutPriorDocument {
  276. if ([self isTestBaseClass]) return;
  277. [self writeMutation:FSTTestPatchMutation("foo/bar", @{@"foo" : @"bar"}, {})];
  278. FSTAssertRemoved(@[ @"foo/bar" ]);
  279. FSTAssertNotContains(@"foo/bar");
  280. [self acknowledgeMutationWithVersion:1];
  281. FSTAssertRemoved(@[ @"foo/bar" ]);
  282. FSTAssertNotContains(@"foo/bar");
  283. }
  284. - (void)testHandlesPatchMutationThenDocumentThenAck {
  285. if ([self isTestBaseClass]) return;
  286. [self writeMutation:FSTTestPatchMutation("foo/bar", @{@"foo" : @"bar"}, {})];
  287. FSTAssertRemoved(@[ @"foo/bar" ]);
  288. FSTAssertNotContains(@"foo/bar");
  289. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/bar", 1, @{@"it" : @"base"}, NO),
  290. @[ @1 ], @[])];
  291. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 1, @{@"foo" : @"bar", @"it" : @"base"}, YES) ]);
  292. FSTAssertContains(FSTTestDoc("foo/bar", 1, @{@"foo" : @"bar", @"it" : @"base"}, YES));
  293. [self acknowledgeMutationWithVersion:2];
  294. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 1, @{@"foo" : @"bar", @"it" : @"base"}, NO) ]);
  295. FSTAssertContains(FSTTestDoc("foo/bar", 1, @{@"foo" : @"bar", @"it" : @"base"}, NO));
  296. }
  297. - (void)testHandlesPatchMutationThenAckThenDocument {
  298. if ([self isTestBaseClass]) return;
  299. [self writeMutation:FSTTestPatchMutation("foo/bar", @{@"foo" : @"bar"}, {})];
  300. FSTAssertRemoved(@[ @"foo/bar" ]);
  301. FSTAssertNotContains(@"foo/bar");
  302. [self acknowledgeMutationWithVersion:1];
  303. FSTAssertRemoved(@[ @"foo/bar" ]);
  304. FSTAssertNotContains(@"foo/bar");
  305. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/bar", 1, @{@"it" : @"base"}, NO),
  306. @[ @1 ], @[])];
  307. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 1, @{@"it" : @"base"}, NO) ]);
  308. FSTAssertContains(FSTTestDoc("foo/bar", 1, @{@"it" : @"base"}, NO));
  309. }
  310. - (void)testHandlesDeleteMutationThenAck {
  311. if ([self isTestBaseClass]) return;
  312. [self writeMutation:FSTTestDeleteMutation(@"foo/bar")];
  313. FSTAssertRemoved(@[ @"foo/bar" ]);
  314. FSTAssertContains(FSTTestDeletedDoc("foo/bar", 0));
  315. [self acknowledgeMutationWithVersion:1];
  316. FSTAssertRemoved(@[ @"foo/bar" ]);
  317. FSTAssertContains(FSTTestDeletedDoc("foo/bar", 0));
  318. }
  319. - (void)testHandlesDocumentThenDeleteMutationThenAck {
  320. if ([self isTestBaseClass]) return;
  321. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/bar", 1, @{@"it" : @"base"}, NO),
  322. @[ @1 ], @[])];
  323. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 1, @{@"it" : @"base"}, NO) ]);
  324. FSTAssertContains(FSTTestDoc("foo/bar", 1, @{@"it" : @"base"}, NO));
  325. [self writeMutation:FSTTestDeleteMutation(@"foo/bar")];
  326. FSTAssertRemoved(@[ @"foo/bar" ]);
  327. FSTAssertContains(FSTTestDeletedDoc("foo/bar", 0));
  328. [self acknowledgeMutationWithVersion:2];
  329. FSTAssertRemoved(@[ @"foo/bar" ]);
  330. FSTAssertContains(FSTTestDeletedDoc("foo/bar", 0));
  331. }
  332. - (void)testHandlesDeleteMutationThenDocumentThenAck {
  333. if ([self isTestBaseClass]) return;
  334. [self writeMutation:FSTTestDeleteMutation(@"foo/bar")];
  335. FSTAssertRemoved(@[ @"foo/bar" ]);
  336. FSTAssertContains(FSTTestDeletedDoc("foo/bar", 0));
  337. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/bar", 1, @{@"it" : @"base"}, NO),
  338. @[ @1 ], @[])];
  339. FSTAssertRemoved(@[ @"foo/bar" ]);
  340. FSTAssertContains(FSTTestDeletedDoc("foo/bar", 0));
  341. [self acknowledgeMutationWithVersion:2];
  342. FSTAssertRemoved(@[ @"foo/bar" ]);
  343. FSTAssertContains(FSTTestDeletedDoc("foo/bar", 0));
  344. }
  345. - (void)testHandlesDocumentThenDeletedDocumentThenDocument {
  346. if ([self isTestBaseClass]) return;
  347. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/bar", 1, @{@"it" : @"base"}, NO),
  348. @[ @1 ], @[])];
  349. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 1, @{@"it" : @"base"}, NO) ]);
  350. FSTAssertContains(FSTTestDoc("foo/bar", 1, @{@"it" : @"base"}, NO));
  351. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDeletedDoc("foo/bar", 2), @[ @1 ], @[])];
  352. FSTAssertRemoved(@[ @"foo/bar" ]);
  353. FSTAssertContains(FSTTestDeletedDoc("foo/bar", 2));
  354. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(
  355. FSTTestDoc("foo/bar", 3, @{@"it" : @"changed"}, NO), @[ @1 ], @[])];
  356. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 3, @{@"it" : @"changed"}, NO) ]);
  357. FSTAssertContains(FSTTestDoc("foo/bar", 3, @{@"it" : @"changed"}, NO));
  358. }
  359. - (void)testHandlesSetMutationThenPatchMutationThenDocumentThenAckThenAck {
  360. if ([self isTestBaseClass]) return;
  361. [self writeMutation:FSTTestSetMutation(@"foo/bar", @{@"foo" : @"old"})];
  362. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 0, @{@"foo" : @"old"}, YES) ]);
  363. FSTAssertContains(FSTTestDoc("foo/bar", 0, @{@"foo" : @"old"}, YES));
  364. [self writeMutation:FSTTestPatchMutation("foo/bar", @{@"foo" : @"bar"}, {})];
  365. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES) ]);
  366. FSTAssertContains(FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES));
  367. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/bar", 1, @{@"it" : @"base"}, NO),
  368. @[ @1 ], @[])];
  369. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 1, @{@"foo" : @"bar"}, YES) ]);
  370. FSTAssertContains(FSTTestDoc("foo/bar", 1, @{@"foo" : @"bar"}, YES));
  371. [self acknowledgeMutationWithVersion:2]; // delete mutation
  372. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 1, @{@"foo" : @"bar"}, YES) ]);
  373. FSTAssertContains(FSTTestDoc("foo/bar", 1, @{@"foo" : @"bar"}, YES));
  374. [self acknowledgeMutationWithVersion:3]; // patch mutation
  375. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 1, @{@"foo" : @"bar"}, NO) ]);
  376. FSTAssertContains(FSTTestDoc("foo/bar", 1, @{@"foo" : @"bar"}, NO));
  377. }
  378. - (void)testHandlesSetMutationAndPatchMutationTogether {
  379. if ([self isTestBaseClass]) return;
  380. [self writeMutations:@[
  381. FSTTestSetMutation(@"foo/bar", @{@"foo" : @"old"}),
  382. FSTTestPatchMutation("foo/bar", @{@"foo" : @"bar"}, {})
  383. ]];
  384. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES) ]);
  385. FSTAssertContains(FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES));
  386. }
  387. - (void)testHandlesSetMutationThenPatchMutationThenReject {
  388. if ([self isTestBaseClass]) return;
  389. [self writeMutation:FSTTestSetMutation(@"foo/bar", @{@"foo" : @"old"})];
  390. [self acknowledgeMutationWithVersion:1];
  391. FSTAssertContains(FSTTestDoc("foo/bar", 0, @{@"foo" : @"old"}, NO));
  392. [self writeMutation:FSTTestPatchMutation("foo/bar", @{@"foo" : @"bar"}, {})];
  393. FSTAssertContains(FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES));
  394. [self rejectMutation];
  395. FSTAssertChanged(@[ FSTTestDoc("foo/bar", 0, @{@"foo" : @"old"}, NO) ]);
  396. FSTAssertContains(FSTTestDoc("foo/bar", 0, @{@"foo" : @"old"}, NO));
  397. }
  398. - (void)testHandlesSetMutationsAndPatchMutationOfJustOneTogether {
  399. if ([self isTestBaseClass]) return;
  400. [self writeMutations:@[
  401. FSTTestSetMutation(@"foo/bar", @{@"foo" : @"old"}),
  402. FSTTestSetMutation(@"bar/baz", @{@"bar" : @"baz"}),
  403. FSTTestPatchMutation("foo/bar", @{@"foo" : @"bar"}, {})
  404. ]];
  405. FSTAssertChanged((@[
  406. FSTTestDoc("bar/baz", 0, @{@"bar" : @"baz"}, YES),
  407. FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES)
  408. ]));
  409. FSTAssertContains(FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES));
  410. FSTAssertContains(FSTTestDoc("bar/baz", 0, @{@"bar" : @"baz"}, YES));
  411. }
  412. - (void)testHandlesDeleteMutationThenPatchMutationThenAckThenAck {
  413. if ([self isTestBaseClass]) return;
  414. [self writeMutation:FSTTestDeleteMutation(@"foo/bar")];
  415. FSTAssertRemoved(@[ @"foo/bar" ]);
  416. FSTAssertContains(FSTTestDeletedDoc("foo/bar", 0));
  417. [self writeMutation:FSTTestPatchMutation("foo/bar", @{@"foo" : @"bar"}, {})];
  418. FSTAssertRemoved(@[ @"foo/bar" ]);
  419. FSTAssertContains(FSTTestDeletedDoc("foo/bar", 0));
  420. [self acknowledgeMutationWithVersion:2]; // delete mutation
  421. FSTAssertRemoved(@[ @"foo/bar" ]);
  422. FSTAssertContains(FSTTestDeletedDoc("foo/bar", 0));
  423. [self acknowledgeMutationWithVersion:3]; // patch mutation
  424. FSTAssertRemoved(@[ @"foo/bar" ]);
  425. FSTAssertContains(FSTTestDeletedDoc("foo/bar", 0));
  426. }
  427. - (void)testCollectsGarbageAfterChangeBatchWithNoTargetIDs {
  428. if ([self isTestBaseClass]) return;
  429. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDeletedDoc("foo/bar", 2), @[ @1 ], @[])];
  430. FSTAssertRemoved(@[ @"foo/bar" ]);
  431. [self collectGarbage];
  432. FSTAssertNotContains(@"foo/bar");
  433. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/bar", 2, @{@"foo" : @"bar"}, NO),
  434. @[ @1 ], @[])];
  435. [self collectGarbage];
  436. FSTAssertNotContains(@"foo/bar");
  437. }
  438. - (void)testCollectsGarbageAfterChangeBatch {
  439. if ([self isTestBaseClass]) return;
  440. FSTQuery *query = FSTTestQuery("foo");
  441. [self allocateQuery:query];
  442. FSTAssertTargetID(2);
  443. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/bar", 2, @{@"foo" : @"bar"}, NO),
  444. @[ @2 ], @[])];
  445. [self collectGarbage];
  446. FSTAssertContains(FSTTestDoc("foo/bar", 2, @{@"foo" : @"bar"}, NO));
  447. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/bar", 2, @{@"foo" : @"baz"}, NO),
  448. @[], @[ @2 ])];
  449. [self collectGarbage];
  450. FSTAssertNotContains(@"foo/bar");
  451. }
  452. - (void)testCollectsGarbageAfterAcknowledgedMutation {
  453. if ([self isTestBaseClass]) return;
  454. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/bar", 0, @{@"foo" : @"old"}, NO),
  455. @[ @1 ], @[])];
  456. [self writeMutation:FSTTestPatchMutation("foo/bar", @{@"foo" : @"bar"}, {})];
  457. [self writeMutation:FSTTestSetMutation(@"foo/bah", @{@"foo" : @"bah"})];
  458. [self writeMutation:FSTTestDeleteMutation(@"foo/baz")];
  459. [self collectGarbage];
  460. FSTAssertContains(FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES));
  461. FSTAssertContains(FSTTestDoc("foo/bah", 0, @{@"foo" : @"bah"}, YES));
  462. FSTAssertContains(FSTTestDeletedDoc("foo/baz", 0));
  463. [self acknowledgeMutationWithVersion:3];
  464. [self collectGarbage];
  465. FSTAssertNotContains(@"foo/bar");
  466. FSTAssertContains(FSTTestDoc("foo/bah", 0, @{@"foo" : @"bah"}, YES));
  467. FSTAssertContains(FSTTestDeletedDoc("foo/baz", 0));
  468. [self acknowledgeMutationWithVersion:4];
  469. [self collectGarbage];
  470. FSTAssertNotContains(@"foo/bar");
  471. FSTAssertNotContains(@"foo/bah");
  472. FSTAssertContains(FSTTestDeletedDoc("foo/baz", 0));
  473. [self acknowledgeMutationWithVersion:5];
  474. [self collectGarbage];
  475. FSTAssertNotContains(@"foo/bar");
  476. FSTAssertNotContains(@"foo/bah");
  477. FSTAssertNotContains(@"foo/baz");
  478. }
  479. - (void)testCollectsGarbageAfterRejectedMutation {
  480. if ([self isTestBaseClass]) return;
  481. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/bar", 0, @{@"foo" : @"old"}, NO),
  482. @[ @1 ], @[])];
  483. [self writeMutation:FSTTestPatchMutation("foo/bar", @{@"foo" : @"bar"}, {})];
  484. [self writeMutation:FSTTestSetMutation(@"foo/bah", @{@"foo" : @"bah"})];
  485. [self writeMutation:FSTTestDeleteMutation(@"foo/baz")];
  486. [self collectGarbage];
  487. FSTAssertContains(FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES));
  488. FSTAssertContains(FSTTestDoc("foo/bah", 0, @{@"foo" : @"bah"}, YES));
  489. FSTAssertContains(FSTTestDeletedDoc("foo/baz", 0));
  490. [self rejectMutation]; // patch mutation
  491. [self collectGarbage];
  492. FSTAssertNotContains(@"foo/bar");
  493. FSTAssertContains(FSTTestDoc("foo/bah", 0, @{@"foo" : @"bah"}, YES));
  494. FSTAssertContains(FSTTestDeletedDoc("foo/baz", 0));
  495. [self rejectMutation]; // set mutation
  496. [self collectGarbage];
  497. FSTAssertNotContains(@"foo/bar");
  498. FSTAssertNotContains(@"foo/bah");
  499. FSTAssertContains(FSTTestDeletedDoc("foo/baz", 0));
  500. [self rejectMutation]; // delete mutation
  501. [self collectGarbage];
  502. FSTAssertNotContains(@"foo/bar");
  503. FSTAssertNotContains(@"foo/bah");
  504. FSTAssertNotContains(@"foo/baz");
  505. }
  506. - (void)testPinsDocumentsInTheLocalView {
  507. if ([self isTestBaseClass]) return;
  508. FSTQuery *query = FSTTestQuery("foo");
  509. [self allocateQuery:query];
  510. FSTAssertTargetID(2);
  511. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/bar", 1, @{@"foo" : @"bar"}, NO),
  512. @[ @2 ], @[])];
  513. [self writeMutation:FSTTestSetMutation(@"foo/baz", @{@"foo" : @"baz"})];
  514. [self collectGarbage];
  515. FSTAssertContains(FSTTestDoc("foo/bar", 1, @{@"foo" : @"bar"}, NO));
  516. FSTAssertContains(FSTTestDoc("foo/baz", 0, @{@"foo" : @"baz"}, YES));
  517. [self notifyLocalViewChanges:FSTTestViewChanges(query, @[ @"foo/bar", @"foo/baz" ], @[])];
  518. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/bar", 1, @{@"foo" : @"bar"}, NO),
  519. @[], @[ @2 ])];
  520. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/baz", 2, @{@"foo" : @"baz"}, NO),
  521. @[ @1 ], @[])];
  522. [self acknowledgeMutationWithVersion:2];
  523. [self collectGarbage];
  524. FSTAssertContains(FSTTestDoc("foo/bar", 1, @{@"foo" : @"bar"}, NO));
  525. FSTAssertContains(FSTTestDoc("foo/baz", 2, @{@"foo" : @"baz"}, NO));
  526. [self notifyLocalViewChanges:FSTTestViewChanges(query, @[], @[ @"foo/bar", @"foo/baz" ])];
  527. [self collectGarbage];
  528. FSTAssertNotContains(@"foo/bar");
  529. FSTAssertNotContains(@"foo/baz");
  530. }
  531. - (void)testThrowsAwayDocumentsWithUnknownTargetIDsImmediately {
  532. if ([self isTestBaseClass]) return;
  533. FSTTargetID targetID = 321;
  534. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/bar", 1, @{}, NO),
  535. @[ @(targetID) ], @[])];
  536. FSTAssertContains(FSTTestDoc("foo/bar", 1, @{}, NO));
  537. [self collectGarbage];
  538. FSTAssertNotContains(@"foo/bar");
  539. }
  540. - (void)testCanExecuteDocumentQueries {
  541. if ([self isTestBaseClass]) return;
  542. [self.localStore locallyWriteMutations:@[
  543. FSTTestSetMutation(@"foo/bar", @{@"foo" : @"bar"}),
  544. FSTTestSetMutation(@"foo/baz", @{@"foo" : @"baz"}),
  545. FSTTestSetMutation(@"foo/bar/Foo/Bar", @{@"Foo" : @"Bar"})
  546. ]];
  547. FSTQuery *query = FSTTestQuery("foo/bar");
  548. FSTDocumentDictionary *docs = [self.localStore executeQuery:query];
  549. XCTAssertEqualObjects([docs values], @[ FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES) ]);
  550. }
  551. - (void)testCanExecuteCollectionQueries {
  552. if ([self isTestBaseClass]) return;
  553. [self.localStore locallyWriteMutations:@[
  554. FSTTestSetMutation(@"fo/bar", @{@"fo" : @"bar"}),
  555. FSTTestSetMutation(@"foo/bar", @{@"foo" : @"bar"}),
  556. FSTTestSetMutation(@"foo/baz", @{@"foo" : @"baz"}),
  557. FSTTestSetMutation(@"foo/bar/Foo/Bar", @{@"Foo" : @"Bar"}),
  558. FSTTestSetMutation(@"fooo/blah", @{@"fooo" : @"blah"})
  559. ]];
  560. FSTQuery *query = FSTTestQuery("foo");
  561. FSTDocumentDictionary *docs = [self.localStore executeQuery:query];
  562. XCTAssertEqualObjects([docs values], (@[
  563. FSTTestDoc("foo/bar", 0, @{@"foo" : @"bar"}, YES),
  564. FSTTestDoc("foo/baz", 0, @{@"foo" : @"baz"}, YES)
  565. ]));
  566. }
  567. - (void)testCanExecuteMixedCollectionQueries {
  568. if ([self isTestBaseClass]) return;
  569. FSTQuery *query = FSTTestQuery("foo");
  570. [self allocateQuery:query];
  571. FSTAssertTargetID(2);
  572. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/baz", 10, @{@"a" : @"b"}, NO),
  573. @[ @2 ], @[])];
  574. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/bar", 20, @{@"a" : @"b"}, NO),
  575. @[ @2 ], @[])];
  576. [self.localStore locallyWriteMutations:@[ FSTTestSetMutation(@"foo/bonk", @{@"a" : @"b"}) ]];
  577. FSTDocumentDictionary *docs = [self.localStore executeQuery:query];
  578. XCTAssertEqualObjects([docs values], (@[
  579. FSTTestDoc("foo/bar", 20, @{@"a" : @"b"}, NO),
  580. FSTTestDoc("foo/baz", 10, @{@"a" : @"b"}, NO),
  581. FSTTestDoc("foo/bonk", 0, @{@"a" : @"b"}, YES)
  582. ]));
  583. }
  584. - (void)testPersistsResumeTokens {
  585. if ([self isTestBaseClass]) return;
  586. // This test only works in the absence of the FSTEagerGarbageCollector.
  587. [self restartWithNoopGarbageCollector];
  588. FSTQuery *query = FSTTestQuery("foo/bar");
  589. FSTQueryData *queryData = [self.localStore allocateQuery:query];
  590. FSTBoxedTargetID *targetID = @(queryData.targetID);
  591. NSData *resumeToken = FSTTestResumeTokenFromSnapshotVersion(1000);
  592. FSTWatchChange *watchChange =
  593. [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
  594. targetIDs:@[ targetID ]
  595. resumeToken:resumeToken];
  596. NSMutableDictionary<FSTBoxedTargetID *, FSTQueryData *> *listens =
  597. [NSMutableDictionary dictionary];
  598. listens[targetID] = queryData;
  599. NSMutableDictionary<FSTBoxedTargetID *, NSNumber *> *pendingResponses =
  600. [NSMutableDictionary dictionary];
  601. FSTWatchChangeAggregator *aggregator =
  602. [[FSTWatchChangeAggregator alloc] initWithSnapshotVersion:FSTTestVersion(1000)
  603. listenTargets:listens
  604. pendingTargetResponses:pendingResponses];
  605. [aggregator addWatchChanges:@[ watchChange ]];
  606. FSTRemoteEvent *remoteEvent = [aggregator remoteEvent];
  607. [self applyRemoteEvent:remoteEvent];
  608. // Stop listening so that the query should become inactive (but persistent)
  609. [self.localStore releaseQuery:query];
  610. // Should come back with the same resume token
  611. FSTQueryData *queryData2 = [self.localStore allocateQuery:query];
  612. XCTAssertEqualObjects(queryData2.resumeToken, resumeToken);
  613. }
  614. - (void)testRemoteDocumentKeysForTarget {
  615. if ([self isTestBaseClass]) return;
  616. [self restartWithNoopGarbageCollector];
  617. FSTQuery *query = FSTTestQuery("foo");
  618. [self allocateQuery:query];
  619. FSTAssertTargetID(2);
  620. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/baz", 10, @{@"a" : @"b"}, NO),
  621. @[ @2 ], @[])];
  622. [self applyRemoteEvent:FSTTestUpdateRemoteEvent(FSTTestDoc("foo/bar", 20, @{@"a" : @"b"}, NO),
  623. @[ @2 ], @[])];
  624. [self.localStore locallyWriteMutations:@[ FSTTestSetMutation(@"foo/bonk", @{@"a" : @"b"}) ]];
  625. FSTDocumentKeySet *keys = [self.localStore remoteDocumentKeysForTarget:2];
  626. FSTAssertEqualSets(keys, (@[ FSTTestDocKey(@"foo/bar"), FSTTestDocKey(@"foo/baz") ]));
  627. [self restartWithNoopGarbageCollector];
  628. keys = [self.localStore remoteDocumentKeysForTarget:2];
  629. FSTAssertEqualSets(keys, (@[ FSTTestDocKey(@"foo/bar"), FSTTestDocKey(@"foo/baz") ]));
  630. }
  631. @end
  632. NS_ASSUME_NONNULL_END