FSTRemoteEventTests.mm 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  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 <XCTest/XCTest.h>
  17. #include <memory>
  18. #include <unordered_map>
  19. #include <utility>
  20. #include <vector>
  21. #import "Firestore/Source/Core/FSTQuery.h"
  22. #import "Firestore/Source/Local/FSTQueryData.h"
  23. #import "Firestore/Source/Model/FSTDocument.h"
  24. #include "Firestore/core/src/firebase/firestore/model/document_key.h"
  25. #include "Firestore/core/src/firebase/firestore/model/types.h"
  26. #include "Firestore/core/src/firebase/firestore/remote/existence_filter.h"
  27. #include "Firestore/core/src/firebase/firestore/remote/remote_event.h"
  28. #include "Firestore/core/src/firebase/firestore/remote/watch_change.h"
  29. #import "Firestore/Example/Tests/Util/FSTHelpers.h"
  30. #include "Firestore/core/test/firebase/firestore/testutil/testutil.h"
  31. #include "absl/memory/memory.h"
  32. namespace testutil = firebase::firestore::testutil;
  33. using firebase::firestore::model::DocumentKey;
  34. using firebase::firestore::model::DocumentKeySet;
  35. using firebase::firestore::model::SnapshotVersion;
  36. using firebase::firestore::model::TargetId;
  37. using firebase::firestore::remote::DocumentWatchChange;
  38. using firebase::firestore::remote::ExistenceFilter;
  39. using firebase::firestore::remote::ExistenceFilterWatchChange;
  40. using firebase::firestore::remote::RemoteEvent;
  41. using firebase::firestore::remote::TargetChange;
  42. using firebase::firestore::remote::TestTargetMetadataProvider;
  43. using firebase::firestore::remote::WatchChange;
  44. using firebase::firestore::remote::WatchChangeAggregator;
  45. using firebase::firestore::remote::WatchTargetChange;
  46. using firebase::firestore::remote::WatchTargetChangeState;
  47. using firebase::firestore::testutil::VectorOfUniquePtrs;
  48. using firebase::firestore::util::MakeString;
  49. using firebase::firestore::util::Status;
  50. NS_ASSUME_NONNULL_BEGIN
  51. namespace {
  52. template <typename... Elems>
  53. std::vector<std::unique_ptr<WatchChange>> Changes(Elems... elems) {
  54. return VectorOfUniquePtrs<WatchChange>(std::move(elems)...);
  55. }
  56. // These helpers work around the fact that `make_unique` cannot deduce the
  57. // desired type (`vector<TargetId>` in this case) from an initialization list
  58. // (e.g., `{1,2}`).
  59. std::unique_ptr<DocumentWatchChange> MakeDocChange(std::vector<TargetId> updated,
  60. std::vector<TargetId> removed,
  61. DocumentKey key,
  62. FSTMaybeDocument *doc) {
  63. return absl::make_unique<DocumentWatchChange>(std::move(updated), std::move(removed),
  64. std::move(key), doc);
  65. }
  66. std::unique_ptr<WatchTargetChange> MakeTargetChange(WatchTargetChangeState state,
  67. std::vector<TargetId> target_ids) {
  68. return absl::make_unique<WatchTargetChange>(state, std::move(target_ids));
  69. }
  70. std::unique_ptr<WatchTargetChange> MakeTargetChange(WatchTargetChangeState state,
  71. std::vector<TargetId> target_ids,
  72. NSData *token) {
  73. return absl::make_unique<WatchTargetChange>(state, std::move(target_ids), token);
  74. }
  75. } // namespace
  76. @interface FSTRemoteEventTests : XCTestCase
  77. @end
  78. @implementation FSTRemoteEventTests {
  79. NSData *_resumeToken1;
  80. TestTargetMetadataProvider _targetMetadataProvider;
  81. std::unordered_map<TargetId, int> _noOutstandingResponses;
  82. }
  83. - (void)setUp {
  84. _resumeToken1 = [@"resume1" dataUsingEncoding:NSUTF8StringEncoding];
  85. }
  86. /**
  87. * Creates a map with query data for the provided target IDs. All targets are considered active
  88. * and query a collection named "coll".
  89. */
  90. - (std::unordered_map<TargetId, FSTQueryData *>)queryDataForTargets:
  91. (std::initializer_list<TargetId>)targetIDs {
  92. std::unordered_map<TargetId, FSTQueryData *> targets;
  93. for (TargetId targetID : targetIDs) {
  94. FSTQuery *query = FSTTestQuery("coll");
  95. targets[targetID] = [[FSTQueryData alloc] initWithQuery:query
  96. targetID:targetID
  97. listenSequenceNumber:0
  98. purpose:FSTQueryPurposeListen];
  99. }
  100. return targets;
  101. }
  102. /**
  103. * Creates a map with query data for the provided target IDs. All targets are marked as limbo
  104. * queries for the document at "coll/limbo".
  105. */
  106. - (std::unordered_map<TargetId, FSTQueryData *>)queryDataForLimboTargets:
  107. (std::initializer_list<TargetId>)targetIDs {
  108. std::unordered_map<TargetId, FSTQueryData *> targets;
  109. for (TargetId targetID : targetIDs) {
  110. FSTQuery *query = FSTTestQuery("coll/limbo");
  111. targets[targetID] = [[FSTQueryData alloc] initWithQuery:query
  112. targetID:targetID
  113. listenSequenceNumber:0
  114. purpose:FSTQueryPurposeLimboResolution];
  115. }
  116. return targets;
  117. }
  118. /**
  119. * Creates an aggregator initialized with the set of provided `WatchChange`s. Tests can add further
  120. * changes via `HandleDocumentChange`, `HandleTargetChange` and `HandleExistenceFilterChange`.
  121. *
  122. * @param targetMap A map of query data for all active targets. The map must include an entry for
  123. * every target referenced by any of the watch changes.
  124. * @param outstandingResponses The number of outstanding ACKs a target has to receive before it is
  125. * considered active, or `_noOutstandingResponses` if all targets are already active.
  126. * @param existingKeys The set of documents that are considered synced with the test targets as
  127. * part of a previous listen. To modify this set during test execution, invoke
  128. * `_targetMetadataProvider.SetSyncedKeys()`.
  129. * @param watchChanges The watch changes to apply before returning the aggregator. Supported
  130. * changes are `DocumentWatchChange` and `WatchTargetChange`.
  131. */
  132. - (WatchChangeAggregator)
  133. aggregatorWithTargetMap:(const std::unordered_map<TargetId, FSTQueryData *> &)targetMap
  134. outstandingResponses:(const std::unordered_map<TargetId, int> &)outstandingResponses
  135. existingKeys:(DocumentKeySet)existingKeys
  136. changes:(const std::vector<std::unique_ptr<WatchChange>> &)watchChanges {
  137. WatchChangeAggregator aggregator{&_targetMetadataProvider};
  138. std::vector<TargetId> targetIDs;
  139. for (const auto &kv : targetMap) {
  140. TargetId targetID = kv.first;
  141. FSTQueryData *queryData = kv.second;
  142. targetIDs.push_back(targetID);
  143. _targetMetadataProvider.SetSyncedKeys(existingKeys, queryData);
  144. };
  145. for (const auto &kv : outstandingResponses) {
  146. TargetId targetID = kv.first;
  147. int count = kv.second;
  148. for (int i = 0; i < count; ++i) {
  149. aggregator.RecordPendingTargetRequest(targetID);
  150. }
  151. }
  152. for (const std::unique_ptr<WatchChange> &change : watchChanges) {
  153. switch (change->type()) {
  154. case WatchChange::Type::Document: {
  155. aggregator.HandleDocumentChange(*static_cast<const DocumentWatchChange *>(change.get()));
  156. break;
  157. }
  158. case WatchChange::Type::TargetChange: {
  159. aggregator.HandleTargetChange(*static_cast<const WatchTargetChange *>(change.get()));
  160. break;
  161. }
  162. default:
  163. HARD_ASSERT("Encountered unexpected type of WatchChange");
  164. }
  165. }
  166. aggregator.HandleTargetChange(
  167. WatchTargetChange{WatchTargetChangeState::NoChange, targetIDs, _resumeToken1});
  168. return aggregator;
  169. }
  170. /**
  171. * Creates a single remote event that includes target changes for all provided `WatchChange`s.
  172. *
  173. * @param snapshotVersion The version at which to create the remote event. This corresponds to the
  174. * snapshot version provided by the NO_CHANGE event.
  175. * @param targetMap A map of query data for all active targets. The map must include an entry for
  176. * every target referenced by any of the watch changes.
  177. * @param outstandingResponses The number of outstanding ACKs a target has to receive before it is
  178. * considered active, or `_noOutstandingResponses` if all targets are already active.
  179. * @param existingKeys The set of documents that are considered synced with the test targets as
  180. * part of a previous listen.
  181. * @param watchChanges The watch changes to apply before creating the remote event. Supported
  182. * changes are `DocumentWatchChange` and `WatchTargetChange`.
  183. */
  184. - (RemoteEvent)
  185. remoteEventAtSnapshotVersion:(FSTTestSnapshotVersion)snapshotVersion
  186. targetMap:(std::unordered_map<TargetId, FSTQueryData *>)targetMap
  187. outstandingResponses:(const std::unordered_map<TargetId, int> &)outstandingResponses
  188. existingKeys:(DocumentKeySet)existingKeys
  189. changes:(const std::vector<std::unique_ptr<WatchChange>> &)watchChanges {
  190. WatchChangeAggregator aggregator = [self aggregatorWithTargetMap:targetMap
  191. outstandingResponses:outstandingResponses
  192. existingKeys:existingKeys
  193. changes:watchChanges];
  194. return aggregator.CreateRemoteEvent(testutil::Version(snapshotVersion));
  195. }
  196. - (void)testWillAccumulateDocumentAddedAndRemovedEvents {
  197. // The target map that contains an entry for every target in this test. If a target ID is
  198. // omitted, the target is considered inactive and `TestTargetMetadataProvider` will fail on
  199. // access.
  200. std::unordered_map<TargetId, FSTQueryData *> targetMap{
  201. [self queryDataForTargets:{1, 2, 3, 4, 5, 6}]};
  202. FSTDocument *existingDoc = FSTTestDoc("docs/1", 1, @{@"value" : @1}, FSTDocumentStateSynced);
  203. auto change1 = MakeDocChange({1, 2, 3}, {4, 5, 6}, existingDoc.key, existingDoc);
  204. FSTDocument *newDoc = FSTTestDoc("docs/2", 2, @{@"value" : @2}, FSTDocumentStateSynced);
  205. auto change2 = MakeDocChange({1, 4}, {2, 6}, newDoc.key, newDoc);
  206. // Create a remote event that includes both `change1` and `change2` as well as a NO_CHANGE event
  207. // with the default resume token (`_resumeToken1`).
  208. // As `existingDoc` is provided as an existing key, any updates to this document will be treated
  209. // as modifications rather than adds.
  210. RemoteEvent event =
  211. [self remoteEventAtSnapshotVersion:3
  212. targetMap:targetMap
  213. outstandingResponses:_noOutstandingResponses
  214. existingKeys:DocumentKeySet{existingDoc.key}
  215. changes:Changes(std::move(change1), std::move(change2))];
  216. XCTAssertEqual(event.snapshot_version(), testutil::Version(3));
  217. XCTAssertEqual(event.document_updates().size(), 2);
  218. XCTAssertEqualObjects(event.document_updates().at(existingDoc.key), existingDoc);
  219. XCTAssertEqualObjects(event.document_updates().at(newDoc.key), newDoc);
  220. // 'change1' and 'change2' affect six different targets
  221. XCTAssertEqual(event.target_changes().size(), 6);
  222. TargetChange targetChange1{_resumeToken1, false, DocumentKeySet{newDoc.key},
  223. DocumentKeySet{existingDoc.key}, DocumentKeySet{}};
  224. XCTAssertTrue(event.target_changes().at(1) == targetChange1);
  225. TargetChange targetChange2{_resumeToken1, false, DocumentKeySet{},
  226. DocumentKeySet{existingDoc.key}, DocumentKeySet{}};
  227. XCTAssertTrue(event.target_changes().at(2) == targetChange2);
  228. TargetChange targetChange3{_resumeToken1, false, DocumentKeySet{},
  229. DocumentKeySet{existingDoc.key}, DocumentKeySet{}};
  230. XCTAssertTrue(event.target_changes().at(3) == targetChange3);
  231. TargetChange targetChange4{_resumeToken1, false, DocumentKeySet{newDoc.key}, DocumentKeySet{},
  232. DocumentKeySet{existingDoc.key}};
  233. XCTAssertTrue(event.target_changes().at(4) == targetChange4);
  234. TargetChange targetChange5{_resumeToken1, false, DocumentKeySet{}, DocumentKeySet{},
  235. DocumentKeySet{existingDoc.key}};
  236. XCTAssertTrue(event.target_changes().at(5) == targetChange5);
  237. TargetChange targetChange6{_resumeToken1, false, DocumentKeySet{}, DocumentKeySet{},
  238. DocumentKeySet{existingDoc.key}};
  239. XCTAssertTrue(event.target_changes().at(6) == targetChange6);
  240. }
  241. - (void)testWillIgnoreEventsForPendingTargets {
  242. std::unordered_map<TargetId, FSTQueryData *> targetMap{[self queryDataForTargets:{1}]};
  243. FSTDocument *doc1 = FSTTestDoc("docs/1", 1, @{@"value" : @1}, FSTDocumentStateSynced);
  244. auto change1 = MakeDocChange({1}, {}, doc1.key, doc1);
  245. auto change2 = MakeTargetChange(WatchTargetChangeState::Removed, {1});
  246. auto change3 = MakeTargetChange(WatchTargetChangeState::Added, {1});
  247. FSTDocument *doc2 = FSTTestDoc("docs/2", 2, @{@"value" : @2}, FSTDocumentStateSynced);
  248. auto change4 = MakeDocChange({1}, {}, doc2.key, doc2);
  249. // We're waiting for the unwatch and watch ack
  250. std::unordered_map<TargetId, int> outstandingResponses{{1, 2}};
  251. RemoteEvent event =
  252. [self remoteEventAtSnapshotVersion:3
  253. targetMap:targetMap
  254. outstandingResponses:outstandingResponses
  255. existingKeys:DocumentKeySet {}
  256. changes:Changes(std::move(change1), std::move(change2),
  257. std::move(change3), std::move(change4))];
  258. XCTAssertEqual(event.snapshot_version(), testutil::Version(3));
  259. // doc1 is ignored because it was part of an inactive target, but doc2 is in the changes
  260. // because it become active.
  261. XCTAssertEqual(event.document_updates().size(), 1);
  262. XCTAssertEqualObjects(event.document_updates().at(doc2.key), doc2);
  263. XCTAssertEqual(event.target_changes().size(), 1);
  264. }
  265. - (void)testWillIgnoreEventsForRemovedTargets {
  266. std::unordered_map<TargetId, FSTQueryData *> targetMap{[self queryDataForTargets:{}]};
  267. FSTDocument *doc1 = FSTTestDoc("docs/1", 1, @{@"value" : @1}, FSTDocumentStateSynced);
  268. auto change1 = MakeDocChange({1}, {}, doc1.key, doc1);
  269. auto change2 = MakeTargetChange(WatchTargetChangeState::Removed, {1});
  270. // We're waiting for the unwatch ack
  271. std::unordered_map<TargetId, int> outstandingResponses{{1, 1}};
  272. RemoteEvent event =
  273. [self remoteEventAtSnapshotVersion:3
  274. targetMap:targetMap
  275. outstandingResponses:outstandingResponses
  276. existingKeys:DocumentKeySet {}
  277. changes:Changes(std::move(change1), std::move(change2))];
  278. XCTAssertEqual(event.snapshot_version(), testutil::Version(3));
  279. // doc1 is ignored because it was part of an inactive target
  280. XCTAssertEqual(event.document_updates().size(), 0);
  281. // Target 1 is ignored because it was removed
  282. XCTAssertEqual(event.target_changes().size(), 0);
  283. }
  284. - (void)testWillKeepResetMappingEvenWithUpdates {
  285. std::unordered_map<TargetId, FSTQueryData *> targetMap{[self queryDataForTargets:{1}]};
  286. FSTDocument *doc1 = FSTTestDoc("docs/1", 1, @{@"value" : @1}, FSTDocumentStateSynced);
  287. auto change1 = MakeDocChange({1}, {}, doc1.key, doc1);
  288. // Reset stream, ignoring doc1
  289. auto change2 = MakeTargetChange(WatchTargetChangeState::Reset, {1});
  290. // Add doc2, doc3
  291. FSTDocument *doc2 = FSTTestDoc("docs/2", 2, @{@"value" : @2}, FSTDocumentStateSynced);
  292. auto change3 = MakeDocChange({1}, {}, doc2.key, doc2);
  293. FSTDocument *doc3 = FSTTestDoc("docs/3", 3, @{@"value" : @3}, FSTDocumentStateSynced);
  294. auto change4 = MakeDocChange({1}, {}, doc3.key, doc3);
  295. // Remove doc2 again, should not show up in reset mapping
  296. auto change5 = MakeDocChange({}, {1}, doc2.key, doc2);
  297. RemoteEvent event =
  298. [self remoteEventAtSnapshotVersion:3
  299. targetMap:targetMap
  300. outstandingResponses:_noOutstandingResponses
  301. existingKeys:DocumentKeySet{doc1.key}
  302. changes:Changes(std::move(change1), std::move(change2),
  303. std::move(change3), std::move(change4),
  304. std::move(change5))];
  305. XCTAssertEqual(event.snapshot_version(), testutil::Version(3));
  306. XCTAssertEqual(event.document_updates().size(), 3);
  307. XCTAssertEqualObjects(event.document_updates().at(doc1.key), doc1);
  308. XCTAssertEqualObjects(event.document_updates().at(doc2.key), doc2);
  309. XCTAssertEqualObjects(event.document_updates().at(doc3.key), doc3);
  310. XCTAssertEqual(event.target_changes().size(), 1);
  311. // Only doc3 is part of the new mapping
  312. TargetChange expectedChange{_resumeToken1, false, DocumentKeySet{doc3.key}, DocumentKeySet{},
  313. DocumentKeySet{doc1.key}};
  314. XCTAssertTrue(event.target_changes().at(1) == expectedChange);
  315. }
  316. - (void)testWillHandleSingleReset {
  317. std::unordered_map<TargetId, FSTQueryData *> targetMap{[self queryDataForTargets:{1}]};
  318. // Reset target
  319. WatchTargetChange change{WatchTargetChangeState::Reset, {1}};
  320. WatchChangeAggregator aggregator = [self aggregatorWithTargetMap:targetMap
  321. outstandingResponses:_noOutstandingResponses
  322. existingKeys:DocumentKeySet {}
  323. changes:{}];
  324. aggregator.HandleTargetChange(change);
  325. RemoteEvent event = aggregator.CreateRemoteEvent(testutil::Version(3));
  326. XCTAssertEqual(event.snapshot_version(), testutil::Version(3));
  327. XCTAssertEqual(event.document_updates().size(), 0);
  328. XCTAssertEqual(event.target_changes().size(), 1);
  329. // Reset mapping is empty
  330. TargetChange expectedChange{
  331. [NSData data], false, DocumentKeySet{}, DocumentKeySet{}, DocumentKeySet{}};
  332. XCTAssertTrue(event.target_changes().at(1) == expectedChange);
  333. }
  334. - (void)testWillHandleTargetAddAndRemovalInSameBatch {
  335. std::unordered_map<TargetId, FSTQueryData *> targetMap{[self queryDataForTargets:{1, 2}]};
  336. FSTDocument *doc1a = FSTTestDoc("docs/1", 1, @{@"value" : @1}, FSTDocumentStateSynced);
  337. auto change1 = MakeDocChange({1}, {2}, doc1a.key, doc1a);
  338. FSTDocument *doc1b = FSTTestDoc("docs/1", 1, @{@"value" : @2}, FSTDocumentStateSynced);
  339. auto change2 = MakeDocChange({2}, {1}, doc1b.key, doc1b);
  340. RemoteEvent event =
  341. [self remoteEventAtSnapshotVersion:3
  342. targetMap:targetMap
  343. outstandingResponses:_noOutstandingResponses
  344. existingKeys:DocumentKeySet{doc1a.key}
  345. changes:Changes(std::move(change1), std::move(change2))];
  346. XCTAssertEqual(event.snapshot_version(), testutil::Version(3));
  347. XCTAssertEqual(event.document_updates().size(), 1);
  348. XCTAssertEqualObjects(event.document_updates().at(doc1b.key), doc1b);
  349. XCTAssertEqual(event.target_changes().size(), 2);
  350. TargetChange targetChange1{_resumeToken1, false, DocumentKeySet{}, DocumentKeySet{},
  351. DocumentKeySet{doc1b.key}};
  352. XCTAssertTrue(event.target_changes().at(1) == targetChange1);
  353. TargetChange targetChange2{_resumeToken1, false, DocumentKeySet{}, DocumentKeySet{doc1b.key},
  354. DocumentKeySet{}};
  355. XCTAssertTrue(event.target_changes().at(2) == targetChange2);
  356. }
  357. - (void)testTargetCurrentChangeWillMarkTheTargetCurrent {
  358. std::unordered_map<TargetId, FSTQueryData *> targetMap{[self queryDataForTargets:{1}]};
  359. auto change = MakeTargetChange(WatchTargetChangeState::Current, {1}, _resumeToken1);
  360. RemoteEvent event = [self remoteEventAtSnapshotVersion:3
  361. targetMap:targetMap
  362. outstandingResponses:_noOutstandingResponses
  363. existingKeys:DocumentKeySet {}
  364. changes:Changes(std::move(change))];
  365. XCTAssertEqual(event.snapshot_version(), testutil::Version(3));
  366. XCTAssertEqual(event.document_updates().size(), 0);
  367. XCTAssertEqual(event.target_changes().size(), 1);
  368. TargetChange targetChange1{_resumeToken1, true, DocumentKeySet{}, DocumentKeySet{},
  369. DocumentKeySet{}};
  370. XCTAssertTrue(event.target_changes().at(1) == targetChange1);
  371. }
  372. - (void)testTargetAddedChangeWillResetPreviousState {
  373. std::unordered_map<TargetId, FSTQueryData *> targetMap{[self queryDataForTargets:{1, 3}]};
  374. FSTDocument *doc1 = FSTTestDoc("docs/1", 1, @{@"value" : @1}, FSTDocumentStateSynced);
  375. auto change1 = MakeDocChange({1, 3}, {2}, doc1.key, doc1);
  376. auto change2 = MakeTargetChange(WatchTargetChangeState::Current, {1, 2, 3}, _resumeToken1);
  377. auto change3 = MakeTargetChange(WatchTargetChangeState::Removed, {1});
  378. auto change4 = MakeTargetChange(WatchTargetChangeState::Removed, {2});
  379. auto change5 = MakeTargetChange(WatchTargetChangeState::Added, {1});
  380. FSTDocument *doc2 = FSTTestDoc("docs/2", 2, @{@"value" : @2}, FSTDocumentStateSynced);
  381. auto change6 = MakeDocChange({1}, {3}, doc2.key, doc2);
  382. std::unordered_map<TargetId, int> outstandingResponses{{1, 2}, {2, 1}};
  383. RemoteEvent event =
  384. [self remoteEventAtSnapshotVersion:3
  385. targetMap:targetMap
  386. outstandingResponses:outstandingResponses
  387. existingKeys:DocumentKeySet{doc2.key}
  388. changes:Changes(std::move(change1), std::move(change2),
  389. std::move(change3), std::move(change4),
  390. std::move(change5), std::move(change6))];
  391. XCTAssertEqual(event.snapshot_version(), testutil::Version(3));
  392. XCTAssertEqual(event.document_updates().size(), 2);
  393. XCTAssertEqualObjects(event.document_updates().at(doc1.key), doc1);
  394. XCTAssertEqualObjects(event.document_updates().at(doc2.key), doc2);
  395. // target 1 and 3 are affected (1 because of re-add), target 2 is not because of remove
  396. XCTAssertEqual(event.target_changes().size(), 2);
  397. // doc1 was before the remove, so it does not show up in the mapping.
  398. // Current was before the remove.
  399. TargetChange targetChange1{_resumeToken1, false, DocumentKeySet{}, DocumentKeySet{doc2.key},
  400. DocumentKeySet{}};
  401. XCTAssertTrue(event.target_changes().at(1) == targetChange1);
  402. // Doc1 was before the remove
  403. // Current was before the remove
  404. TargetChange targetChange3{_resumeToken1, true, DocumentKeySet{doc1.key}, DocumentKeySet{},
  405. DocumentKeySet{doc2.key}};
  406. XCTAssertTrue(event.target_changes().at(3) == targetChange3);
  407. }
  408. - (void)testNoChangeWillStillMarkTheAffectedTargets {
  409. std::unordered_map<TargetId, FSTQueryData *> targetMap{[self queryDataForTargets:{1}]};
  410. WatchChangeAggregator aggregator = [self aggregatorWithTargetMap:targetMap
  411. outstandingResponses:_noOutstandingResponses
  412. existingKeys:DocumentKeySet {}
  413. changes:{}];
  414. WatchTargetChange change{WatchTargetChangeState::NoChange, {1}, _resumeToken1};
  415. aggregator.HandleTargetChange(change);
  416. RemoteEvent event = aggregator.CreateRemoteEvent(testutil::Version(3));
  417. XCTAssertEqual(event.snapshot_version(), testutil::Version(3));
  418. XCTAssertEqual(event.document_updates().size(), 0);
  419. XCTAssertEqual(event.target_changes().size(), 1);
  420. TargetChange targetChange{_resumeToken1, false, DocumentKeySet{}, DocumentKeySet{},
  421. DocumentKeySet{}};
  422. XCTAssertTrue(event.target_changes().at(1) == targetChange);
  423. }
  424. - (void)testExistenceFilterMismatchClearsTarget {
  425. std::unordered_map<TargetId, FSTQueryData *> targetMap{[self queryDataForTargets:{1, 2}]};
  426. FSTDocument *doc1 = FSTTestDoc("docs/1", 1, @{@"value" : @1}, FSTDocumentStateSynced);
  427. auto change1 = MakeDocChange({1}, {}, doc1.key, doc1);
  428. FSTDocument *doc2 = FSTTestDoc("docs/2", 2, @{@"value" : @2}, FSTDocumentStateSynced);
  429. auto change2 = MakeDocChange({1}, {}, doc2.key, doc2);
  430. auto change3 = MakeTargetChange(WatchTargetChangeState::Current, {1}, _resumeToken1);
  431. WatchChangeAggregator aggregator = [self
  432. aggregatorWithTargetMap:targetMap
  433. outstandingResponses:_noOutstandingResponses
  434. existingKeys:DocumentKeySet{doc1.key, doc2.key}
  435. changes:Changes(std::move(change1), std::move(change2), std::move(change3))];
  436. RemoteEvent event = aggregator.CreateRemoteEvent(testutil::Version(3));
  437. XCTAssertEqual(event.snapshot_version(), testutil::Version(3));
  438. XCTAssertEqual(event.document_updates().size(), 2);
  439. XCTAssertEqualObjects(event.document_updates().at(doc1.key), doc1);
  440. XCTAssertEqualObjects(event.document_updates().at(doc2.key), doc2);
  441. XCTAssertEqual(event.target_changes().size(), 2);
  442. TargetChange targetChange1{_resumeToken1, true, DocumentKeySet{},
  443. DocumentKeySet{doc1.key, doc2.key}, DocumentKeySet{}};
  444. XCTAssertTrue(event.target_changes().at(1) == targetChange1);
  445. TargetChange targetChange2{_resumeToken1, false, DocumentKeySet{}, DocumentKeySet{},
  446. DocumentKeySet{}};
  447. XCTAssertTrue(event.target_changes().at(2) == targetChange2);
  448. // The existence filter mismatch will remove the document from target 1,
  449. // but not synthesize a document delete.
  450. ExistenceFilterWatchChange change4{ExistenceFilter{1}, 1};
  451. aggregator.HandleExistenceFilter(change4);
  452. event = aggregator.CreateRemoteEvent(testutil::Version(4));
  453. TargetChange targetChange3{
  454. [NSData data], false, DocumentKeySet{}, DocumentKeySet{}, DocumentKeySet{doc1.key, doc2.key}};
  455. XCTAssertTrue(event.target_changes().at(1) == targetChange3);
  456. XCTAssertEqual(event.target_changes().size(), 1);
  457. XCTAssertEqual(event.target_mismatches().size(), 1);
  458. XCTAssertEqual(event.document_updates().size(), 0);
  459. }
  460. - (void)testExistenceFilterMismatchRemovesCurrentChanges {
  461. std::unordered_map<TargetId, FSTQueryData *> targetMap{[self queryDataForTargets:{1}]};
  462. WatchChangeAggregator aggregator = [self aggregatorWithTargetMap:targetMap
  463. outstandingResponses:_noOutstandingResponses
  464. existingKeys:DocumentKeySet {}
  465. changes:{}];
  466. WatchTargetChange markCurrent{WatchTargetChangeState::Current, {1}, _resumeToken1};
  467. aggregator.HandleTargetChange(markCurrent);
  468. FSTDocument *doc1 = FSTTestDoc("docs/1", 1, @{@"value" : @1}, FSTDocumentStateSynced);
  469. DocumentWatchChange addDoc{{1}, {}, doc1.key, doc1};
  470. aggregator.HandleDocumentChange(addDoc);
  471. // The existence filter mismatch will remove the document from target 1, but not synthesize a
  472. // document delete.
  473. ExistenceFilterWatchChange existenceFilter{ExistenceFilter{0}, 1};
  474. aggregator.HandleExistenceFilter(existenceFilter);
  475. RemoteEvent event = aggregator.CreateRemoteEvent(testutil::Version(3));
  476. XCTAssertEqual(event.snapshot_version(), testutil::Version(3));
  477. XCTAssertEqual(event.document_updates().size(), 1);
  478. XCTAssertEqual(event.target_mismatches().size(), 1);
  479. XCTAssertEqualObjects(event.document_updates().at(doc1.key), doc1);
  480. XCTAssertEqual(event.target_changes().size(), 1);
  481. TargetChange targetChange1{
  482. [NSData data], false, DocumentKeySet{}, DocumentKeySet{}, DocumentKeySet{}};
  483. XCTAssertTrue(event.target_changes().at(1) == targetChange1);
  484. }
  485. - (void)testDocumentUpdate {
  486. std::unordered_map<TargetId, FSTQueryData *> targetMap{[self queryDataForTargets:{1}]};
  487. FSTDocument *doc1 = FSTTestDoc("docs/1", 1, @{@"value" : @1}, FSTDocumentStateSynced);
  488. auto change1 = MakeDocChange({1}, {}, doc1.key, doc1);
  489. FSTDocument *doc2 = FSTTestDoc("docs/2", 2, @{@"value" : @2}, FSTDocumentStateSynced);
  490. auto change2 = MakeDocChange({1}, {}, doc2.key, doc2);
  491. WatchChangeAggregator aggregator =
  492. [self aggregatorWithTargetMap:targetMap
  493. outstandingResponses:_noOutstandingResponses
  494. existingKeys:DocumentKeySet {}
  495. changes:Changes(std::move(change1), std::move(change2))];
  496. RemoteEvent event = aggregator.CreateRemoteEvent(testutil::Version(3));
  497. XCTAssertEqual(event.snapshot_version(), testutil::Version(3));
  498. XCTAssertEqual(event.document_updates().size(), 2);
  499. XCTAssertEqualObjects(event.document_updates().at(doc1.key), doc1);
  500. XCTAssertEqualObjects(event.document_updates().at(doc2.key), doc2);
  501. _targetMetadataProvider.SetSyncedKeys(DocumentKeySet{doc1.key, doc2.key}, targetMap[1]);
  502. FSTDeletedDocument *deletedDoc1 = [FSTDeletedDocument documentWithKey:doc1.key
  503. version:testutil::Version(3)
  504. hasCommittedMutations:NO];
  505. DocumentWatchChange change3{{}, {1}, deletedDoc1.key, deletedDoc1};
  506. aggregator.HandleDocumentChange(change3);
  507. FSTDocument *updatedDoc2 = FSTTestDoc("docs/2", 3, @{@"value" : @2}, FSTDocumentStateSynced);
  508. DocumentWatchChange change4{{1}, {}, updatedDoc2.key, updatedDoc2};
  509. aggregator.HandleDocumentChange(change4);
  510. FSTDocument *doc3 = FSTTestDoc("docs/3", 3, @{@"value" : @3}, FSTDocumentStateSynced);
  511. DocumentWatchChange change5{{1}, {}, doc3.key, doc3};
  512. aggregator.HandleDocumentChange(change5);
  513. event = aggregator.CreateRemoteEvent(testutil::Version(3));
  514. XCTAssertEqual(event.snapshot_version(), testutil::Version(3));
  515. XCTAssertEqual(event.document_updates().size(), 3);
  516. // doc1 is replaced
  517. XCTAssertEqualObjects(event.document_updates().at(doc1.key), deletedDoc1);
  518. // doc2 is updated
  519. XCTAssertEqualObjects(event.document_updates().at(doc2.key), updatedDoc2);
  520. // doc3 is new
  521. XCTAssertEqualObjects(event.document_updates().at(doc3.key), doc3);
  522. // Target is unchanged
  523. XCTAssertEqual(event.target_changes().size(), 1);
  524. TargetChange targetChange1{_resumeToken1, false, DocumentKeySet{doc3.key},
  525. DocumentKeySet{updatedDoc2.key}, DocumentKeySet{deletedDoc1.key}};
  526. XCTAssertTrue(event.target_changes().at(1) == targetChange1);
  527. }
  528. - (void)testResumeTokensHandledPerTarget {
  529. std::unordered_map<TargetId, FSTQueryData *> targetMap{[self queryDataForTargets:{1, 2}]};
  530. WatchChangeAggregator aggregator = [self aggregatorWithTargetMap:targetMap
  531. outstandingResponses:_noOutstandingResponses
  532. existingKeys:DocumentKeySet {}
  533. changes:{}];
  534. WatchTargetChange change1{WatchTargetChangeState::Current, {1}, _resumeToken1};
  535. aggregator.HandleTargetChange(change1);
  536. NSData *resumeToken2 = [@"resume2" dataUsingEncoding:NSUTF8StringEncoding];
  537. WatchTargetChange change2{WatchTargetChangeState::Current, {2}, resumeToken2};
  538. aggregator.HandleTargetChange(change2);
  539. RemoteEvent event = aggregator.CreateRemoteEvent(testutil::Version(3));
  540. XCTAssertEqual(event.target_changes().size(), 2);
  541. TargetChange targetChange1{_resumeToken1, true, DocumentKeySet{}, DocumentKeySet{},
  542. DocumentKeySet{}};
  543. XCTAssertTrue(event.target_changes().at(1) == targetChange1);
  544. TargetChange targetChange2{resumeToken2, true, DocumentKeySet{}, DocumentKeySet{},
  545. DocumentKeySet{}};
  546. XCTAssertTrue(event.target_changes().at(2) == targetChange2);
  547. }
  548. - (void)testLastResumeTokenWins {
  549. std::unordered_map<TargetId, FSTQueryData *> targetMap{[self queryDataForTargets:{1, 2}]};
  550. WatchChangeAggregator aggregator = [self aggregatorWithTargetMap:targetMap
  551. outstandingResponses:_noOutstandingResponses
  552. existingKeys:DocumentKeySet {}
  553. changes:{}];
  554. WatchTargetChange change1{WatchTargetChangeState::Current, {1}, _resumeToken1};
  555. aggregator.HandleTargetChange(change1);
  556. NSData *resumeToken2 = [@"resume2" dataUsingEncoding:NSUTF8StringEncoding];
  557. WatchTargetChange change2{WatchTargetChangeState::NoChange, {1}, resumeToken2};
  558. aggregator.HandleTargetChange(change2);
  559. NSData *resumeToken3 = [@"resume3" dataUsingEncoding:NSUTF8StringEncoding];
  560. WatchTargetChange change3{WatchTargetChangeState::NoChange, {2}, resumeToken3};
  561. aggregator.HandleTargetChange(change3);
  562. RemoteEvent event = aggregator.CreateRemoteEvent(testutil::Version(3));
  563. XCTAssertEqual(event.target_changes().size(), 2);
  564. TargetChange targetChange1{resumeToken2, true, DocumentKeySet{}, DocumentKeySet{},
  565. DocumentKeySet{}};
  566. XCTAssertTrue(event.target_changes().at(1) == targetChange1);
  567. TargetChange targetChange2{resumeToken3, false, DocumentKeySet{}, DocumentKeySet{},
  568. DocumentKeySet{}};
  569. XCTAssertTrue(event.target_changes().at(2) == targetChange2);
  570. }
  571. - (void)testSynthesizeDeletes {
  572. std::unordered_map<TargetId, FSTQueryData *> targetMap{[self queryDataForLimboTargets:{1}]};
  573. DocumentKey limboKey = testutil::Key("coll/limbo");
  574. auto resolveLimboTarget = MakeTargetChange(WatchTargetChangeState::Current, {1});
  575. RemoteEvent event = [self remoteEventAtSnapshotVersion:3
  576. targetMap:targetMap
  577. outstandingResponses:_noOutstandingResponses
  578. existingKeys:DocumentKeySet {}
  579. changes:Changes(std::move(resolveLimboTarget))];
  580. FSTDeletedDocument *expected = [FSTDeletedDocument documentWithKey:limboKey
  581. version:event.snapshot_version()
  582. hasCommittedMutations:NO];
  583. XCTAssertEqualObjects(event.document_updates().at(limboKey), expected);
  584. XCTAssertTrue(event.limbo_document_changes().contains(limboKey));
  585. }
  586. - (void)testDoesntSynthesizeDeletesForWrongState {
  587. std::unordered_map<TargetId, FSTQueryData *> targetMap{[self queryDataForTargets:{1}]};
  588. auto wrongState = MakeTargetChange(WatchTargetChangeState::NoChange, {1});
  589. RemoteEvent event = [self remoteEventAtSnapshotVersion:3
  590. targetMap:targetMap
  591. outstandingResponses:_noOutstandingResponses
  592. existingKeys:DocumentKeySet {}
  593. changes:Changes(std::move(wrongState))];
  594. XCTAssertEqual(event.document_updates().size(), 0);
  595. XCTAssertEqual(event.limbo_document_changes().size(), 0);
  596. }
  597. - (void)testDoesntSynthesizeDeletesForExistingDoc {
  598. std::unordered_map<TargetId, FSTQueryData *> targetMap{[self queryDataForTargets:{3}]};
  599. auto hasDocument = MakeTargetChange(WatchTargetChangeState::Current, {3});
  600. RemoteEvent event =
  601. [self remoteEventAtSnapshotVersion:3
  602. targetMap:targetMap
  603. outstandingResponses:_noOutstandingResponses
  604. existingKeys:DocumentKeySet{FSTTestDocKey(@"coll/limbo")}
  605. changes:Changes(std::move(hasDocument))];
  606. XCTAssertEqual(event.document_updates().size(), 0);
  607. XCTAssertEqual(event.limbo_document_changes().size(), 0);
  608. }
  609. - (void)testSeparatesDocumentUpdates {
  610. std::unordered_map<TargetId, FSTQueryData *> targetMap{[self queryDataForLimboTargets:{1}]};
  611. FSTDocument *newDoc = FSTTestDoc("docs/new", 1, @{@"key" : @"value"}, FSTDocumentStateSynced);
  612. auto newDocChange = MakeDocChange({1}, {}, newDoc.key, newDoc);
  613. FSTDocument *existingDoc =
  614. FSTTestDoc("docs/existing", 1, @{@"some" : @"data"}, FSTDocumentStateSynced);
  615. auto existingDocChange = MakeDocChange({1}, {}, existingDoc.key, existingDoc);
  616. FSTDeletedDocument *deletedDoc = FSTTestDeletedDoc("docs/deleted", 1, NO);
  617. auto deletedDocChange = MakeDocChange({}, {1}, deletedDoc.key, deletedDoc);
  618. FSTDeletedDocument *missingDoc = FSTTestDeletedDoc("docs/missing", 1, NO);
  619. auto missingDocChange = MakeDocChange({}, {1}, missingDoc.key, missingDoc);
  620. RemoteEvent event = [self
  621. remoteEventAtSnapshotVersion:3
  622. targetMap:targetMap
  623. outstandingResponses:_noOutstandingResponses
  624. existingKeys:DocumentKeySet{existingDoc.key, deletedDoc.key}
  625. changes:Changes(std::move(newDocChange), std::move(existingDocChange),
  626. std::move(deletedDocChange),
  627. std::move(missingDocChange))];
  628. TargetChange targetChange2{_resumeToken1, false, DocumentKeySet{newDoc.key},
  629. DocumentKeySet{existingDoc.key}, DocumentKeySet{deletedDoc.key}};
  630. XCTAssertTrue(event.target_changes().at(1) == targetChange2);
  631. }
  632. - (void)testTracksLimboDocuments {
  633. std::unordered_map<TargetId, FSTQueryData *> targetMap = [self queryDataForTargets:{1}];
  634. auto additionalTargets = [self queryDataForLimboTargets:{2}];
  635. targetMap.insert(additionalTargets.begin(), additionalTargets.end());
  636. // Add 3 docs: 1 is limbo and non-limbo, 2 is limbo-only, 3 is non-limbo
  637. FSTDocument *doc1 = FSTTestDoc("docs/1", 1, @{@"key" : @"value"}, FSTDocumentStateSynced);
  638. FSTDocument *doc2 = FSTTestDoc("docs/2", 1, @{@"key" : @"value"}, FSTDocumentStateSynced);
  639. FSTDocument *doc3 = FSTTestDoc("docs/3", 1, @{@"key" : @"value"}, FSTDocumentStateSynced);
  640. // Target 2 is a limbo target
  641. auto docChange1 = MakeDocChange({1, 2}, {}, doc1.key, doc1);
  642. auto docChange2 = MakeDocChange({2}, {}, doc2.key, doc2);
  643. auto docChange3 = MakeDocChange({1}, {}, doc3.key, doc3);
  644. auto targetsChange = MakeTargetChange(WatchTargetChangeState::Current, {1, 2});
  645. RemoteEvent event =
  646. [self remoteEventAtSnapshotVersion:3
  647. targetMap:targetMap
  648. outstandingResponses:_noOutstandingResponses
  649. existingKeys:DocumentKeySet {}
  650. changes:Changes(std::move(docChange1), std::move(docChange2),
  651. std::move(docChange3), std::move(targetsChange))];
  652. DocumentKeySet limboDocChanges = event.limbo_document_changes();
  653. // Doc1 is in both limbo and non-limbo targets, therefore not tracked as limbo
  654. XCTAssertFalse(limboDocChanges.contains(doc1.key));
  655. // Doc2 is only in the limbo target, so is tracked as a limbo document
  656. XCTAssertTrue(limboDocChanges.contains(doc2.key));
  657. // Doc3 is only in the non-limbo target, therefore not tracked as limbo
  658. XCTAssertFalse(limboDocChanges.contains(doc3.key));
  659. }
  660. @end
  661. NS_ASSUME_NONNULL_END