FSTRemoteEventTests.mm 34 KB

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