diff --git a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm index 12b4f9ecaf0..d0d8f45703d 100644 --- a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm +++ b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm @@ -28,12 +28,14 @@ #import "Firestore/Example/Tests/Util/FSTHelpers.h" #include "Firestore/core/src/firebase/firestore/model/types.h" +#include "Firestore/core/src/firebase/firestore/remote/remote_event.h" #include "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h" #include "absl/memory/memory.h" using firebase::firestore::core::DocumentViewChangeType; using firebase::firestore::model::DocumentKeySet; using firebase::firestore::model::OnlineState; +using firebase::firestore::remote::TargetChange; using firebase::firestore::util::ExecutorLibdispatch; NS_ASSUME_NONNULL_BEGIN @@ -84,8 +86,8 @@ - (void)testRaisesCollectionEvents { FSTQueryListener *otherListener = [self listenToQuery:query accumulatingSnapshots:otherAccum]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; - FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1, doc2 ], nil); - FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[ doc2prime ], nil); + FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1, doc2 ], absl::nullopt); + FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[ doc2prime ], absl::nullopt); FSTDocumentViewChange *change1 = [FSTDocumentViewChange changeWithDocument:doc1 type:DocumentViewChangeType::kAdded]; @@ -142,7 +144,7 @@ - (void)testRaisesEventForEmptyCollectionAfterSync { accumulatingSnapshots:accum]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; - FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[], nil); + FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[], absl::nullopt); FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[], FSTTestTargetChangeMarkCurrent()); [listener queryDidChangeViewSnapshot:snap1]; @@ -167,8 +169,8 @@ - (void)testMutingAsyncListenerPreventsAllSubsequentEvents { }]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; - FSTViewSnapshot *viewSnapshot1 = FSTTestApplyChanges(view, @[ doc1 ], nil); - FSTViewSnapshot *viewSnapshot2 = FSTTestApplyChanges(view, @[ doc2 ], nil); + FSTViewSnapshot *viewSnapshot1 = FSTTestApplyChanges(view, @[ doc1 ], absl::nullopt); + FSTViewSnapshot *viewSnapshot2 = FSTTestApplyChanges(view, @[ doc2 ], absl::nullopt); FSTViewSnapshotHandler handler = listener.asyncSnapshotHandler; handler(viewSnapshot1, nil); @@ -204,11 +206,11 @@ - (void)testDoesNotRaiseEventsForMetadataChangesUnlessSpecified { accumulatingSnapshots:fullAccum]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; - FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1 ], nil); + FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1 ], absl::nullopt); - FSTTargetChange *ackTarget = FSTTestTargetChangeAckDocuments({doc1.key}); + TargetChange ackTarget = FSTTestTargetChangeAckDocuments({doc1.key}); FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[], ackTarget); - FSTViewSnapshot *snap3 = FSTTestApplyChanges(view, @[ doc2 ], nil); + FSTViewSnapshot *snap3 = FSTTestApplyChanges(view, @[ doc2 ], absl::nullopt); [filteredListener queryDidChangeViewSnapshot:snap1]; // local event [filteredListener queryDidChangeViewSnapshot:snap2]; // no event @@ -248,9 +250,9 @@ - (void)testRaisesDocumentMetadataEventsOnlyWhenSpecified { accumulatingSnapshots:fullAccum]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; - FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1, doc2 ], nil); - FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[ doc1Prime ], nil); - FSTViewSnapshot *snap3 = FSTTestApplyChanges(view, @[ doc3 ], nil); + FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1, doc2 ], absl::nullopt); + FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[ doc1Prime ], absl::nullopt); + FSTViewSnapshot *snap3 = FSTTestApplyChanges(view, @[ doc3 ], absl::nullopt); FSTDocumentViewChange *change1 = [FSTDocumentViewChange changeWithDocument:doc1 type:DocumentViewChangeType::kAdded]; @@ -303,10 +305,10 @@ - (void)testRaisesQueryMetadataEventsOnlyWhenHasPendingWritesOnTheQueryChanges { accumulatingSnapshots:fullAccum]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; - FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1, doc2 ], nil); - FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[ doc1Prime ], nil); - FSTViewSnapshot *snap3 = FSTTestApplyChanges(view, @[ doc3 ], nil); - FSTViewSnapshot *snap4 = FSTTestApplyChanges(view, @[ doc2Prime ], nil); + FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1, doc2 ], absl::nullopt); + FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[ doc1Prime ], absl::nullopt); + FSTViewSnapshot *snap3 = FSTTestApplyChanges(view, @[ doc3 ], absl::nullopt); + FSTViewSnapshot *snap4 = FSTTestApplyChanges(view, @[ doc2Prime ], absl::nullopt); [fullListener queryDidChangeViewSnapshot:snap1]; [fullListener queryDidChangeViewSnapshot:snap2]; // Emits no events. @@ -343,8 +345,8 @@ - (void)testMetadataOnlyDocumentChangesAreFilteredOutWhenIncludeDocumentMetadata accumulatingSnapshots:filteredAccum]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; - FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1, doc2 ], nil); - FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[ doc1Prime, doc3 ], nil); + FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1, doc2 ], absl::nullopt); + FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[ doc1Prime, doc3 ], absl::nullopt); FSTDocumentViewChange *change3 = [FSTDocumentViewChange changeWithDocument:doc3 type:DocumentViewChangeType::kAdded]; @@ -378,8 +380,8 @@ - (void)testWillWaitForSyncIfOnline { accumulatingSnapshots:events]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; - FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1 ], nil); - FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[ doc2 ], nil); + FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1 ], absl::nullopt); + FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[ doc2 ], absl::nullopt); FSTViewSnapshot *snap3 = FSTTestApplyChanges(view, @[], FSTTestTargetChangeAckDocuments({doc1.key, doc2.key})); @@ -420,8 +422,8 @@ - (void)testWillRaiseInitialEventWhenGoingOffline { accumulatingSnapshots:events]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; - FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1 ], nil); - FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[ doc2 ], nil); + FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1 ], absl::nullopt); + FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[ doc2 ], absl::nullopt); [listener applyChangedOnlineState:OnlineState::Online]; // no event [listener queryDidChangeViewSnapshot:snap1]; // no event @@ -463,7 +465,7 @@ - (void)testWillRaiseInitialEventWhenGoingOfflineAndThereAreNoDocs { accumulatingSnapshots:events]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; - FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[], nil); + FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[], absl::nullopt); [listener applyChangedOnlineState:OnlineState::Online]; // no event [listener queryDidChangeViewSnapshot:snap1]; // no event @@ -490,7 +492,7 @@ - (void)testWillRaiseInitialEventWhenStartingOfflineAndThereAreNoDocs { accumulatingSnapshots:events]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; - FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[], nil); + FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[], absl::nullopt); [listener applyChangedOnlineState:OnlineState::Offline]; // no event [listener queryDidChangeViewSnapshot:snap1]; // event diff --git a/Firestore/Example/Tests/Core/FSTViewTests.mm b/Firestore/Example/Tests/Core/FSTViewTests.mm index 9173b3ce579..5e6c432d40b 100644 --- a/Firestore/Example/Tests/Core/FSTViewTests.mm +++ b/Firestore/Example/Tests/Core/FSTViewTests.mm @@ -30,6 +30,7 @@ #include "Firestore/core/src/firebase/firestore/model/resource_path.h" #include "Firestore/core/test/firebase/firestore/testutil/testutil.h" +#include "absl/types/optional.h" namespace testutil = firebase::firestore::testutil; using firebase::firestore::core::DocumentViewChangeType; @@ -89,7 +90,7 @@ - (void)testRemovesDocuments { FSTTestDoc("rooms/eros/messages/3", 0, @{@"text" : @"msg3"}, FSTDocumentStateSynced); // initial state - FSTTestApplyChanges(view, @[ doc1, doc2 ], nil); + FSTTestApplyChanges(view, @[ doc1, doc2 ], absl::nullopt); // delete doc2, add doc3 FSTViewSnapshot *snapshot = @@ -120,10 +121,10 @@ - (void)testReturnsNilIfThereAreNoChanges { FSTTestDoc("rooms/eros/messages/2", 0, @{@"text" : @"msg2"}, FSTDocumentStateSynced); // initial state - FSTTestApplyChanges(view, @[ doc1, doc2 ], nil); + FSTTestApplyChanges(view, @[ doc1, doc2 ], absl::nullopt); // reapply same docs, no changes - FSTViewSnapshot *snapshot = FSTTestApplyChanges(view, @[ doc1, doc2 ], nil); + FSTViewSnapshot *snapshot = FSTTestApplyChanges(view, @[ doc1, doc2 ], absl::nullopt); XCTAssertNil(snapshot); } @@ -131,7 +132,7 @@ - (void)testDoesNotReturnNilForFirstChanges { FSTQuery *query = [self queryForMessages]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; - FSTViewSnapshot *snapshot = FSTTestApplyChanges(view, @[], nil); + FSTViewSnapshot *snapshot = FSTTestApplyChanges(view, @[], absl::nullopt); XCTAssertNotNil(snapshot); } @@ -155,7 +156,8 @@ - (void)testFiltersDocumentsBasedOnQueryWithFilter { FSTDocument *doc5 = FSTTestDoc("rooms/eros/messages/5", 0, @{@"sort" : @1}, FSTDocumentStateSynced); - FSTViewSnapshot *snapshot = FSTTestApplyChanges(view, @[ doc1, doc2, doc3, doc4, doc5 ], nil); + FSTViewSnapshot *snapshot = + FSTTestApplyChanges(view, @[ doc1, doc2, doc3, doc4, doc5 ], absl::nullopt); XCTAssertEqual(snapshot.query, query); @@ -189,7 +191,7 @@ - (void)testUpdatesDocumentsBasedOnQueryWithFilter { FSTTestDoc("rooms/eros/messages/3", 0, @{@"sort" : @2}, FSTDocumentStateSynced); FSTDocument *doc4 = FSTTestDoc("rooms/eros/messages/4", 0, @{}, FSTDocumentStateSynced); - FSTViewSnapshot *snapshot = FSTTestApplyChanges(view, @[ doc1, doc2, doc3, doc4 ], nil); + FSTViewSnapshot *snapshot = FSTTestApplyChanges(view, @[ doc1, doc2, doc3, doc4 ], absl::nullopt); XCTAssertEqual(snapshot.query, query); @@ -202,7 +204,7 @@ - (void)testUpdatesDocumentsBasedOnQueryWithFilter { FSTDocument *newDoc4 = FSTTestDoc("rooms/eros/messages/4", 1, @{@"sort" : @0}, FSTDocumentStateSynced); - snapshot = FSTTestApplyChanges(view, @[ newDoc2, newDoc3, newDoc4 ], nil); + snapshot = FSTTestApplyChanges(view, @[ newDoc2, newDoc3, newDoc4 ], absl::nullopt); XCTAssertEqual(snapshot.query, query); @@ -232,7 +234,7 @@ - (void)testRemovesDocumentsForQueryWithLimit { FSTTestDoc("rooms/eros/messages/3", 0, @{@"text" : @"msg3"}, FSTDocumentStateSynced); // initial state - FSTTestApplyChanges(view, @[ doc1, doc3 ], nil); + FSTTestApplyChanges(view, @[ doc1, doc3 ], absl::nullopt); // add doc2, which should push out doc3 FSTViewSnapshot *snapshot = FSTTestApplyChanges( @@ -269,7 +271,7 @@ - (void)testDoesntReportChangesForDocumentBeyondLimitOfQuery { FSTTestDoc("rooms/eros/messages/4", 0, @{@"num" : @4}, FSTDocumentStateSynced); // initial state - FSTTestApplyChanges(view, @[ doc1, doc2 ], nil); + FSTTestApplyChanges(view, @[ doc1, doc2 ], absl::nullopt); // change doc2 to 5, and add doc3 and doc4. // doc2 will be modified + removed = removed diff --git a/Firestore/Example/Tests/Remote/FSTRemoteEventTests.mm b/Firestore/Example/Tests/Remote/FSTRemoteEventTests.mm index b728ee94777..22b750a7a54 100644 --- a/Firestore/Example/Tests/Remote/FSTRemoteEventTests.mm +++ b/Firestore/Example/Tests/Remote/FSTRemoteEventTests.mm @@ -46,6 +46,7 @@ using firebase::firestore::remote::DocumentWatchChange; using firebase::firestore::remote::ExistenceFilter; using firebase::firestore::remote::ExistenceFilterWatchChange; +using firebase::firestore::remote::TargetChange; using firebase::firestore::remote::WatchChange; using firebase::firestore::remote::WatchChangeAggregator; using firebase::firestore::remote::WatchTargetChange; @@ -252,31 +253,29 @@ - (void)testWillAccumulateDocumentAddedAndRemovedEvents { // 'change1' and 'change2' affect six different targets XCTAssertEqual(event.targetChanges.size(), 6); - FSTTargetChange *targetChange1 = - FSTTestTargetChange(DocumentKeySet{newDoc.key}, DocumentKeySet{existingDoc.key}, - DocumentKeySet{}, _resumeToken1, NO); - XCTAssertEqualObjects(event.targetChanges.at(1), targetChange1); + TargetChange targetChange1{_resumeToken1, false, DocumentKeySet{newDoc.key}, + DocumentKeySet{existingDoc.key}, DocumentKeySet{}}; + XCTAssertTrue(event.targetChanges.at(1) == targetChange1); - FSTTargetChange *targetChange2 = FSTTestTargetChange( - DocumentKeySet{}, DocumentKeySet{existingDoc.key}, DocumentKeySet{}, _resumeToken1, NO); - XCTAssertEqualObjects(event.targetChanges.at(2), targetChange2); + TargetChange targetChange2{_resumeToken1, false, DocumentKeySet{}, + DocumentKeySet{existingDoc.key}, DocumentKeySet{}}; + XCTAssertTrue(event.targetChanges.at(2) == targetChange2); - FSTTargetChange *targetChange3 = FSTTestTargetChange( - DocumentKeySet{}, DocumentKeySet{existingDoc.key}, DocumentKeySet{}, _resumeToken1, NO); - XCTAssertEqualObjects(event.targetChanges.at(3), targetChange3); + TargetChange targetChange3{_resumeToken1, false, DocumentKeySet{}, + DocumentKeySet{existingDoc.key}, DocumentKeySet{}}; + XCTAssertTrue(event.targetChanges.at(3) == targetChange3); - FSTTargetChange *targetChange4 = - FSTTestTargetChange(DocumentKeySet{newDoc.key}, DocumentKeySet{}, - DocumentKeySet{existingDoc.key}, _resumeToken1, NO); - XCTAssertEqualObjects(event.targetChanges.at(4), targetChange4); + TargetChange targetChange4{_resumeToken1, false, DocumentKeySet{newDoc.key}, DocumentKeySet{}, + DocumentKeySet{existingDoc.key}}; + XCTAssertTrue(event.targetChanges.at(4) == targetChange4); - FSTTargetChange *targetChange5 = FSTTestTargetChange( - DocumentKeySet{}, DocumentKeySet{}, DocumentKeySet{existingDoc.key}, _resumeToken1, NO); - XCTAssertEqualObjects(event.targetChanges.at(5), targetChange5); + TargetChange targetChange5{_resumeToken1, false, DocumentKeySet{}, DocumentKeySet{}, + DocumentKeySet{existingDoc.key}}; + XCTAssertTrue(event.targetChanges.at(5) == targetChange5); - FSTTargetChange *targetChange6 = FSTTestTargetChange( - DocumentKeySet{}, DocumentKeySet{}, DocumentKeySet{existingDoc.key}, _resumeToken1, NO); - XCTAssertEqualObjects(event.targetChanges.at(6), targetChange6); + TargetChange targetChange6{_resumeToken1, false, DocumentKeySet{}, DocumentKeySet{}, + DocumentKeySet{existingDoc.key}}; + XCTAssertTrue(event.targetChanges.at(6) == targetChange6); } - (void)testWillIgnoreEventsForPendingTargets { @@ -368,9 +367,9 @@ - (void)testWillKeepResetMappingEvenWithUpdates { XCTAssertEqual(event.targetChanges.size(), 1); // Only doc3 is part of the new mapping - FSTTargetChange *expectedChange = FSTTestTargetChange( - DocumentKeySet{doc3.key}, DocumentKeySet{}, DocumentKeySet{doc1.key}, _resumeToken1, NO); - XCTAssertEqualObjects(event.targetChanges.at(1), expectedChange); + TargetChange expectedChange{_resumeToken1, false, DocumentKeySet{doc3.key}, DocumentKeySet{}, + DocumentKeySet{doc1.key}}; + XCTAssertTrue(event.targetChanges.at(1) == expectedChange); } - (void)testWillHandleSingleReset { @@ -392,9 +391,9 @@ - (void)testWillHandleSingleReset { XCTAssertEqual(event.targetChanges.size(), 1); // Reset mapping is empty - FSTTargetChange *expectedChange = - FSTTestTargetChange(DocumentKeySet{}, DocumentKeySet{}, DocumentKeySet{}, [NSData data], NO); - XCTAssertEqualObjects(event.targetChanges.at(1), expectedChange); + TargetChange expectedChange{ + [NSData data], false, DocumentKeySet{}, DocumentKeySet{}, DocumentKeySet{}}; + XCTAssertTrue(event.targetChanges.at(1) == expectedChange); } - (void)testWillHandleTargetAddAndRemovalInSameBatch { @@ -418,13 +417,13 @@ - (void)testWillHandleTargetAddAndRemovalInSameBatch { XCTAssertEqual(event.targetChanges.size(), 2); - FSTTargetChange *targetChange1 = FSTTestTargetChange( - DocumentKeySet{}, DocumentKeySet{}, DocumentKeySet{doc1b.key}, _resumeToken1, NO); - XCTAssertEqualObjects(event.targetChanges.at(1), targetChange1); + TargetChange targetChange1{_resumeToken1, false, DocumentKeySet{}, DocumentKeySet{}, + DocumentKeySet{doc1b.key}}; + XCTAssertTrue(event.targetChanges.at(1) == targetChange1); - FSTTargetChange *targetChange2 = FSTTestTargetChange(DocumentKeySet{}, DocumentKeySet{doc1b.key}, - DocumentKeySet{}, _resumeToken1, NO); - XCTAssertEqualObjects(event.targetChanges.at(2), targetChange2); + TargetChange targetChange2{_resumeToken1, false, DocumentKeySet{}, DocumentKeySet{doc1b.key}, + DocumentKeySet{}}; + XCTAssertTrue(event.targetChanges.at(2) == targetChange2); } - (void)testTargetCurrentChangeWillMarkTheTargetCurrent { @@ -442,9 +441,9 @@ - (void)testTargetCurrentChangeWillMarkTheTargetCurrent { XCTAssertEqual(event.documentUpdates.size(), 0); XCTAssertEqual(event.targetChanges.size(), 1); - FSTTargetChange *targetChange = - FSTTestTargetChange(DocumentKeySet{}, DocumentKeySet{}, DocumentKeySet{}, _resumeToken1, YES); - XCTAssertEqualObjects(event.targetChanges.at(1), targetChange); + TargetChange targetChange1{_resumeToken1, true, DocumentKeySet{}, DocumentKeySet{}, + DocumentKeySet{}}; + XCTAssertTrue(event.targetChanges.at(1) == targetChange1); } - (void)testTargetAddedChangeWillResetPreviousState { @@ -480,15 +479,15 @@ - (void)testTargetAddedChangeWillResetPreviousState { // doc1 was before the remove, so it does not show up in the mapping. // Current was before the remove. - FSTTargetChange *targetChange1 = FSTTestTargetChange(DocumentKeySet{}, DocumentKeySet{doc2.key}, - DocumentKeySet{}, _resumeToken1, NO); - XCTAssertEqualObjects(event.targetChanges.at(1), targetChange1); + TargetChange targetChange1{_resumeToken1, false, DocumentKeySet{}, DocumentKeySet{doc2.key}, + DocumentKeySet{}}; + XCTAssertTrue(event.targetChanges.at(1) == targetChange1); // Doc1 was before the remove // Current was before the remove - FSTTargetChange *targetChange3 = FSTTestTargetChange( - DocumentKeySet{doc1.key}, DocumentKeySet{}, DocumentKeySet{doc2.key}, _resumeToken1, YES); - XCTAssertEqualObjects(event.targetChanges.at(3), targetChange3); + TargetChange targetChange3{_resumeToken1, true, DocumentKeySet{doc1.key}, DocumentKeySet{}, + DocumentKeySet{doc2.key}}; + XCTAssertTrue(event.targetChanges.at(3) == targetChange3); } - (void)testNoChangeWillStillMarkTheAffectedTargets { @@ -508,9 +507,9 @@ - (void)testNoChangeWillStillMarkTheAffectedTargets { XCTAssertEqual(event.documentUpdates.size(), 0); XCTAssertEqual(event.targetChanges.size(), 1); - FSTTargetChange *targetChange = - FSTTestTargetChange(DocumentKeySet{}, DocumentKeySet{}, DocumentKeySet{}, _resumeToken1, NO); - XCTAssertEqualObjects(event.targetChanges.at(1), targetChange); + TargetChange targetChange{_resumeToken1, false, DocumentKeySet{}, DocumentKeySet{}, + DocumentKeySet{}}; + XCTAssertTrue(event.targetChanges.at(1) == targetChange); } - (void)testExistenceFilterMismatchClearsTarget { @@ -537,13 +536,13 @@ - (void)testExistenceFilterMismatchClearsTarget { XCTAssertEqual(event.targetChanges.size(), 2); - FSTTargetChange *targetChange1 = FSTTestTargetChange( - DocumentKeySet{}, DocumentKeySet{doc1.key, doc2.key}, DocumentKeySet{}, _resumeToken1, YES); - XCTAssertEqualObjects(event.targetChanges.at(1), targetChange1); + TargetChange targetChange1{_resumeToken1, true, DocumentKeySet{}, + DocumentKeySet{doc1.key, doc2.key}, DocumentKeySet{}}; + XCTAssertTrue(event.targetChanges.at(1) == targetChange1); - FSTTargetChange *targetChange2 = - FSTTestTargetChange(DocumentKeySet{}, DocumentKeySet{}, DocumentKeySet{}, _resumeToken1, NO); - XCTAssertEqualObjects(event.targetChanges.at(2), targetChange2); + TargetChange targetChange2{_resumeToken1, false, DocumentKeySet{}, DocumentKeySet{}, + DocumentKeySet{}}; + XCTAssertTrue(event.targetChanges.at(2) == targetChange2); // The existence filter mismatch will remove the document from target 1, // but not synthesize a document delete. @@ -552,9 +551,9 @@ - (void)testExistenceFilterMismatchClearsTarget { event = aggregator.CreateRemoteEvent(testutil::Version(4)); - FSTTargetChange *targetChange3 = FSTTestTargetChange( - DocumentKeySet{}, DocumentKeySet{}, DocumentKeySet{doc1.key, doc2.key}, [NSData data], NO); - XCTAssertEqualObjects(event.targetChanges.at(1), targetChange3); + TargetChange targetChange3{ + [NSData data], false, DocumentKeySet{}, DocumentKeySet{}, DocumentKeySet{doc1.key, doc2.key}}; + XCTAssertTrue(event.targetChanges.at(1) == targetChange3); XCTAssertEqual(event.targetChanges.size(), 1); XCTAssertEqual(event.targetMismatches.size(), 1); @@ -590,9 +589,9 @@ - (void)testExistenceFilterMismatchRemovesCurrentChanges { XCTAssertEqual(event.targetChanges.size(), 1); - FSTTargetChange *targetChange1 = - FSTTestTargetChange(DocumentKeySet{}, DocumentKeySet{}, DocumentKeySet{}, [NSData data], NO); - XCTAssertEqualObjects(event.targetChanges.at(1), targetChange1); + TargetChange targetChange1{ + [NSData data], false, DocumentKeySet{}, DocumentKeySet{}, DocumentKeySet{}}; + XCTAssertTrue(event.targetChanges.at(1) == targetChange1); } - (void)testDocumentUpdate { @@ -647,10 +646,9 @@ - (void)testDocumentUpdate { // Target is unchanged XCTAssertEqual(event.targetChanges.size(), 1); - FSTTargetChange *targetChange = - FSTTestTargetChange(DocumentKeySet{doc3.key}, DocumentKeySet{updatedDoc2.key}, - DocumentKeySet{deletedDoc1.key}, _resumeToken1, NO); - XCTAssertEqualObjects(event.targetChanges.at(1), targetChange); + TargetChange targetChange1{_resumeToken1, false, DocumentKeySet{doc3.key}, + DocumentKeySet{updatedDoc2.key}, DocumentKeySet{deletedDoc1.key}}; + XCTAssertTrue(event.targetChanges.at(1) == targetChange1); } - (void)testResumeTokensHandledPerTarget { @@ -671,13 +669,13 @@ - (void)testResumeTokensHandledPerTarget { FSTRemoteEvent *event = aggregator.CreateRemoteEvent(testutil::Version(3)); XCTAssertEqual(event.targetChanges.size(), 2); - FSTTargetChange *targetChange1 = - FSTTestTargetChange(DocumentKeySet{}, DocumentKeySet{}, DocumentKeySet{}, _resumeToken1, YES); - XCTAssertEqualObjects(event.targetChanges.at(1), targetChange1); + TargetChange targetChange1{_resumeToken1, true, DocumentKeySet{}, DocumentKeySet{}, + DocumentKeySet{}}; + XCTAssertTrue(event.targetChanges.at(1) == targetChange1); - FSTTargetChange *targetChange2 = - FSTTestTargetChange(DocumentKeySet{}, DocumentKeySet{}, DocumentKeySet{}, resumeToken2, YES); - XCTAssertEqualObjects(event.targetChanges.at(2), targetChange2); + TargetChange targetChange2{resumeToken2, true, DocumentKeySet{}, DocumentKeySet{}, + DocumentKeySet{}}; + XCTAssertTrue(event.targetChanges.at(2) == targetChange2); } - (void)testLastResumeTokenWins { @@ -702,13 +700,13 @@ - (void)testLastResumeTokenWins { FSTRemoteEvent *event = aggregator.CreateRemoteEvent(testutil::Version(3)); XCTAssertEqual(event.targetChanges.size(), 2); - FSTTargetChange *targetChange1 = - FSTTestTargetChange(DocumentKeySet{}, DocumentKeySet{}, DocumentKeySet{}, resumeToken2, YES); - XCTAssertEqualObjects(event.targetChanges.at(1), targetChange1); + TargetChange targetChange1{resumeToken2, true, DocumentKeySet{}, DocumentKeySet{}, + DocumentKeySet{}}; + XCTAssertTrue(event.targetChanges.at(1) == targetChange1); - FSTTargetChange *targetChange2 = - FSTTestTargetChange(DocumentKeySet{}, DocumentKeySet{}, DocumentKeySet{}, resumeToken3, NO); - XCTAssertEqualObjects(event.targetChanges.at(2), targetChange2); + TargetChange targetChange2{resumeToken3, false, DocumentKeySet{}, DocumentKeySet{}, + DocumentKeySet{}}; + XCTAssertTrue(event.targetChanges.at(2) == targetChange2); } - (void)testSynthesizeDeletes { @@ -786,11 +784,10 @@ - (void)testSeparatesDocumentUpdates { std::move(deletedDocChange), std::move(missingDocChange))]; - FSTTargetChange *targetChange = - FSTTestTargetChange(DocumentKeySet{newDoc.key}, DocumentKeySet{existingDoc.key}, - DocumentKeySet{deletedDoc.key}, _resumeToken1, NO); + TargetChange targetChange2{_resumeToken1, false, DocumentKeySet{newDoc.key}, + DocumentKeySet{existingDoc.key}, DocumentKeySet{deletedDoc.key}}; - XCTAssertEqualObjects(event.targetChanges.at(1), targetChange); + XCTAssertTrue(event.targetChanges.at(1) == targetChange2); } - (void)testTracksLimboDocuments { diff --git a/Firestore/Example/Tests/Util/FSTHelpers.h b/Firestore/Example/Tests/Util/FSTHelpers.h index a276ef4900b..446c1fdc47c 100644 --- a/Firestore/Example/Tests/Util/FSTHelpers.h +++ b/Firestore/Example/Tests/Util/FSTHelpers.h @@ -27,7 +27,9 @@ #include "Firestore/core/src/firebase/firestore/model/field_value.h" #include "Firestore/core/src/firebase/firestore/model/resource_path.h" #include "Firestore/core/src/firebase/firestore/model/types.h" +#include "Firestore/core/src/firebase/firestore/remote/remote_event.h" #include "absl/strings/string_view.h" +#include "absl/types/optional.h" @class FIRGeoPoint; @class FSTDeleteMutation; @@ -43,7 +45,6 @@ @class FSTRemoteEvent; @class FSTSetMutation; @class FSTSortOrder; -@class FSTTargetChange; @class FIRTimestamp; @class FSTTransformMutation; @class FSTView; @@ -256,9 +257,10 @@ NSComparator FSTTestDocComparator(const absl::string_view fieldPath); FSTDocumentSet *FSTTestDocSet(NSComparator comp, NSArray *docs); /** Computes changes to the view with the docs and then applies them and returns the snapshot. */ -FSTViewSnapshot *_Nullable FSTTestApplyChanges(FSTView *view, - NSArray *docs, - FSTTargetChange *_Nullable targetChange); +FSTViewSnapshot *_Nullable FSTTestApplyChanges( + FSTView *view, + NSArray *docs, + const absl::optional &targetChange); /** Creates a set mutation for the document key at the given path. */ FSTSetMutation *FSTTestSetMutation(NSString *path, NSDictionary *values); @@ -305,17 +307,11 @@ FSTLocalViewChanges *FSTTestViewChanges(firebase::firestore::model::TargetId tar NSArray *removedKeys); /** Creates a test target change that acks all 'docs' and marks the target as CURRENT */ -FSTTargetChange *FSTTestTargetChangeAckDocuments(firebase::firestore::model::DocumentKeySet docs); +firebase::firestore::remote::TargetChange FSTTestTargetChangeAckDocuments( + firebase::firestore::model::DocumentKeySet docs); /** Creates a test target change that marks the target as CURRENT */ -FSTTargetChange *FSTTestTargetChangeMarkCurrent(); - -/** Creates a test target change. */ -FSTTargetChange *FSTTestTargetChange(firebase::firestore::model::DocumentKeySet added, - firebase::firestore::model::DocumentKeySet modified, - firebase::firestore::model::DocumentKeySet removed, - NSData *resumeToken, - BOOL current); +firebase::firestore::remote::TargetChange FSTTestTargetChangeMarkCurrent(); /** Creates a resume token to match the given snapshot version. */ NSData *_Nullable FSTTestResumeTokenFromSnapshotVersion(FSTTestSnapshotVersion watchSnapshot); diff --git a/Firestore/Example/Tests/Util/FSTHelpers.mm b/Firestore/Example/Tests/Util/FSTHelpers.mm index ffd04dd8c1b..04022769093 100644 --- a/Firestore/Example/Tests/Util/FSTHelpers.mm +++ b/Firestore/Example/Tests/Util/FSTHelpers.mm @@ -73,6 +73,7 @@ using firebase::firestore::model::TargetId; using firebase::firestore::model::TransformOperation; using firebase::firestore::remote::DocumentWatchChange; +using firebase::firestore::remote::TargetChange; using firebase::firestore::remote::WatchChangeAggregator; NS_ASSUME_NONNULL_BEGIN @@ -299,7 +300,7 @@ MaybeDocumentMap FSTTestDocUpdates(NSArray *docs) { FSTViewSnapshot *_Nullable FSTTestApplyChanges(FSTView *view, NSArray *docs, - FSTTargetChange *_Nullable targetChange) { + const absl::optional &targetChange) { return [view applyChangesToDocuments:[view computeChangesWithDocuments:FSTTestDocUpdates(docs)] targetChange:targetChange] .snapshot; @@ -385,32 +386,20 @@ - (nullable FSTQueryData *)queryDataForTarget:(TargetId)targetID { return aggregator.CreateRemoteEvent(doc.version); } -FSTTargetChange *FSTTestTargetChangeMarkCurrent() { - return [[FSTTargetChange alloc] initWithResumeToken:[NSData data] - current:YES - addedDocuments:DocumentKeySet {} - modifiedDocuments:DocumentKeySet {} - removedDocuments:DocumentKeySet{}]; -} - -FSTTargetChange *FSTTestTargetChangeAckDocuments(DocumentKeySet docs) { - return [[FSTTargetChange alloc] initWithResumeToken:[NSData data] - current:YES - addedDocuments:docs - modifiedDocuments:DocumentKeySet {} - removedDocuments:DocumentKeySet{}]; -} - -FSTTargetChange *FSTTestTargetChange(DocumentKeySet added, - DocumentKeySet modified, - DocumentKeySet removed, - NSData *resumeToken, - BOOL current) { - return [[FSTTargetChange alloc] initWithResumeToken:resumeToken - current:current - addedDocuments:added - modifiedDocuments:modified - removedDocuments:removed]; +TargetChange FSTTestTargetChangeMarkCurrent() { + return {[NSData data], + /*current=*/true, + /*added_documents=*/DocumentKeySet{}, + /*modified_documents=*/DocumentKeySet{}, + /*removed_documents=*/DocumentKeySet{}}; +} + +TargetChange FSTTestTargetChangeAckDocuments(DocumentKeySet docs) { + return {[NSData data], + /*current=*/true, + /*added_documents*/ std::move(docs), + /*modified_documents*/ DocumentKeySet{}, + /*removed_documents*/ DocumentKeySet{}}; } FSTRemoteEvent *FSTTestUpdateRemoteEventWithLimboTargets( diff --git a/Firestore/Source/Core/FSTSyncEngine.mm b/Firestore/Source/Core/FSTSyncEngine.mm index 57ec5434316..8e7ff023052 100644 --- a/Firestore/Source/Core/FSTSyncEngine.mm +++ b/Firestore/Source/Core/FSTSyncEngine.mm @@ -43,6 +43,7 @@ #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/log.h" +#include "absl/types/optional.h" using firebase::firestore::auth::HashUser; using firebase::firestore::auth::User; @@ -57,6 +58,7 @@ using firebase::firestore::model::OnlineState; using firebase::firestore::model::SnapshotVersion; using firebase::firestore::model::TargetId; +using firebase::firestore::remote::TargetChange; using firebase::firestore::util::AsyncQueue; NS_ASSUME_NONNULL_BEGIN @@ -324,23 +326,23 @@ - (void)applyRemoteEvent:(FSTRemoteEvent *)remoteEvent { // Update `receivedDocument` as appropriate for any limbo targets. for (const auto &entry : remoteEvent.targetChanges) { TargetId targetID = entry.first; - FSTTargetChange *change = entry.second; + const TargetChange &change = entry.second; const auto iter = _limboResolutionsByTarget.find(targetID); if (iter != _limboResolutionsByTarget.end()) { LimboResolution &limboResolution = iter->second; // Since this is a limbo resolution lookup, it's for a single document and it could be // added, modified, or removed, but not a combination. - HARD_ASSERT(change.addedDocuments.size() + change.modifiedDocuments.size() + - change.removedDocuments.size() <= + HARD_ASSERT(change.added_documents().size() + change.modified_documents().size() + + change.removed_documents().size() <= 1, "Limbo resolution for single document contains multiple changes."); - if (change.addedDocuments.size() > 0) { + if (change.added_documents().size() > 0) { limboResolution.document_received = true; - } else if (change.modifiedDocuments.size() > 0) { + } else if (change.modified_documents().size() > 0) { HARD_ASSERT(limboResolution.document_received, "Received change for limbo target document without add."); - } else if (change.removedDocuments.size() > 0) { + } else if (change.removed_documents().size() > 0) { HARD_ASSERT(limboResolution.document_received, "Received remove for limbo target document without add."); limboResolution.document_received = false; @@ -498,7 +500,7 @@ - (void)emitNewSnapshotsAndNotifyLocalStoreWithChanges:(const MaybeDocumentMap & previousChanges:viewDocChanges]; } - FSTTargetChange *_Nullable targetChange = nil; + absl::optional targetChange; if (remoteEvent) { auto it = remoteEvent.targetChanges.find(queryView.targetID); if (it != remoteEvent.targetChanges.end()) { diff --git a/Firestore/Source/Core/FSTView.h b/Firestore/Source/Core/FSTView.h index 1a25cd4ea7d..6c0620194df 100644 --- a/Firestore/Source/Core/FSTView.h +++ b/Firestore/Source/Core/FSTView.h @@ -21,10 +21,21 @@ #include "Firestore/core/src/firebase/firestore/model/document_map.h" #include "Firestore/core/src/firebase/firestore/model/types.h" +#include "absl/types/optional.h" + +namespace firebase { +namespace firestore { +namespace remote { + +class TargetChange; + +} // namespace remote +} // namespace firestore +} // namespace firebase + @class FSTDocumentSet; @class FSTDocumentViewChangeSet; @class FSTQuery; -@class FSTTargetChange; @class FSTViewSnapshot; NS_ASSUME_NONNULL_BEGIN @@ -139,8 +150,10 @@ typedef NS_ENUM(NSInteger, FSTLimboDocumentChangeType) { * @param targetChange A target change to apply for computing limbo docs and sync state. * @return A new FSTViewChange with the given docs, changes, and sync state. */ -- (FSTViewChange *)applyChangesToDocuments:(FSTViewDocumentChanges *)docChanges - targetChange:(nullable FSTTargetChange *)targetChange; +- (FSTViewChange *) + applyChangesToDocuments:(FSTViewDocumentChanges *)docChanges + targetChange: + (const absl::optional &)targetChange; /** * Applies an OnlineState change to the view, potentially generating an FSTViewChange if the diff --git a/Firestore/Source/Core/FSTView.mm b/Firestore/Source/Core/FSTView.mm index d400544bd58..0efdc79b1b3 100644 --- a/Firestore/Source/Core/FSTView.mm +++ b/Firestore/Source/Core/FSTView.mm @@ -26,6 +26,7 @@ #import "Firestore/Source/Remote/FSTRemoteEvent.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/remote/remote_event.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" using firebase::firestore::core::DocumentViewChangeType; @@ -33,6 +34,7 @@ using firebase::firestore::model::DocumentKeySet; using firebase::firestore::model::MaybeDocumentMap; using firebase::firestore::model::OnlineState; +using firebase::firestore::remote::TargetChange; NS_ASSUME_NONNULL_BEGIN @@ -343,11 +345,11 @@ - (BOOL)shouldWaitForSyncedDocument:(FSTDocument *)newDoc oldDocument:(FSTDocume } - (FSTViewChange *)applyChangesToDocuments:(FSTViewDocumentChanges *)docChanges { - return [self applyChangesToDocuments:docChanges targetChange:nil]; + return [self applyChangesToDocuments:docChanges targetChange:{}]; } - (FSTViewChange *)applyChangesToDocuments:(FSTViewDocumentChanges *)docChanges - targetChange:(nullable FSTTargetChange *)targetChange { + targetChange:(const absl::optional &)targetChange { HARD_ASSERT(!docChanges.needsRefill, "Cannot apply changes that need a refill"); FSTDocumentSet *oldDocuments = self.documentSet; @@ -392,7 +394,7 @@ - (FSTViewChange *)applyChangesToDocuments:(FSTViewDocumentChanges *)docChanges - (FSTViewChange *)applyChangedOnlineState:(OnlineState)onlineState { if (self.isCurrent && onlineState == OnlineState::Offline) { // If we're offline, set `current` to NO and then call applyChanges to refresh our syncState - // and generate an FSTViewChange as appropriate. We are guaranteed to get a new FSTTargetChange + // and generate an FSTViewChange as appropriate. We are guaranteed to get a new `TargetChange` // that sets `current` back to YES once the client is back online. self.current = NO; return @@ -433,20 +435,22 @@ - (BOOL)shouldBeLimboDocumentKey:(const DocumentKey &)key { /** * Updates syncedDocuments and current based on the given change. */ -- (void)applyTargetChange:(nullable FSTTargetChange *)targetChange { - if (targetChange) { - for (const DocumentKey &key : targetChange.addedDocuments) { +- (void)applyTargetChange:(const absl::optional &)maybeTargetChange { + if (maybeTargetChange.has_value()) { + const TargetChange &target_change = maybeTargetChange.value(); + + for (const DocumentKey &key : target_change.added_documents()) { _syncedDocuments = _syncedDocuments.insert(key); } - for (const DocumentKey &key : targetChange.modifiedDocuments) { + for (const DocumentKey &key : target_change.modified_documents()) { HARD_ASSERT(_syncedDocuments.find(key) != _syncedDocuments.end(), "Modified document %s not found in view.", key.ToString()); } - for (const DocumentKey &key : targetChange.removedDocuments) { + for (const DocumentKey &key : target_change.removed_documents()) { _syncedDocuments = _syncedDocuments.erase(key); } - self.current = targetChange.current; + self.current = target_change.current(); } } diff --git a/Firestore/Source/Local/FSTLocalStore.mm b/Firestore/Source/Local/FSTLocalStore.mm index 11f0aec0898..034c16e532a 100644 --- a/Firestore/Source/Local/FSTLocalStore.mm +++ b/Firestore/Source/Local/FSTLocalStore.mm @@ -42,6 +42,7 @@ #include "Firestore/core/src/firebase/firestore/local/reference_set.h" #include "Firestore/core/src/firebase/firestore/local/remote_document_cache.h" #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" +#include "Firestore/core/src/firebase/firestore/remote/remote_event.h" #include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/log.h" @@ -60,6 +61,7 @@ using firebase::firestore::model::ListenSequenceNumber; using firebase::firestore::model::SnapshotVersion; using firebase::firestore::model::TargetId; +using firebase::firestore::remote::TargetChange; NS_ASSUME_NONNULL_BEGIN @@ -216,7 +218,7 @@ - (MaybeDocumentMap)applyRemoteEvent:(FSTRemoteEvent *)remoteEvent { DocumentKeySet authoritativeUpdates; for (const auto &entry : remoteEvent.targetChanges) { TargetId targetID = entry.first; - FSTTargetChange *change = entry.second; + const TargetChange &change = entry.second; // Do not ref/unref unassigned targetIDs - it may lead to leaks. auto found = _targetIDs.find(targetID); @@ -233,20 +235,20 @@ - (MaybeDocumentMap)applyRemoteEvent:(FSTRemoteEvent *)remoteEvent { // If the document is only updated while removing it from a target then watch isn't obligated // to send the absolute latest version: it can send the first version that caused the document // not to match. - for (const DocumentKey &key : change.addedDocuments) { + for (const DocumentKey &key : change.added_documents()) { authoritativeUpdates = authoritativeUpdates.insert(key); } - for (const DocumentKey &key : change.modifiedDocuments) { + for (const DocumentKey &key : change.modified_documents()) { authoritativeUpdates = authoritativeUpdates.insert(key); } - _queryCache->RemoveMatchingKeys(change.removedDocuments, targetID); - _queryCache->AddMatchingKeys(change.addedDocuments, targetID); + _queryCache->RemoveMatchingKeys(change.removed_documents(), targetID); + _queryCache->AddMatchingKeys(change.added_documents(), targetID); // Update the resume token if the change includes one. Don't clear any preexisting value. // Bump the sequence number as well, so that documents being removed now are ordered later // than documents that were previously removed from this target. - NSData *resumeToken = change.resumeToken; + NSData *resumeToken = change.resume_token(); if (resumeToken.length > 0) { FSTQueryData *oldQueryData = queryData; queryData = [queryData queryDataByReplacingSnapshotVersion:remoteEvent.snapshotVersion @@ -328,7 +330,7 @@ - (MaybeDocumentMap)applyRemoteEvent:(FSTRemoteEvent *)remoteEvent { */ - (BOOL)shouldPersistQueryData:(FSTQueryData *)newQueryData oldQueryData:(FSTQueryData *)oldQueryData - change:(FSTTargetChange *)change { + change:(const TargetChange &)change { // Avoid clearing any existing value if (newQueryData.resumeToken.length == 0) return NO; @@ -348,8 +350,8 @@ - (BOOL)shouldPersistQueryData:(FSTQueryData *)newQueryData // worth persisting. Note that the RemoteStore keeps an in-memory view of the currently active // targets which includes the current resume token, so stream failure or user changes will still // use an up-to-date resume token regardless of what we do here. - size_t changes = change.addedDocuments.size() + change.modifiedDocuments.size() + - change.removedDocuments.size(); + size_t changes = change.added_documents().size() + change.modified_documents().size() + + change.removed_documents().size(); return changes > 0; } diff --git a/Firestore/Source/Remote/FSTRemoteEvent.h b/Firestore/Source/Remote/FSTRemoteEvent.h index 3a97a4be138..38667aa136d 100644 --- a/Firestore/Source/Remote/FSTRemoteEvent.h +++ b/Firestore/Source/Remote/FSTRemoteEvent.h @@ -33,62 +33,6 @@ NS_ASSUME_NONNULL_BEGIN -#pragma mark - FSTTargetChange - -/** - * An FSTTargetChange specifies the set of changes for a specific target as part of an - * FSTRemoteEvent. These changes track which documents are added, modified or emoved, as well as the - * target's resume token and whether the target is marked CURRENT. - * - * The actual changes *to* documents are not part of the FSTTargetChange since documents may be part - * of multiple targets. - */ -@interface FSTTargetChange : NSObject - -/** - * Creates a new target change with the given SnapshotVersion. - */ -- (instancetype)initWithResumeToken:(NSData *)resumeToken - current:(BOOL)current - addedDocuments:(firebase::firestore::model::DocumentKeySet)addedDocuments - modifiedDocuments:(firebase::firestore::model::DocumentKeySet)modifiedDocuments - removedDocuments:(firebase::firestore::model::DocumentKeySet)removedDocuments - NS_DESIGNATED_INITIALIZER; - -- (instancetype)init NS_UNAVAILABLE; - -/** - * An opaque, server-assigned token that allows watching a query to be resumed after - * disconnecting without retransmitting all the data that matches the query. The resume token - * essentially identifies a point in time from which the server should resume sending results. - */ -@property(nonatomic, strong, readonly) NSData *resumeToken; - -/** - * The "current" (synced) status of this target. Note that "current" has special meaning in the RPC - * protocol that implies that a target is both up-to-date and consistent with the rest of the watch - * stream. - */ -@property(nonatomic, assign, readonly) BOOL current; - -/** - * The set of documents that were newly assigned to this target as part of this remote event. - */ -- (const firebase::firestore::model::DocumentKeySet &)addedDocuments; - -/** - * The set of documents that were already assigned to this target but received an update during this - * remote event. - */ -- (const firebase::firestore::model::DocumentKeySet &)modifiedDocuments; - -/** - * The set of documents that were removed from this target as part of this remote event. - */ -- (const firebase::firestore::model::DocumentKeySet &)removedDocuments; - -@end - #pragma mark - FSTRemoteEvent /** @@ -100,8 +44,8 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype) initWithSnapshotVersion:(firebase::firestore::model::SnapshotVersion)snapshotVersion targetChanges: - (std::unordered_map) - targetChanges + (std::unordered_map)targetChanges targetMismatches: (std::unordered_set)targetMismatches documentUpdates: @@ -119,8 +63,8 @@ NS_ASSUME_NONNULL_BEGIN - (const firebase::firestore::model::DocumentKeySet &)limboDocumentChanges; /** A map from target to changes to the target. See TargetChange. */ -- (const std::unordered_map &) - targetChanges; +- (const std::unordered_map &)targetChanges; /** * A set of targets that is known to be inconsistent. Listens for these targets should be diff --git a/Firestore/Source/Remote/FSTRemoteEvent.mm b/Firestore/Source/Remote/FSTRemoteEvent.mm index 408c2501ce8..89ab3ebb541 100644 --- a/Firestore/Source/Remote/FSTRemoteEvent.mm +++ b/Firestore/Source/Remote/FSTRemoteEvent.mm @@ -40,81 +40,28 @@ using firebase::firestore::model::TargetId; using firebase::firestore::remote::DocumentWatchChange; using firebase::firestore::remote::ExistenceFilterWatchChange; +using firebase::firestore::remote::TargetChange; using firebase::firestore::remote::WatchTargetChange; using firebase::firestore::remote::WatchTargetChangeState; using firebase::firestore::util::Hash; NS_ASSUME_NONNULL_BEGIN -#pragma mark - FSTTargetChange - -@implementation FSTTargetChange { - DocumentKeySet _addedDocuments; - DocumentKeySet _modifiedDocuments; - DocumentKeySet _removedDocuments; -} - -- (instancetype)initWithResumeToken:(NSData *)resumeToken - current:(BOOL)current - addedDocuments:(DocumentKeySet)addedDocuments - modifiedDocuments:(DocumentKeySet)modifiedDocuments - removedDocuments:(DocumentKeySet)removedDocuments { - if (self = [super init]) { - _resumeToken = [resumeToken copy]; - _current = current; - _addedDocuments = std::move(addedDocuments); - _modifiedDocuments = std::move(modifiedDocuments); - _removedDocuments = std::move(removedDocuments); - } - return self; -} - -- (const DocumentKeySet &)addedDocuments { - return _addedDocuments; -} - -- (const DocumentKeySet &)modifiedDocuments { - return _modifiedDocuments; -} - -- (const DocumentKeySet &)removedDocuments { - return _removedDocuments; -} - -- (BOOL)isEqual:(id)other { - if (other == self) { - return YES; - } - if (![other isMemberOfClass:[FSTTargetChange class]]) { - return NO; - } - - return [self current] == [other current] && - [[self resumeToken] isEqualToData:[other resumeToken]] && - [self addedDocuments] == [other addedDocuments] && - [self modifiedDocuments] == [other modifiedDocuments] && - [self removedDocuments] == [other removedDocuments]; -} - -@end - -#pragma mark - FSTRemoteEvent - @implementation FSTRemoteEvent { SnapshotVersion _snapshotVersion; - std::unordered_map _targetChanges; + std::unordered_map _targetChanges; std::unordered_set _targetMismatches; std::unordered_map _documentUpdates; DocumentKeySet _limboDocumentChanges; } -- (instancetype) - initWithSnapshotVersion:(SnapshotVersion)snapshotVersion - targetChanges:(std::unordered_map)targetChanges - targetMismatches:(std::unordered_set)targetMismatches - documentUpdates:(std::unordered_map) +- (instancetype)initWithSnapshotVersion:(SnapshotVersion)snapshotVersion + targetChanges:(std::unordered_map)targetChanges + targetMismatches:(std::unordered_set)targetMismatches + documentUpdates: + (std::unordered_map) documentUpdates - limboDocuments:(DocumentKeySet)limboDocuments { + limboDocuments:(DocumentKeySet)limboDocuments { self = [super init]; if (self) { _snapshotVersion = std::move(snapshotVersion); @@ -134,7 +81,7 @@ @implementation FSTRemoteEvent { return _limboDocumentChanges; } -- (const std::unordered_map &)targetChanges { +- (const std::unordered_map &)targetChanges { return _targetChanges; } diff --git a/Firestore/Source/Remote/FSTRemoteStore.mm b/Firestore/Source/Remote/FSTRemoteStore.mm index 507c4c6ec6f..4e309b95724 100644 --- a/Firestore/Source/Remote/FSTRemoteStore.mm +++ b/Firestore/Source/Remote/FSTRemoteStore.mm @@ -60,6 +60,7 @@ using firebase::firestore::remote::WriteStream; using firebase::firestore::remote::DocumentWatchChange; using firebase::firestore::remote::ExistenceFilterWatchChange; +using firebase::firestore::remote::TargetChange; using firebase::firestore::remote::WatchChange; using firebase::firestore::remote::WatchChangeAggregator; using firebase::firestore::remote::WatchTargetChange; @@ -377,7 +378,8 @@ - (void)raiseWatchSnapshotWithSnapshotVersion:(const SnapshotVersion &)snapshotV // Update in-memory resume tokens. FSTLocalStore will update the persistent view of these when // applying the completed FSTRemoteEvent. for (const auto &entry : remoteEvent.targetChanges) { - NSData *resumeToken = entry.second.resumeToken; + const TargetChange &target_change = entry.second; + NSData *resumeToken = target_change.resume_token(); if (resumeToken.length > 0) { TargetId targetID = entry.first; auto found = _listenTargets.find(targetID); diff --git a/Firestore/core/src/firebase/firestore/remote/remote_event.h b/Firestore/core/src/firebase/firestore/remote/remote_event.h index e19a9badc06..e8b6f01ba9e 100644 --- a/Firestore/core/src/firebase/firestore/remote/remote_event.h +++ b/Firestore/core/src/firebase/firestore/remote/remote_event.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "Firestore/core/src/firebase/firestore/core/view_snapshot.h" @@ -40,7 +41,6 @@ @class FSTMaybeDocument; @class FSTQueryData; @class FSTRemoteEvent; -@class FSTTargetChange; NS_ASSUME_NONNULL_BEGIN @@ -70,6 +70,84 @@ namespace firebase { namespace firestore { namespace remote { +/** + * A `TargetChange` specifies the set of changes for a specific target as part + * of an `FSTRemoteEvent`. These changes track which documents are added, + * modified or emoved, as well as the target's resume token and whether the + * target is marked CURRENT. + * + * The actual changes *to* documents are not part of the `TargetChange` since + * documents may be part of multiple targets. + */ +class TargetChange { + public: + TargetChange() = default; + + TargetChange(NSData* resume_token, + bool current, + model::DocumentKeySet added_documents, + model::DocumentKeySet modified_documents, + model::DocumentKeySet removed_documents) + : resume_token_{resume_token}, + current_{current}, + added_documents_{std::move(added_documents)}, + modified_documents_{std::move(modified_documents)}, + removed_documents_{std::move(removed_documents)} { + } + + /** + * An opaque, server-assigned token that allows watching a query to be resumed + * after disconnecting without retransmitting all the data that matches the + * query. The resume token essentially identifies a point in time from which + * the server should resume sending results. + */ + NSData* resume_token() const { + return resume_token_; + } + + /** + * The "current" (synced) status of this target. Note that "current" has + * special meaning in the RPC protocol that implies that a target is both + * up-to-date and consistent with the rest of the watch stream. + */ + bool current() const { + return current_; + } + + /** + * The set of documents that were newly assigned to this target as part of + * this remote event. + */ + const model::DocumentKeySet& added_documents() const { + return added_documents_; + } + + /** + * The set of documents that were already assigned to this target but received + * an update during this remote event. + */ + const model::DocumentKeySet& modified_documents() const { + return modified_documents_; + } + + /** + * The set of documents that were removed from this target as part of this + * remote event. + */ + const model::DocumentKeySet& removed_documents() const { + return removed_documents_; + } + + private: + NSData* resume_token_ = nil; + bool current_ = false; + model::DocumentKeySet added_documents_; + model::DocumentKeySet modified_documents_; + model::DocumentKeySet removed_documents_; +}; + +bool operator==(const TargetChange& lhs, const TargetChange& rhs); + /** Tracks the internal state of a Watch target. */ class TargetState { public: @@ -78,12 +156,12 @@ class TargetState { /** * Whether this target has been marked 'current'. * - * 'Current' has special meaning in the RPC protocol: It implies that the + * 'current' has special meaning in the RPC protocol: It implies that the * Watch backend has sent us all changes up to the point at which the target * was added and that the target is consistent with the rest of the watch * stream. */ - bool Current() const { + bool current() const { return current_; } @@ -114,7 +192,7 @@ class TargetState { * To reset the document changes after raising this snapshot, call * `ClearPendingChanges()`. */ - FSTTargetChange* ToTargetChange() const; + TargetChange ToTargetChange() const; /** Resets the document changes and sets `HasPendingChanges` to false. */ void ClearPendingChanges(); diff --git a/Firestore/core/src/firebase/firestore/remote/remote_event.mm b/Firestore/core/src/firebase/firestore/remote/remote_event.mm index d9203f532c8..787e7b596af 100644 --- a/Firestore/core/src/firebase/firestore/remote/remote_event.mm +++ b/Firestore/core/src/firebase/firestore/remote/remote_event.mm @@ -33,6 +33,16 @@ namespace firestore { namespace remote { +// TargetChange + +bool operator==(const TargetChange& lhs, const TargetChange& rhs) { + return [lhs.resume_token() isEqualToData:rhs.resume_token()] && + lhs.current() == rhs.current() && + lhs.added_documents() == rhs.added_documents() && + lhs.modified_documents() == rhs.modified_documents() && + lhs.removed_documents() == rhs.removed_documents(); +} + // TargetState TargetState::TargetState() : resume_token_{[NSData data]} { @@ -45,7 +55,7 @@ } } -FSTTargetChange* TargetState::ToTargetChange() const { +TargetChange TargetState::ToTargetChange() const { DocumentKeySet added_documents; DocumentKeySet modified_documents; DocumentKeySet removed_documents; @@ -69,12 +79,9 @@ } } - return [[FSTTargetChange alloc] - initWithResumeToken:resume_token() - current:Current() - addedDocuments:std::move(added_documents) - modifiedDocuments:std::move(modified_documents) - removedDocuments:std::move(removed_documents)]; + return TargetChange{resume_token(), current(), std::move(added_documents), + std::move(modified_documents), + std::move(removed_documents)}; } void TargetState::ClearPendingChanges() { @@ -237,7 +244,7 @@ FSTRemoteEvent* WatchChangeAggregator::CreateRemoteEvent( const SnapshotVersion& snapshot_version) { - std::unordered_map target_changes; + std::unordered_map target_changes; for (auto& entry : target_states_) { TargetId target_id = entry.first; @@ -245,7 +252,7 @@ FSTQueryData* query_data = QueryDataForActiveTarget(target_id); if (query_data) { - if (target_state.Current() && [query_data.query isDocumentQuery]) { + if (target_state.current() && [query_data.query isDocumentQuery]) { // Document queries for document that don't exist can produce an empty // result set. To update our local cache, we synthesize a document // delete if we have not previously received the document. This resolves @@ -291,12 +298,12 @@ } } - FSTRemoteEvent* remote_event = - [[FSTRemoteEvent alloc] initWithSnapshotVersion:snapshot_version - targetChanges:target_changes - targetMismatches:pending_target_resets_ - documentUpdates:pending_document_updates_ - limboDocuments:resolved_limbo_documents]; + FSTRemoteEvent* remote_event = [[FSTRemoteEvent alloc] + initWithSnapshotVersion:snapshot_version + targetChanges:target_changes + targetMismatches:std::move(pending_target_resets_) + documentUpdates:std::move(pending_document_updates_) + limboDocuments:std::move(resolved_limbo_documents)]; // Re-initialize the current state to ensure that we do not modify the // generated `RemoteEvent`. @@ -355,10 +362,10 @@ int WatchChangeAggregator::GetCurrentDocumentCountForTarget( TargetId target_id) { TargetState& target_state = EnsureTargetState(target_id); - FSTTargetChange* target_change = target_state.ToTargetChange(); + TargetChange target_change = target_state.ToTargetChange(); return ([target_metadata_provider_ remoteKeysForTarget:target_id].size() + - target_change.addedDocuments.size() - - target_change.removedDocuments.size()); + target_change.added_documents().size() - + target_change.removed_documents().size()); } void WatchChangeAggregator::RecordPendingTargetRequest(TargetId target_id) {