FSTRemoteEventTests.mm 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730
  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/Remote/FSTRemoteEvent.h"
  17. #import <XCTest/XCTest.h>
  18. #import "Firestore/Source/Core/FSTQuery.h"
  19. #import "Firestore/Source/Local/FSTQueryData.h"
  20. #import "Firestore/Source/Model/FSTDocument.h"
  21. #import "Firestore/Source/Model/FSTDocumentKey.h"
  22. #import "Firestore/Source/Remote/FSTExistenceFilter.h"
  23. #import "Firestore/Source/Remote/FSTWatchChange.h"
  24. #include "Firestore/core/src/firebase/firestore/model/document_key.h"
  25. #import "Firestore/Example/Tests/Remote/FSTWatchChange+Testing.h"
  26. #import "Firestore/Example/Tests/Util/FSTHelpers.h"
  27. #include "Firestore/core/test/firebase/firestore/testutil/testutil.h"
  28. namespace testutil = firebase::firestore::testutil;
  29. using firebase::firestore::model::DocumentKey;
  30. using firebase::firestore::model::DocumentKeySet;
  31. NS_ASSUME_NONNULL_BEGIN
  32. @interface FSTRemoteEventTests : XCTestCase
  33. @end
  34. @implementation FSTRemoteEventTests {
  35. NSData *_resumeToken1;
  36. NSMutableDictionary<NSNumber *, NSNumber *> *_noPendingResponses;
  37. }
  38. - (void)setUp {
  39. _resumeToken1 = [@"resume1" dataUsingEncoding:NSUTF8StringEncoding];
  40. _noPendingResponses = [NSMutableDictionary dictionary];
  41. }
  42. - (FSTWatchChangeAggregator *)aggregatorWithTargets:(NSArray<NSNumber *> *)targets
  43. outstanding:
  44. (NSDictionary<NSNumber *, NSNumber *> *)outstanding
  45. changes:(NSArray<FSTWatchChange *> *)watchChanges {
  46. NSMutableDictionary<NSNumber *, FSTQueryData *> *listens = [NSMutableDictionary dictionary];
  47. FSTQueryData *dummyQueryData = [FSTQueryData alloc];
  48. for (NSNumber *targetID in targets) {
  49. listens[targetID] = dummyQueryData;
  50. }
  51. FSTWatchChangeAggregator *aggregator =
  52. [[FSTWatchChangeAggregator alloc] initWithSnapshotVersion:testutil::Version(3)
  53. listenTargets:listens
  54. pendingTargetResponses:outstanding];
  55. [aggregator addWatchChanges:watchChanges];
  56. return aggregator;
  57. }
  58. - (void)testWillAccumulateDocumentAddedAndRemovedEvents {
  59. FSTDocument *doc1 = FSTTestDoc("docs/1", 1, @{ @"value" : @1 }, NO);
  60. FSTDocument *doc2 = FSTTestDoc("docs/2", 2, @{ @"value" : @2 }, NO);
  61. FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1, @2, @3 ]
  62. removedTargetIDs:@[ @4, @5, @6 ]
  63. documentKey:doc1.key
  64. document:doc1];
  65. FSTWatchChange *change2 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1, @4 ]
  66. removedTargetIDs:@[ @2, @6 ]
  67. documentKey:doc2.key
  68. document:doc2];
  69. FSTWatchChangeAggregator *aggregator = [self aggregatorWithTargets:@[ @1, @2, @3, @4, @5, @6 ]
  70. outstanding:_noPendingResponses
  71. changes:@[ change1, change2 ]];
  72. FSTRemoteEvent *event = [aggregator remoteEvent];
  73. XCTAssertEqual(event.snapshotVersion, testutil::Version(3));
  74. XCTAssertEqual(event.documentUpdates.size(), 2);
  75. XCTAssertEqualObjects(event.documentUpdates.at(doc1.key), doc1);
  76. XCTAssertEqualObjects(event.documentUpdates.at(doc2.key), doc2);
  77. XCTAssertEqual(event.targetChanges.count, 6);
  78. FSTUpdateMapping *mapping1 =
  79. [FSTUpdateMapping mappingWithAddedDocuments:@[ doc1, doc2 ] removedDocuments:@[]];
  80. XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
  81. FSTUpdateMapping *mapping2 =
  82. [FSTUpdateMapping mappingWithAddedDocuments:@[ doc1 ] removedDocuments:@[ doc2 ]];
  83. XCTAssertEqualObjects(event.targetChanges[@2].mapping, mapping2);
  84. FSTUpdateMapping *mapping3 =
  85. [FSTUpdateMapping mappingWithAddedDocuments:@[ doc1 ] removedDocuments:@[]];
  86. XCTAssertEqualObjects(event.targetChanges[@3].mapping, mapping3);
  87. FSTUpdateMapping *mapping4 =
  88. [FSTUpdateMapping mappingWithAddedDocuments:@[ doc2 ] removedDocuments:@[ doc1 ]];
  89. XCTAssertEqualObjects(event.targetChanges[@4].mapping, mapping4);
  90. FSTUpdateMapping *mapping5 =
  91. [FSTUpdateMapping mappingWithAddedDocuments:@[] removedDocuments:@[ doc1 ]];
  92. XCTAssertEqualObjects(event.targetChanges[@5].mapping, mapping5);
  93. FSTUpdateMapping *mapping6 =
  94. [FSTUpdateMapping mappingWithAddedDocuments:@[] removedDocuments:@[ doc1, doc2 ]];
  95. XCTAssertEqualObjects(event.targetChanges[@6].mapping, mapping6);
  96. }
  97. - (void)testWillIgnoreEventsForPendingTargets {
  98. FSTDocument *doc1 = FSTTestDoc("docs/1", 1, @{ @"value" : @1 }, NO);
  99. FSTDocument *doc2 = FSTTestDoc("docs/2", 2, @{ @"value" : @2 }, NO);
  100. FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
  101. removedTargetIDs:@[]
  102. documentKey:doc1.key
  103. document:doc1];
  104. FSTWatchChange *change2 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateRemoved
  105. targetIDs:@[ @1 ]
  106. cause:nil];
  107. FSTWatchChange *change3 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateAdded
  108. targetIDs:@[ @1 ]
  109. cause:nil];
  110. FSTWatchChange *change4 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
  111. removedTargetIDs:@[]
  112. documentKey:doc2.key
  113. document:doc2];
  114. // We're waiting for the unwatch and watch ack
  115. NSDictionary<NSNumber *, NSNumber *> *pendingResponses = @{ @1 : @2 };
  116. FSTWatchChangeAggregator *aggregator =
  117. [self aggregatorWithTargets:@[ @1 ]
  118. outstanding:pendingResponses
  119. changes:@[ change1, change2, change3, change4 ]];
  120. FSTRemoteEvent *event = [aggregator remoteEvent];
  121. XCTAssertEqual(event.snapshotVersion, testutil::Version(3));
  122. // doc1 is ignored because it was part of an inactive target, but doc2 is in the changes
  123. // because it become active.
  124. XCTAssertEqual(event.documentUpdates.size(), 1);
  125. XCTAssertEqualObjects(event.documentUpdates.at(doc2.key), doc2);
  126. XCTAssertEqual(event.targetChanges.count, 1);
  127. }
  128. - (void)testWillIgnoreEventsForRemovedTargets {
  129. FSTDocument *doc1 = FSTTestDoc("docs/1", 1, @{ @"value" : @1 }, NO);
  130. FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
  131. removedTargetIDs:@[]
  132. documentKey:doc1.key
  133. document:doc1];
  134. FSTWatchChange *change2 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateRemoved
  135. targetIDs:@[ @1 ]
  136. cause:nil];
  137. // We're waiting for the unwatch ack
  138. NSDictionary<NSNumber *, NSNumber *> *pendingResponses = @{ @1 : @1 };
  139. FSTWatchChangeAggregator *aggregator =
  140. [self aggregatorWithTargets:@[] outstanding:pendingResponses changes:@[ change1, change2 ]];
  141. FSTRemoteEvent *event = [aggregator remoteEvent];
  142. XCTAssertEqual(event.snapshotVersion, testutil::Version(3));
  143. // doc1 is ignored because it was part of an inactive target
  144. XCTAssertEqual(event.documentUpdates.size(), 0);
  145. // Target 1 is ignored because it was removed
  146. XCTAssertEqual(event.targetChanges.count, 0);
  147. }
  148. - (void)testWillKeepResetMappingEvenWithUpdates {
  149. FSTDocument *doc1 = FSTTestDoc("docs/1", 1, @{ @"value" : @1 }, NO);
  150. FSTDocument *doc2 = FSTTestDoc("docs/2", 2, @{ @"value" : @2 }, NO);
  151. FSTDocument *doc3 = FSTTestDoc("docs/3", 3, @{ @"value" : @3 }, NO);
  152. FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
  153. removedTargetIDs:@[]
  154. documentKey:doc1.key
  155. document:doc1];
  156. // Reset stream, ignoring doc1
  157. FSTWatchChange *change2 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateReset
  158. targetIDs:@[ @1 ]
  159. cause:nil];
  160. // Add doc2, doc3
  161. FSTWatchChange *change3 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
  162. removedTargetIDs:@[]
  163. documentKey:doc2.key
  164. document:doc2];
  165. FSTWatchChange *change4 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
  166. removedTargetIDs:@[]
  167. documentKey:doc3.key
  168. document:doc3];
  169. // Remove doc2 again, should not show up in reset mapping
  170. FSTWatchChange *change5 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[]
  171. removedTargetIDs:@[ @1 ]
  172. documentKey:doc2.key
  173. document:doc2];
  174. FSTWatchChangeAggregator *aggregator =
  175. [self aggregatorWithTargets:@[ @1 ]
  176. outstanding:_noPendingResponses
  177. changes:@[ change1, change2, change3, change4, change5 ]];
  178. FSTRemoteEvent *event = [aggregator remoteEvent];
  179. XCTAssertEqual(event.snapshotVersion, testutil::Version(3));
  180. XCTAssertEqual(event.documentUpdates.size(), 3);
  181. XCTAssertEqualObjects(event.documentUpdates.at(doc1.key), doc1);
  182. XCTAssertEqualObjects(event.documentUpdates.at(doc2.key), doc2);
  183. XCTAssertEqualObjects(event.documentUpdates.at(doc3.key), doc3);
  184. XCTAssertEqual(event.targetChanges.count, 1);
  185. // Only doc3 is part of the new mapping
  186. FSTResetMapping *expectedMapping = [FSTResetMapping mappingWithDocuments:@[ doc3 ]];
  187. XCTAssertEqualObjects(event.targetChanges[@1].mapping, expectedMapping);
  188. }
  189. - (void)testWillHandleSingleReset {
  190. // Reset target
  191. FSTWatchChange *change = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateReset
  192. targetIDs:@[ @1 ]
  193. cause:nil];
  194. FSTWatchChangeAggregator *aggregator =
  195. [self aggregatorWithTargets:@[ @1 ] outstanding:_noPendingResponses changes:@[ change ]];
  196. FSTRemoteEvent *event = [aggregator remoteEvent];
  197. XCTAssertEqual(event.snapshotVersion, testutil::Version(3));
  198. XCTAssertEqual(event.documentUpdates.size(), 0);
  199. XCTAssertEqual(event.targetChanges.count, 1);
  200. // Reset mapping is empty
  201. FSTResetMapping *expectedMapping = [FSTResetMapping mappingWithDocuments:@[]];
  202. XCTAssertEqualObjects(event.targetChanges[@1].mapping, expectedMapping);
  203. }
  204. - (void)testWillHandleTargetAddAndRemovalInSameBatch {
  205. FSTDocument *doc1a = FSTTestDoc("docs/1", 1, @{ @"value" : @1 }, NO);
  206. FSTDocument *doc1b = FSTTestDoc("docs/1", 1, @{ @"value" : @2 }, NO);
  207. FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
  208. removedTargetIDs:@[ @2 ]
  209. documentKey:doc1a.key
  210. document:doc1a];
  211. FSTWatchChange *change2 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @2 ]
  212. removedTargetIDs:@[ @1 ]
  213. documentKey:doc1b.key
  214. document:doc1b];
  215. FSTWatchChangeAggregator *aggregator = [self aggregatorWithTargets:@[ @1, @2 ]
  216. outstanding:_noPendingResponses
  217. changes:@[ change1, change2 ]];
  218. FSTRemoteEvent *event = [aggregator remoteEvent];
  219. XCTAssertEqual(event.snapshotVersion, testutil::Version(3));
  220. XCTAssertEqual(event.documentUpdates.size(), 1);
  221. XCTAssertEqualObjects(event.documentUpdates.at(doc1b.key), doc1b);
  222. XCTAssertEqual(event.targetChanges.count, 2);
  223. FSTUpdateMapping *mapping1 =
  224. [FSTUpdateMapping mappingWithAddedDocuments:@[] removedDocuments:@[ doc1b ]];
  225. XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
  226. FSTUpdateMapping *mapping2 =
  227. [FSTUpdateMapping mappingWithAddedDocuments:@[ doc1b ] removedDocuments:@[]];
  228. XCTAssertEqualObjects(event.targetChanges[@2].mapping, mapping2);
  229. }
  230. - (void)testTargetCurrentChangeWillMarkTheTargetCurrent {
  231. FSTWatchChange *change = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
  232. targetIDs:@[ @1 ]
  233. resumeToken:_resumeToken1];
  234. FSTWatchChangeAggregator *aggregator =
  235. [self aggregatorWithTargets:@[ @1 ] outstanding:_noPendingResponses changes:@[ change ]];
  236. FSTRemoteEvent *event = [aggregator remoteEvent];
  237. XCTAssertEqual(event.snapshotVersion, testutil::Version(3));
  238. XCTAssertEqual(event.documentUpdates.size(), 0);
  239. XCTAssertEqual(event.targetChanges.count, 1);
  240. FSTTargetChange *targetChange = event.targetChanges[@1];
  241. XCTAssertEqualObjects(targetChange.mapping, [[FSTUpdateMapping alloc] init]);
  242. XCTAssertEqual(targetChange.currentStatusUpdate, FSTCurrentStatusUpdateMarkCurrent);
  243. XCTAssertEqualObjects(targetChange.resumeToken, _resumeToken1);
  244. }
  245. - (void)testTargetAddedChangeWillResetPreviousState {
  246. FSTDocument *doc1 = FSTTestDoc("docs/1", 1, @{ @"value" : @1 }, NO);
  247. FSTDocument *doc2 = FSTTestDoc("docs/2", 2, @{ @"value" : @2 }, NO);
  248. FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1, @3 ]
  249. removedTargetIDs:@[ @2 ]
  250. documentKey:doc1.key
  251. document:doc1];
  252. FSTWatchChange *change2 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
  253. targetIDs:@[ @1, @2, @3 ]
  254. resumeToken:_resumeToken1];
  255. FSTWatchChange *change3 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateRemoved
  256. targetIDs:@[ @1 ]
  257. cause:nil];
  258. FSTWatchChange *change4 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateRemoved
  259. targetIDs:@[ @2 ]
  260. cause:nil];
  261. FSTWatchChange *change5 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateAdded
  262. targetIDs:@[ @1 ]
  263. cause:nil];
  264. FSTWatchChange *change6 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
  265. removedTargetIDs:@[ @3 ]
  266. documentKey:doc2.key
  267. document:doc2];
  268. NSDictionary<NSNumber *, NSNumber *> *pendingResponses = @{ @1 : @2, @2 : @1 };
  269. FSTWatchChangeAggregator *aggregator =
  270. [self aggregatorWithTargets:@[ @1, @3 ]
  271. outstanding:pendingResponses
  272. changes:@[ change1, change2, change3, change4, change5, change6 ]];
  273. FSTRemoteEvent *event = [aggregator remoteEvent];
  274. XCTAssertEqual(event.snapshotVersion, testutil::Version(3));
  275. XCTAssertEqual(event.documentUpdates.size(), 2);
  276. XCTAssertEqualObjects(event.documentUpdates.at(doc1.key), doc1);
  277. XCTAssertEqualObjects(event.documentUpdates.at(doc2.key), doc2);
  278. // target 1 and 3 are affected (1 because of re-add), target 2 is not because of remove
  279. XCTAssertEqual(event.targetChanges.count, 2);
  280. // doc1 was before the remove, so it does not show up in the mapping
  281. FSTUpdateMapping *mapping1 =
  282. [FSTUpdateMapping mappingWithAddedDocuments:@[ doc2 ] removedDocuments:@[]];
  283. XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
  284. // Current was before the remove
  285. XCTAssertEqual(event.targetChanges[@1].currentStatusUpdate, FSTCurrentStatusUpdateNone);
  286. // Doc1 was before the remove
  287. FSTUpdateMapping *mapping3 =
  288. [FSTUpdateMapping mappingWithAddedDocuments:@[ doc1 ] removedDocuments:@[ doc2 ]];
  289. XCTAssertEqualObjects(event.targetChanges[@3].mapping, mapping3);
  290. // Current was before the remove
  291. XCTAssertEqual(event.targetChanges[@3].currentStatusUpdate, FSTCurrentStatusUpdateMarkCurrent);
  292. XCTAssertEqualObjects(event.targetChanges[@3].resumeToken, _resumeToken1);
  293. }
  294. - (void)testNoChangeWillStillMarkTheAffectedTargets {
  295. FSTWatchChange *change = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateNoChange
  296. targetIDs:@[ @1 ]
  297. resumeToken:_resumeToken1];
  298. FSTWatchChangeAggregator *aggregator =
  299. [self aggregatorWithTargets:@[ @1 ] outstanding:_noPendingResponses changes:@[ change ]];
  300. FSTRemoteEvent *event = [aggregator remoteEvent];
  301. XCTAssertEqual(event.snapshotVersion, testutil::Version(3));
  302. XCTAssertEqual(event.documentUpdates.size(), 0);
  303. XCTAssertEqual(event.targetChanges.count, 1);
  304. XCTAssertEqualObjects(event.targetChanges[@1].mapping, [[FSTUpdateMapping alloc] init]);
  305. XCTAssertEqual(event.targetChanges[@1].currentStatusUpdate, FSTCurrentStatusUpdateNone);
  306. XCTAssertEqualObjects(event.targetChanges[@1].resumeToken, _resumeToken1);
  307. }
  308. - (void)testExistenceFiltersWillReplacePreviousExistenceFilters {
  309. FSTExistenceFilter *filter1 = [FSTExistenceFilter filterWithCount:1];
  310. FSTExistenceFilter *filter2 = [FSTExistenceFilter filterWithCount:2];
  311. FSTWatchChange *change1 = [FSTExistenceFilterWatchChange changeWithFilter:filter1 targetID:1];
  312. FSTWatchChange *change2 = [FSTExistenceFilterWatchChange changeWithFilter:filter1 targetID:2];
  313. // replace filter1 for target 2
  314. FSTWatchChange *change3 = [FSTExistenceFilterWatchChange changeWithFilter:filter2 targetID:2];
  315. FSTWatchChangeAggregator *aggregator =
  316. [self aggregatorWithTargets:@[ @1, @2 ]
  317. outstanding:_noPendingResponses
  318. changes:@[ change1, change2, change3 ]];
  319. FSTRemoteEvent *event = [aggregator remoteEvent];
  320. XCTAssertEqual(event.snapshotVersion, testutil::Version(3));
  321. XCTAssertEqual(event.documentUpdates.size(), 0);
  322. XCTAssertEqual(event.targetChanges.count, 0);
  323. XCTAssertEqual(aggregator.existenceFilters.count, 2);
  324. XCTAssertEqual(aggregator.existenceFilters[@1], filter1);
  325. XCTAssertEqual(aggregator.existenceFilters[@2], filter2);
  326. }
  327. - (void)testExistenceFilterMismatchResetsTarget {
  328. FSTDocument *doc1 = FSTTestDoc("docs/1", 1, @{ @"value" : @1 }, NO);
  329. FSTDocument *doc2 = FSTTestDoc("docs/2", 2, @{ @"value" : @2 }, NO);
  330. FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
  331. removedTargetIDs:@[]
  332. documentKey:doc1.key
  333. document:doc1];
  334. FSTWatchChange *change2 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
  335. removedTargetIDs:@[]
  336. documentKey:doc2.key
  337. document:doc2];
  338. FSTWatchChange *change3 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
  339. targetIDs:@[ @1 ]
  340. resumeToken:_resumeToken1];
  341. FSTWatchChangeAggregator *aggregator =
  342. [self aggregatorWithTargets:@[ @1 ]
  343. outstanding:_noPendingResponses
  344. changes:@[ change1, change2, change3 ]];
  345. FSTRemoteEvent *event = [aggregator remoteEvent];
  346. XCTAssertEqual(event.snapshotVersion, testutil::Version(3));
  347. XCTAssertEqual(event.documentUpdates.size(), 2);
  348. XCTAssertEqualObjects(event.documentUpdates.at(doc1.key), doc1);
  349. XCTAssertEqualObjects(event.documentUpdates.at(doc2.key), doc2);
  350. XCTAssertEqual(event.targetChanges.count, 1);
  351. FSTUpdateMapping *mapping1 =
  352. [FSTUpdateMapping mappingWithAddedDocuments:@[ doc1, doc2 ] removedDocuments:@[]];
  353. XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
  354. XCTAssertEqual(event.targetChanges[@1].snapshotVersion, testutil::Version(3));
  355. XCTAssertEqual(event.targetChanges[@1].currentStatusUpdate, FSTCurrentStatusUpdateMarkCurrent);
  356. XCTAssertEqualObjects(event.targetChanges[@1].resumeToken, _resumeToken1);
  357. [event handleExistenceFilterMismatchForTargetID:@1];
  358. // Mapping is reset
  359. XCTAssertEqualObjects(event.targetChanges[@1].mapping, [[FSTResetMapping alloc] init]);
  360. // Reset the resume snapshot
  361. XCTAssertEqual(event.targetChanges[@1].snapshotVersion, testutil::Version(0));
  362. // Target needs to be set to not current
  363. XCTAssertEqual(event.targetChanges[@1].currentStatusUpdate, FSTCurrentStatusUpdateMarkNotCurrent);
  364. XCTAssertEqual(event.targetChanges[@1].resumeToken.length, 0);
  365. }
  366. - (void)testDocumentUpdate {
  367. FSTDocument *doc1 = FSTTestDoc("docs/1", 1, @{ @"value" : @1 }, NO);
  368. FSTDeletedDocument *deletedDoc1 =
  369. [FSTDeletedDocument documentWithKey:doc1.key version:testutil::Version(3)];
  370. FSTDocument *doc2 = FSTTestDoc("docs/2", 2, @{ @"value" : @2 }, NO);
  371. FSTDocument *doc3 = FSTTestDoc("docs/3", 3, @{ @"value" : @3 }, NO);
  372. FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
  373. removedTargetIDs:@[]
  374. documentKey:doc1.key
  375. document:doc1];
  376. FSTWatchChange *change2 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
  377. removedTargetIDs:@[]
  378. documentKey:doc2.key
  379. document:doc2];
  380. FSTWatchChangeAggregator *aggregator = [self aggregatorWithTargets:@[ @1 ]
  381. outstanding:_noPendingResponses
  382. changes:@[ change1, change2 ]];
  383. FSTRemoteEvent *event = [aggregator remoteEvent];
  384. XCTAssertEqual(event.snapshotVersion, testutil::Version(3));
  385. XCTAssertEqual(event.documentUpdates.size(), 2);
  386. XCTAssertEqualObjects(event.documentUpdates.at(doc1.key), doc1);
  387. XCTAssertEqualObjects(event.documentUpdates.at(doc2.key), doc2);
  388. // Update doc1
  389. [event addDocumentUpdate:deletedDoc1];
  390. [event addDocumentUpdate:doc3];
  391. XCTAssertEqual(event.snapshotVersion, testutil::Version(3));
  392. XCTAssertEqual(event.documentUpdates.size(), 3);
  393. // doc1 is replaced
  394. XCTAssertEqualObjects(event.documentUpdates.at(doc1.key), deletedDoc1);
  395. // doc2 is untouched
  396. XCTAssertEqualObjects(event.documentUpdates.at(doc2.key), doc2);
  397. // doc3 is new
  398. XCTAssertEqualObjects(event.documentUpdates.at(doc3.key), doc3);
  399. // Target is unchanged
  400. XCTAssertEqual(event.targetChanges.count, 1);
  401. FSTUpdateMapping *mapping1 =
  402. [FSTUpdateMapping mappingWithAddedDocuments:@[ doc1, doc2 ] removedDocuments:@[]];
  403. XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
  404. }
  405. - (void)testResumeTokensHandledPerTarget {
  406. NSData *resumeToken2 = [@"resume2" dataUsingEncoding:NSUTF8StringEncoding];
  407. FSTWatchChange *change1 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
  408. targetIDs:@[ @1 ]
  409. resumeToken:_resumeToken1];
  410. FSTWatchChange *change2 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
  411. targetIDs:@[ @2 ]
  412. resumeToken:resumeToken2];
  413. FSTWatchChangeAggregator *aggregator = [self aggregatorWithTargets:@[ @1, @2 ]
  414. outstanding:_noPendingResponses
  415. changes:@[ change1, change2 ]];
  416. FSTRemoteEvent *event = [aggregator remoteEvent];
  417. XCTAssertEqual(event.targetChanges.count, 2);
  418. FSTUpdateMapping *mapping1 =
  419. [FSTUpdateMapping mappingWithAddedDocuments:@[] removedDocuments:@[]];
  420. XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
  421. XCTAssertEqual(event.targetChanges[@1].snapshotVersion, testutil::Version(3));
  422. XCTAssertEqual(event.targetChanges[@1].currentStatusUpdate, FSTCurrentStatusUpdateMarkCurrent);
  423. XCTAssertEqualObjects(event.targetChanges[@1].resumeToken, _resumeToken1);
  424. XCTAssertEqualObjects(event.targetChanges[@2].mapping, mapping1);
  425. XCTAssertEqual(event.targetChanges[@2].snapshotVersion, testutil::Version(3));
  426. XCTAssertEqual(event.targetChanges[@2].currentStatusUpdate, FSTCurrentStatusUpdateMarkCurrent);
  427. XCTAssertEqualObjects(event.targetChanges[@2].resumeToken, resumeToken2);
  428. }
  429. - (void)testLastResumeTokenWins {
  430. NSData *resumeToken2 = [@"resume2" dataUsingEncoding:NSUTF8StringEncoding];
  431. NSData *resumeToken3 = [@"resume3" dataUsingEncoding:NSUTF8StringEncoding];
  432. FSTWatchChange *change1 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
  433. targetIDs:@[ @1 ]
  434. resumeToken:_resumeToken1];
  435. FSTWatchChange *change2 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateReset
  436. targetIDs:@[ @1 ]
  437. resumeToken:resumeToken2];
  438. FSTWatchChange *change3 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateReset
  439. targetIDs:@[ @2 ]
  440. resumeToken:resumeToken3];
  441. FSTWatchChangeAggregator *aggregator =
  442. [self aggregatorWithTargets:@[ @1, @2 ]
  443. outstanding:_noPendingResponses
  444. changes:@[ change1, change2, change3 ]];
  445. FSTRemoteEvent *event = [aggregator remoteEvent];
  446. XCTAssertEqual(event.targetChanges.count, 2);
  447. FSTResetMapping *mapping1 = [FSTResetMapping mappingWithDocuments:@[]];
  448. XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
  449. XCTAssertEqual(event.targetChanges[@1].snapshotVersion, testutil::Version(3));
  450. XCTAssertEqual(event.targetChanges[@1].currentStatusUpdate, FSTCurrentStatusUpdateMarkCurrent);
  451. XCTAssertEqualObjects(event.targetChanges[@1].resumeToken, resumeToken2);
  452. XCTAssertEqualObjects(event.targetChanges[@2].mapping, mapping1);
  453. XCTAssertEqual(event.targetChanges[@2].snapshotVersion, testutil::Version(3));
  454. XCTAssertEqual(event.targetChanges[@2].currentStatusUpdate, FSTCurrentStatusUpdateNone);
  455. XCTAssertEqualObjects(event.targetChanges[@2].resumeToken, resumeToken3);
  456. }
  457. - (void)testSynthesizeDeletes {
  458. FSTWatchChange *shouldSynthesize =
  459. [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent targetIDs:@[ @1 ]];
  460. FSTWatchChangeAggregator *aggregator = [self aggregatorWithTargets:@[ @1 ]
  461. outstanding:_noPendingResponses
  462. changes:@[ shouldSynthesize ]];
  463. FSTRemoteEvent *event = [aggregator remoteEvent];
  464. DocumentKey synthesized = DocumentKey::FromPathString("docs/2");
  465. XCTAssertEqual(event.documentUpdates.find(synthesized), event.documentUpdates.end());
  466. FSTTargetChange *limboTargetChange = event.targetChanges[@1];
  467. [event synthesizeDeleteForLimboTargetChange:limboTargetChange key:synthesized];
  468. FSTDeletedDocument *expected =
  469. [FSTDeletedDocument documentWithKey:synthesized version:event.snapshotVersion];
  470. XCTAssertEqualObjects(expected, event.documentUpdates.at(synthesized));
  471. XCTAssertTrue(event.limboDocumentChanges.contains(synthesized));
  472. }
  473. - (void)testDoesntSynthesizeDeletesForWrongState {
  474. FSTWatchChange *wrongState =
  475. [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateNoChange targetIDs:@[ @2 ]];
  476. FSTWatchChangeAggregator *aggregator =
  477. [self aggregatorWithTargets:@[ @2 ] outstanding:_noPendingResponses changes:@[ wrongState ]];
  478. FSTRemoteEvent *event = [aggregator remoteEvent];
  479. DocumentKey notSynthesized = DocumentKey::FromPathString("docs/no1");
  480. [event synthesizeDeleteForLimboTargetChange:event.targetChanges[@2] key:notSynthesized];
  481. XCTAssertEqual(event.documentUpdates.find(notSynthesized), event.documentUpdates.end());
  482. XCTAssertFalse(event.limboDocumentChanges.contains(notSynthesized));
  483. }
  484. - (void)testDoesntSynthesizeDeletesForExistingDoc {
  485. FSTWatchChange *hasDocument =
  486. [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent targetIDs:@[ @3 ]];
  487. FSTDocument *doc = FSTTestDoc("docs/1", 1, @{ @"value" : @1 }, NO);
  488. FSTWatchChange *docChange = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @3 ]
  489. removedTargetIDs:@[]
  490. documentKey:doc.key
  491. document:doc];
  492. FSTWatchChangeAggregator *aggregator = [self aggregatorWithTargets:@[ @3 ]
  493. outstanding:_noPendingResponses
  494. changes:@[ hasDocument, docChange ]];
  495. FSTRemoteEvent *event = [aggregator remoteEvent];
  496. [event synthesizeDeleteForLimboTargetChange:event.targetChanges[@3] key:doc.key];
  497. FSTMaybeDocument *docData = event.documentUpdates.at(doc.key);
  498. XCTAssertFalse([docData isKindOfClass:[FSTDeletedDocument class]]);
  499. XCTAssertFalse(event.limboDocumentChanges.contains(doc.key));
  500. }
  501. - (void)testFilterUpdates {
  502. FSTDocument *newDoc = FSTTestDoc("docs/new", 1, @{@"key" : @"value"}, NO);
  503. FSTDocument *existingDoc = FSTTestDoc("docs/existing", 1, @{@"some" : @"data"}, NO);
  504. FSTWatchChange *newDocChange = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
  505. removedTargetIDs:@[]
  506. documentKey:newDoc.key
  507. document:newDoc];
  508. FSTWatchChange *existingDocChange =
  509. [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
  510. removedTargetIDs:@[]
  511. documentKey:existingDoc.key
  512. document:existingDoc];
  513. FSTWatchChangeAggregator *aggregator =
  514. [self aggregatorWithTargets:@[ @1 ]
  515. outstanding:_noPendingResponses
  516. changes:@[ newDocChange, existingDocChange ]];
  517. FSTRemoteEvent *event = [aggregator remoteEvent];
  518. DocumentKeySet existingKeys = DocumentKeySet{existingDoc.key};
  519. FSTTargetChange *updateChange = event.targetChanges[@1];
  520. XCTAssertTrue([updateChange.mapping isKindOfClass:[FSTUpdateMapping class]]);
  521. FSTUpdateMapping *update = (FSTUpdateMapping *)updateChange.mapping;
  522. FSTDocumentKey *existingDocKey = existingDoc.key;
  523. FSTDocumentKey *newDocKey = newDoc.key;
  524. XCTAssertTrue(update.addedDocuments.contains(existingDocKey));
  525. [update filterUpdatesUsingExistingKeys:existingKeys];
  526. // Now it's been filtered, since it already existed.
  527. XCTAssertFalse(update.addedDocuments.contains(existingDocKey));
  528. XCTAssertTrue(update.addedDocuments.contains(newDocKey));
  529. }
  530. - (void)testDoesntFilterResets {
  531. FSTDocument *existingDoc = FSTTestDoc("docs/existing", 1, @{@"some" : @"data"}, NO);
  532. const DocumentKey &existingDocKey = existingDoc.key;
  533. FSTWatchTargetChange *resetTargetChange =
  534. [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateReset
  535. targetIDs:@[ @2 ]
  536. resumeToken:_resumeToken1];
  537. FSTWatchChange *existingDocChange =
  538. [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @2 ]
  539. removedTargetIDs:@[]
  540. documentKey:existingDocKey
  541. document:existingDoc];
  542. FSTWatchChangeAggregator *aggregator =
  543. [self aggregatorWithTargets:@[ @2 ]
  544. outstanding:_noPendingResponses
  545. changes:@[ resetTargetChange, existingDocChange ]];
  546. FSTRemoteEvent *event = [aggregator remoteEvent];
  547. DocumentKeySet existingKeys = DocumentKeySet{existingDocKey};
  548. FSTTargetChange *resetChange = event.targetChanges[@2];
  549. XCTAssertTrue([resetChange.mapping isKindOfClass:[FSTResetMapping class]]);
  550. FSTResetMapping *resetMapping = (FSTResetMapping *)resetChange.mapping;
  551. XCTAssertTrue(resetMapping.documents.contains(existingDocKey));
  552. [resetMapping filterUpdatesUsingExistingKeys:existingKeys];
  553. // Document is still there, even though it already exists. Reset mappings don't get filtered.
  554. XCTAssertTrue(resetMapping.documents.contains(existingDocKey));
  555. }
  556. - (void)testTracksLimboDocuments {
  557. // Add 3 docs: 1 is limbo and non-limbo, 2 is limbo-only, 3 is non-limbo
  558. FSTDocument *doc1 = FSTTestDoc("docs/1", 1, @{@"key" : @"value"}, NO);
  559. FSTDocument *doc2 = FSTTestDoc("docs/2", 1, @{@"key" : @"value"}, NO);
  560. FSTDocument *doc3 = FSTTestDoc("docs/3", 1, @{@"key" : @"value"}, NO);
  561. // Target 2 is a limbo target
  562. FSTWatchChange *docChange1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1, @2 ]
  563. removedTargetIDs:@[]
  564. documentKey:doc1.key
  565. document:doc1];
  566. FSTWatchChange *docChange2 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @2 ]
  567. removedTargetIDs:@[]
  568. documentKey:doc2.key
  569. document:doc2];
  570. FSTWatchChange *docChange3 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
  571. removedTargetIDs:@[]
  572. documentKey:doc3.key
  573. document:doc3];
  574. FSTWatchChange *targetsChange =
  575. [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent targetIDs:@[ @1, @2 ]];
  576. NSMutableDictionary<NSNumber *, FSTQueryData *> *listens = [NSMutableDictionary dictionary];
  577. listens[@1] = [FSTQueryData alloc];
  578. listens[@2] = [[FSTQueryData alloc] initWithQuery:[FSTQuery alloc]
  579. targetID:2
  580. listenSequenceNumber:1000
  581. purpose:FSTQueryPurposeLimboResolution];
  582. FSTWatchChangeAggregator *aggregator =
  583. [[FSTWatchChangeAggregator alloc] initWithSnapshotVersion:testutil::Version(3)
  584. listenTargets:listens
  585. pendingTargetResponses:@{}];
  586. [aggregator addWatchChanges:@[ docChange1, docChange2, docChange3, targetsChange ]];
  587. FSTRemoteEvent *event = [aggregator remoteEvent];
  588. DocumentKeySet limboDocChanges = event.limboDocumentChanges;
  589. // Doc1 is in both limbo and non-limbo targets, therefore not tracked as limbo
  590. XCTAssertFalse(limboDocChanges.contains(doc1.key));
  591. // Doc2 is only in the limbo target, so is tracked as a limbo document
  592. XCTAssertTrue(limboDocChanges.contains(doc2.key));
  593. // Doc3 is only in the non-limbo target, therefore not tracked as limbo
  594. XCTAssertFalse(limboDocChanges.contains(doc3.key));
  595. }
  596. @end
  597. NS_ASSUME_NONNULL_END