FSTMutationQueueTests.mm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  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/Example/Tests/Local/FSTMutationQueueTests.h"
  17. #import <FirebaseFirestore/FIRTimestamp.h>
  18. #include <set>
  19. #include <utility>
  20. #include <vector>
  21. #import "Firestore/Source/Local/FSTPersistence.h"
  22. #import "Firestore/Example/Tests/Util/FSTHelpers.h"
  23. #include "Firestore/core/src/firebase/firestore/auth/user.h"
  24. #include "Firestore/core/src/firebase/firestore/model/document_key.h"
  25. #include "Firestore/core/src/firebase/firestore/model/document_key_set.h"
  26. #include "Firestore/core/src/firebase/firestore/model/mutation.h"
  27. #include "Firestore/core/src/firebase/firestore/model/mutation_batch.h"
  28. #include "Firestore/core/src/firebase/firestore/model/set_mutation.h"
  29. #include "Firestore/core/test/firebase/firestore/testutil/testutil.h"
  30. namespace core = firebase::firestore::core;
  31. namespace testutil = firebase::firestore::testutil;
  32. using firebase::Timestamp;
  33. using firebase::firestore::auth::User;
  34. using firebase::firestore::model::DocumentKey;
  35. using firebase::firestore::model::DocumentKeySet;
  36. using firebase::firestore::model::kBatchIdUnknown;
  37. using firebase::firestore::model::Mutation;
  38. using firebase::firestore::model::MutationBatch;
  39. using firebase::firestore::model::SetMutation;
  40. using firebase::firestore::nanopb::ByteString;
  41. using firebase::firestore::testutil::Key;
  42. using firebase::firestore::testutil::Query;
  43. NS_ASSUME_NONNULL_BEGIN
  44. @implementation FSTMutationQueueTests
  45. - (void)tearDown {
  46. [self.persistence shutdown];
  47. [super tearDown];
  48. }
  49. /**
  50. * Xcode will run tests from any class that extends XCTestCase, but this doesn't work for
  51. * FSTMutationQueueTests since it is incomplete without the implementations supplied by its
  52. * subclasses.
  53. */
  54. - (BOOL)isTestBaseClass {
  55. return [self class] == [FSTMutationQueueTests class];
  56. }
  57. - (void)testCountBatches {
  58. if ([self isTestBaseClass]) return;
  59. self.persistence.run("testCountBatches", [&]() {
  60. XCTAssertEqual(0, [self batchCount]);
  61. XCTAssertTrue(self.mutationQueue->IsEmpty());
  62. MutationBatch batch1 = [self addMutationBatch];
  63. XCTAssertEqual(1, [self batchCount]);
  64. XCTAssertFalse(self.mutationQueue->IsEmpty());
  65. MutationBatch batch2 = [self addMutationBatch];
  66. XCTAssertEqual(2, [self batchCount]);
  67. self.mutationQueue->RemoveMutationBatch(batch1);
  68. XCTAssertEqual(1, [self batchCount]);
  69. self.mutationQueue->RemoveMutationBatch(batch2);
  70. XCTAssertEqual(0, [self batchCount]);
  71. XCTAssertTrue(self.mutationQueue->IsEmpty());
  72. });
  73. }
  74. - (void)testAcknowledgeBatchID {
  75. if ([self isTestBaseClass]) return;
  76. self.persistence.run("testAcknowledgeBatchID", [&]() {
  77. XCTAssertEqual([self batchCount], 0);
  78. MutationBatch batch1 = [self addMutationBatch];
  79. MutationBatch batch2 = [self addMutationBatch];
  80. MutationBatch batch3 = [self addMutationBatch];
  81. XCTAssertGreaterThan(batch1.batch_id(), kBatchIdUnknown);
  82. XCTAssertGreaterThan(batch2.batch_id(), batch1.batch_id());
  83. XCTAssertGreaterThan(batch3.batch_id(), batch2.batch_id());
  84. XCTAssertEqual([self batchCount], 3);
  85. self.mutationQueue->AcknowledgeBatch(batch1, {});
  86. self.mutationQueue->RemoveMutationBatch(batch1);
  87. XCTAssertEqual([self batchCount], 2);
  88. self.mutationQueue->AcknowledgeBatch(batch2, {});
  89. XCTAssertEqual([self batchCount], 2);
  90. self.mutationQueue->RemoveMutationBatch(batch2);
  91. XCTAssertEqual([self batchCount], 1);
  92. self.mutationQueue->RemoveMutationBatch(batch3);
  93. XCTAssertEqual([self batchCount], 0);
  94. });
  95. }
  96. - (void)testAcknowledgeThenRemove {
  97. if ([self isTestBaseClass]) return;
  98. self.persistence.run("testAcknowledgeThenRemove", [&]() {
  99. MutationBatch batch1 = [self addMutationBatch];
  100. self.mutationQueue->AcknowledgeBatch(batch1, {});
  101. self.mutationQueue->RemoveMutationBatch(batch1);
  102. XCTAssertEqual([self batchCount], 0);
  103. });
  104. }
  105. - (void)testLookupMutationBatch {
  106. if ([self isTestBaseClass]) return;
  107. // Searching on an empty queue should not find a non-existent batch
  108. self.persistence.run("testLookupMutationBatch", [&]() {
  109. absl::optional<MutationBatch> notFound = self.mutationQueue->LookupMutationBatch(42);
  110. XCTAssertEqual(notFound, absl::nullopt);
  111. std::vector<MutationBatch> batches = [self createBatches:10];
  112. std::vector<MutationBatch> removed = [self removeFirstBatches:3 inBatches:&batches];
  113. // After removing, a batch should not be found
  114. for (size_t i = 0; i < removed.size(); i++) {
  115. notFound = self.mutationQueue->LookupMutationBatch(removed[i].batch_id());
  116. XCTAssertEqual(notFound, absl::nullopt);
  117. }
  118. // Remaining entries should still be found
  119. for (const MutationBatch& batch : batches) {
  120. absl::optional<MutationBatch> found =
  121. self.mutationQueue->LookupMutationBatch(batch.batch_id());
  122. XCTAssertEqual(found->batch_id(), batch.batch_id());
  123. }
  124. // Even on a nonempty queue searching should not find a non-existent batch
  125. notFound = self.mutationQueue->LookupMutationBatch(42);
  126. XCTAssertEqual(notFound, absl::nullopt);
  127. });
  128. }
  129. - (void)testNextMutationBatchAfterBatchID {
  130. if ([self isTestBaseClass]) return;
  131. self.persistence.run("testNextMutationBatchAfterBatchID", [&]() {
  132. std::vector<MutationBatch> batches = [self createBatches:10];
  133. std::vector<MutationBatch> removed = [self removeFirstBatches:3 inBatches:&batches];
  134. for (size_t i = 0; i < batches.size() - 1; i++) {
  135. const MutationBatch& current = batches[i];
  136. const MutationBatch& next = batches[i + 1];
  137. absl::optional<MutationBatch> found =
  138. self.mutationQueue->NextMutationBatchAfterBatchId(current.batch_id());
  139. XCTAssertEqual(found->batch_id(), next.batch_id());
  140. }
  141. for (size_t i = 0; i < removed.size(); i++) {
  142. const MutationBatch& current = removed[i];
  143. const MutationBatch& next = batches[0];
  144. absl::optional<MutationBatch> found =
  145. self.mutationQueue->NextMutationBatchAfterBatchId(current.batch_id());
  146. XCTAssertEqual(found->batch_id(), next.batch_id());
  147. }
  148. const MutationBatch& first = batches[0];
  149. absl::optional<MutationBatch> found =
  150. self.mutationQueue->NextMutationBatchAfterBatchId(first.batch_id() - 42);
  151. XCTAssertEqual(found->batch_id(), first.batch_id());
  152. const MutationBatch& last = batches[batches.size() - 1];
  153. absl::optional<MutationBatch> notFound =
  154. self.mutationQueue->NextMutationBatchAfterBatchId(last.batch_id());
  155. XCTAssertEqual(notFound, absl::nullopt);
  156. });
  157. }
  158. - (void)testAllMutationBatchesAffectingDocumentKey {
  159. if ([self isTestBaseClass]) return;
  160. self.persistence.run("testAllMutationBatchesAffectingDocumentKey", [&]() {
  161. std::vector<Mutation> mutations = {
  162. FSTTestSetMutation(@"foi/bar", @{@"a" : @1}),
  163. FSTTestSetMutation(@"foo/bar", @{@"a" : @1}),
  164. FSTTestPatchMutation("foo/bar", @{@"b" : @1}, {}),
  165. FSTTestSetMutation(@"foo/bar/suffix/key", @{@"a" : @1}),
  166. FSTTestSetMutation(@"foo/baz", @{@"a" : @1}),
  167. FSTTestSetMutation(@"food/bar", @{@"a" : @1}),
  168. };
  169. // Store all the mutations.
  170. std::vector<MutationBatch> batches;
  171. for (const Mutation& mutation : mutations) {
  172. MutationBatch batch = self.mutationQueue->AddMutationBatch(Timestamp::Now(), {}, {mutation});
  173. batches.push_back(batch);
  174. }
  175. std::vector<MutationBatch> expected{batches[1], batches[2]};
  176. std::vector<MutationBatch> matches =
  177. self.mutationQueue->AllMutationBatchesAffectingDocumentKey(testutil::Key("foo/bar"));
  178. XCTAssertEqual(matches, expected);
  179. });
  180. }
  181. - (void)testAllMutationBatchesAffectingDocumentKeys {
  182. if ([self isTestBaseClass]) return;
  183. self.persistence.run("testAllMutationBatchesAffectingDocumentKey", [&]() {
  184. std::vector<Mutation> mutations = {
  185. FSTTestSetMutation(@"fob/bar", @{@"a" : @1}),
  186. FSTTestSetMutation(@"foo/bar", @{@"a" : @1}),
  187. FSTTestPatchMutation("foo/bar", @{@"b" : @1}, {}),
  188. FSTTestSetMutation(@"foo/bar/suffix/key", @{@"a" : @1}),
  189. FSTTestSetMutation(@"foo/baz", @{@"a" : @1}),
  190. FSTTestSetMutation(@"food/bar", @{@"a" : @1}),
  191. };
  192. // Store all the mutations.
  193. std::vector<MutationBatch> batches;
  194. for (const Mutation& mutation : mutations) {
  195. MutationBatch batch = self.mutationQueue->AddMutationBatch(Timestamp::Now(), {}, {mutation});
  196. batches.push_back(batch);
  197. }
  198. DocumentKeySet keys{
  199. Key("foo/bar"),
  200. Key("foo/baz"),
  201. };
  202. std::vector<MutationBatch> expected{batches[1], batches[2], batches[4]};
  203. std::vector<MutationBatch> matches =
  204. self.mutationQueue->AllMutationBatchesAffectingDocumentKeys(keys);
  205. XCTAssertEqual(matches, expected);
  206. });
  207. }
  208. - (void)testAllMutationBatchesAffectingDocumentKeys_handlesOverlap {
  209. if ([self isTestBaseClass]) return;
  210. self.persistence.run("testAllMutationBatchesAffectingDocumentKeys_handlesOverlap", [&]() {
  211. std::vector<Mutation> group1 = {
  212. FSTTestSetMutation(@"foo/bar", @{@"a" : @1}),
  213. FSTTestSetMutation(@"foo/baz", @{@"a" : @1}),
  214. };
  215. MutationBatch batch1 =
  216. self.mutationQueue->AddMutationBatch(Timestamp::Now(), {}, std::move(group1));
  217. std::vector<Mutation> group2 = {FSTTestSetMutation(@"food/bar", @{@"a" : @1})};
  218. self.mutationQueue->AddMutationBatch(Timestamp::Now(), {}, std::move(group2));
  219. std::vector<Mutation> group3 = {
  220. FSTTestSetMutation(@"foo/bar", @{@"b" : @1}),
  221. };
  222. MutationBatch batch3 =
  223. self.mutationQueue->AddMutationBatch(Timestamp::Now(), {}, std::move(group3));
  224. DocumentKeySet keys{
  225. Key("foo/bar"),
  226. Key("foo/baz"),
  227. };
  228. std::vector<MutationBatch> expected{batch1, batch3};
  229. std::vector<MutationBatch> matches =
  230. self.mutationQueue->AllMutationBatchesAffectingDocumentKeys(keys);
  231. XCTAssertEqual(matches, expected);
  232. });
  233. }
  234. - (void)testAllMutationBatchesAffectingQuery {
  235. if ([self isTestBaseClass]) return;
  236. self.persistence.run("testAllMutationBatchesAffectingQuery", [&]() {
  237. std::vector<Mutation> mutations = {
  238. FSTTestSetMutation(@"fob/bar", @{@"a" : @1}),
  239. FSTTestSetMutation(@"foo/bar", @{@"a" : @1}),
  240. FSTTestPatchMutation("foo/bar", @{@"b" : @1}, {}),
  241. FSTTestSetMutation(@"foo/bar/suffix/key", @{@"a" : @1}),
  242. FSTTestSetMutation(@"foo/baz", @{@"a" : @1}),
  243. FSTTestSetMutation(@"food/bar", @{@"a" : @1}),
  244. };
  245. // Store all the mutations.
  246. std::vector<MutationBatch> batches;
  247. for (const Mutation& mutation : mutations) {
  248. MutationBatch batch = self.mutationQueue->AddMutationBatch(Timestamp::Now(), {}, {mutation});
  249. batches.push_back(batch);
  250. }
  251. std::vector<MutationBatch> expected = {batches[1], batches[2], batches[4]};
  252. core::Query query = Query("foo");
  253. std::vector<MutationBatch> matches =
  254. self.mutationQueue->AllMutationBatchesAffectingQuery(query);
  255. XCTAssertEqual(matches, expected);
  256. });
  257. }
  258. - (void)testRemoveMutationBatches {
  259. if ([self isTestBaseClass]) return;
  260. self.persistence.run("testRemoveMutationBatches", [&]() {
  261. std::vector<MutationBatch> batches = [self createBatches:10];
  262. self.mutationQueue->RemoveMutationBatch(batches[0]);
  263. batches.erase(batches.begin());
  264. XCTAssertEqual([self batchCount], 9);
  265. std::vector<MutationBatch> found;
  266. found = self.mutationQueue->AllMutationBatches();
  267. XCTAssertEqual(found, batches);
  268. XCTAssertEqual(found.size(), 9);
  269. self.mutationQueue->RemoveMutationBatch(batches[0]);
  270. self.mutationQueue->RemoveMutationBatch(batches[1]);
  271. self.mutationQueue->RemoveMutationBatch(batches[2]);
  272. batches.erase(batches.begin(), batches.begin() + 3);
  273. XCTAssertEqual([self batchCount], 6);
  274. found = self.mutationQueue->AllMutationBatches();
  275. XCTAssertEqual(found, batches);
  276. XCTAssertEqual(found.size(), 6);
  277. self.mutationQueue->RemoveMutationBatch(batches[0]);
  278. batches.erase(batches.begin());
  279. XCTAssertEqual([self batchCount], 5);
  280. found = self.mutationQueue->AllMutationBatches();
  281. XCTAssertEqual(found, batches);
  282. XCTAssertEqual(found.size(), 5);
  283. self.mutationQueue->RemoveMutationBatch(batches[0]);
  284. batches.erase(batches.begin());
  285. XCTAssertEqual([self batchCount], 4);
  286. self.mutationQueue->RemoveMutationBatch(batches[0]);
  287. batches.erase(batches.begin());
  288. XCTAssertEqual([self batchCount], 3);
  289. found = self.mutationQueue->AllMutationBatches();
  290. XCTAssertEqual(found, batches);
  291. XCTAssertEqual(found.size(), 3);
  292. XCTAssertFalse(self.mutationQueue->IsEmpty());
  293. for (const MutationBatch& batch : batches) {
  294. self.mutationQueue->RemoveMutationBatch(batch);
  295. }
  296. found = self.mutationQueue->AllMutationBatches();
  297. XCTAssertEqual(found.size(), 0);
  298. XCTAssertTrue(self.mutationQueue->IsEmpty());
  299. });
  300. }
  301. - (void)testStreamToken {
  302. if ([self isTestBaseClass]) return;
  303. ByteString streamToken1("token1");
  304. ByteString streamToken2("token2");
  305. self.persistence.run("testStreamToken", [&]() {
  306. self.mutationQueue->SetLastStreamToken(streamToken1);
  307. MutationBatch batch1 = [self addMutationBatch];
  308. [self addMutationBatch];
  309. XCTAssertEqual(self.mutationQueue->GetLastStreamToken(), streamToken1);
  310. self.mutationQueue->AcknowledgeBatch(batch1, streamToken2);
  311. XCTAssertEqual(self.mutationQueue->GetLastStreamToken(), streamToken2);
  312. });
  313. }
  314. #pragma mark - Helpers
  315. /** Creates a new MutationBatch with the next batch ID and a set of dummy mutations. */
  316. - (MutationBatch)addMutationBatch {
  317. return [self addMutationBatchWithKey:@"foo/bar"];
  318. }
  319. /**
  320. * Creates a new MutationBatch with the given key, the next batch ID and a set of dummy
  321. * mutations.
  322. */
  323. - (MutationBatch)addMutationBatchWithKey:(NSString*)key {
  324. SetMutation mutation = FSTTestSetMutation(key, @{@"a" : @1});
  325. MutationBatch batch = self.mutationQueue->AddMutationBatch(Timestamp::Now(), {}, {mutation});
  326. return batch;
  327. }
  328. /**
  329. * Creates an array of batches containing @a number dummy MutationBatches. Each has a different
  330. * batchID.
  331. */
  332. - (std::vector<MutationBatch>)createBatches:(int)number {
  333. std::vector<MutationBatch> batches;
  334. for (int i = 0; i < number; i++) {
  335. MutationBatch batch = [self addMutationBatch];
  336. batches.push_back(batch);
  337. }
  338. return batches;
  339. }
  340. /** Returns the number of mutation batches in the mutation queue. */
  341. - (size_t)batchCount {
  342. return self.mutationQueue->AllMutationBatches().size();
  343. }
  344. /**
  345. * Removes the first n entries from the the given batches and returns them.
  346. *
  347. * @param n The number of batches to remove.
  348. * @param batches The array to mutate, removing entries from it.
  349. * @return A new array containing all the entries that were removed from @a batches.
  350. */
  351. - (std::vector<MutationBatch>)removeFirstBatches:(size_t)n
  352. inBatches:(std::vector<MutationBatch>*)batches {
  353. std::vector<MutationBatch> removed(batches->begin(), batches->begin() + n);
  354. batches->erase(batches->begin(), batches->begin() + n);
  355. for (const MutationBatch& batch : removed) {
  356. self.mutationQueue->RemoveMutationBatch(batch);
  357. }
  358. return removed;
  359. }
  360. @end
  361. NS_ASSUME_NONNULL_END