Files
firebase-ios-sdk/Firestore/Example/Tests/Remote/FSTRemoteEventTests.m
T
Gil 4fbc96904f Use fully qualified imports in Firestore (#467)
This simplifies the import process back into google3 and allows us to add additional directories to the project without needing to update the project files for each directory we add. This shows up most clearly in the change to Firestore/Example/Firestore.xcodeproj/project.pbxproj: this no longer needs to list essentially every directory in the project as a header search path.

* Clang-format configuration for C++ files
* Add support C++-only sources to the podspec
* Podspec support for fully qualified imports
* xcodeproj changes for fully-qualified imports
* Use fully-qualified imports in Firestore sources
2017-11-16 20:52:01 -08:00

557 lines
29 KiB
Objective-C

/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "Firestore/Source/Remote/FSTRemoteEvent.h"
#import <XCTest/XCTest.h>
#import "Firestore/Source/Local/FSTQueryData.h"
#import "Firestore/Source/Model/FSTDocument.h"
#import "Firestore/Source/Model/FSTDocumentKey.h"
#import "Firestore/Source/Remote/FSTExistenceFilter.h"
#import "Firestore/Source/Remote/FSTWatchChange.h"
#import "Firestore/Example/Tests/Remote/FSTWatchChange+Testing.h"
#import "Firestore/Example/Tests/Util/FSTHelpers.h"
NS_ASSUME_NONNULL_BEGIN
@interface FSTRemoteEventTests : XCTestCase
@end
@implementation FSTRemoteEventTests {
NSData *_resumeToken1;
NSMutableDictionary<NSNumber *, NSNumber *> *_noPendingResponses;
}
- (void)setUp {
_resumeToken1 = [@"resume1" dataUsingEncoding:NSUTF8StringEncoding];
_noPendingResponses = [NSMutableDictionary dictionary];
}
- (FSTWatchChangeAggregator *)aggregatorWithTargets:(NSArray<NSNumber *> *)targets
outstanding:
(NSDictionary<NSNumber *, NSNumber *> *)outstanding
changes:(NSArray<FSTWatchChange *> *)watchChanges {
NSMutableDictionary<NSNumber *, FSTQueryData *> *listens = [NSMutableDictionary dictionary];
FSTQueryData *dummyQueryData = [FSTQueryData alloc];
for (NSNumber *targetID in targets) {
listens[targetID] = dummyQueryData;
}
FSTWatchChangeAggregator *aggregator =
[[FSTWatchChangeAggregator alloc] initWithSnapshotVersion:FSTTestVersion(3)
listenTargets:listens
pendingTargetResponses:outstanding];
[aggregator addWatchChanges:watchChanges];
return aggregator;
}
- (void)testWillAccumulateDocumentAddedAndRemovedEvents {
FSTDocument *doc1 = FSTTestDoc(@"docs/1", 1, @{ @"value" : @1 }, NO);
FSTDocument *doc2 = FSTTestDoc(@"docs/2", 2, @{ @"value" : @2 }, NO);
FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1, @2, @3 ]
removedTargetIDs:@[ @4, @5, @6 ]
documentKey:doc1.key
document:doc1];
FSTWatchChange *change2 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1, @4 ]
removedTargetIDs:@[ @2, @6 ]
documentKey:doc2.key
document:doc2];
FSTWatchChangeAggregator *aggregator = [self aggregatorWithTargets:@[ @1, @2, @3, @4, @5, @6 ]
outstanding:_noPendingResponses
changes:@[ change1, change2 ]];
FSTRemoteEvent *event = [aggregator remoteEvent];
XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
XCTAssertEqual(event.documentUpdates.count, 2);
XCTAssertEqualObjects(event.documentUpdates[doc1.key], doc1);
XCTAssertEqualObjects(event.documentUpdates[doc2.key], doc2);
XCTAssertEqual(event.targetChanges.count, 6);
FSTUpdateMapping *mapping1 =
[FSTUpdateMapping mappingWithAddedDocuments:@[ doc1, doc2 ] removedDocuments:@[]];
XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
FSTUpdateMapping *mapping2 =
[FSTUpdateMapping mappingWithAddedDocuments:@[ doc1 ] removedDocuments:@[ doc2 ]];
XCTAssertEqualObjects(event.targetChanges[@2].mapping, mapping2);
FSTUpdateMapping *mapping3 =
[FSTUpdateMapping mappingWithAddedDocuments:@[ doc1 ] removedDocuments:@[]];
XCTAssertEqualObjects(event.targetChanges[@3].mapping, mapping3);
FSTUpdateMapping *mapping4 =
[FSTUpdateMapping mappingWithAddedDocuments:@[ doc2 ] removedDocuments:@[ doc1 ]];
XCTAssertEqualObjects(event.targetChanges[@4].mapping, mapping4);
FSTUpdateMapping *mapping5 =
[FSTUpdateMapping mappingWithAddedDocuments:@[] removedDocuments:@[ doc1 ]];
XCTAssertEqualObjects(event.targetChanges[@5].mapping, mapping5);
FSTUpdateMapping *mapping6 =
[FSTUpdateMapping mappingWithAddedDocuments:@[] removedDocuments:@[ doc1, doc2 ]];
XCTAssertEqualObjects(event.targetChanges[@6].mapping, mapping6);
}
- (void)testWillIgnoreEventsForPendingTargets {
FSTDocument *doc1 = FSTTestDoc(@"docs/1", 1, @{ @"value" : @1 }, NO);
FSTDocument *doc2 = FSTTestDoc(@"docs/2", 2, @{ @"value" : @2 }, NO);
FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
removedTargetIDs:@[]
documentKey:doc1.key
document:doc1];
FSTWatchChange *change2 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateRemoved
targetIDs:@[ @1 ]
cause:nil];
FSTWatchChange *change3 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateAdded
targetIDs:@[ @1 ]
cause:nil];
FSTWatchChange *change4 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
removedTargetIDs:@[]
documentKey:doc2.key
document:doc2];
// We're waiting for the unwatch and watch ack
NSDictionary<NSNumber *, NSNumber *> *pendingResponses = @{ @1 : @2 };
FSTWatchChangeAggregator *aggregator =
[self aggregatorWithTargets:@[ @1 ]
outstanding:pendingResponses
changes:@[ change1, change2, change3, change4 ]];
FSTRemoteEvent *event = [aggregator remoteEvent];
XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
// doc1 is ignored because it was part of an inactive target, but doc2 is in the changes
// because it become active.
XCTAssertEqual(event.documentUpdates.count, 1);
XCTAssertEqualObjects(event.documentUpdates[doc2.key], doc2);
XCTAssertEqual(event.targetChanges.count, 1);
}
- (void)testWillIgnoreEventsForRemovedTargets {
FSTDocument *doc1 = FSTTestDoc(@"docs/1", 1, @{ @"value" : @1 }, NO);
FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
removedTargetIDs:@[]
documentKey:doc1.key
document:doc1];
FSTWatchChange *change2 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateRemoved
targetIDs:@[ @1 ]
cause:nil];
// We're waiting for the unwatch ack
NSDictionary<NSNumber *, NSNumber *> *pendingResponses = @{ @1 : @1 };
FSTWatchChangeAggregator *aggregator =
[self aggregatorWithTargets:@[] outstanding:pendingResponses changes:@[ change1, change2 ]];
FSTRemoteEvent *event = [aggregator remoteEvent];
XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
// doc1 is ignored because it was part of an inactive target
XCTAssertEqual(event.documentUpdates.count, 0);
// Target 1 is ignored because it was removed
XCTAssertEqual(event.targetChanges.count, 0);
}
- (void)testWillKeepResetMappingEvenWithUpdates {
FSTDocument *doc1 = FSTTestDoc(@"docs/1", 1, @{ @"value" : @1 }, NO);
FSTDocument *doc2 = FSTTestDoc(@"docs/2", 2, @{ @"value" : @2 }, NO);
FSTDocument *doc3 = FSTTestDoc(@"docs/3", 3, @{ @"value" : @3 }, NO);
FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
removedTargetIDs:@[]
documentKey:doc1.key
document:doc1];
// Reset stream, ignoring doc1
FSTWatchChange *change2 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateReset
targetIDs:@[ @1 ]
cause:nil];
// Add doc2, doc3
FSTWatchChange *change3 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
removedTargetIDs:@[]
documentKey:doc2.key
document:doc2];
FSTWatchChange *change4 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
removedTargetIDs:@[]
documentKey:doc3.key
document:doc3];
// Remove doc2 again, should not show up in reset mapping
FSTWatchChange *change5 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[]
removedTargetIDs:@[ @1 ]
documentKey:doc2.key
document:doc2];
FSTWatchChangeAggregator *aggregator =
[self aggregatorWithTargets:@[ @1 ]
outstanding:_noPendingResponses
changes:@[ change1, change2, change3, change4, change5 ]];
FSTRemoteEvent *event = [aggregator remoteEvent];
XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
XCTAssertEqual(event.documentUpdates.count, 3);
XCTAssertEqualObjects(event.documentUpdates[doc1.key], doc1);
XCTAssertEqualObjects(event.documentUpdates[doc2.key], doc2);
XCTAssertEqualObjects(event.documentUpdates[doc3.key], doc3);
XCTAssertEqual(event.targetChanges.count, 1);
// Only doc3 is part of the new mapping
FSTResetMapping *expectedMapping = [FSTResetMapping mappingWithDocuments:@[ doc3 ]];
XCTAssertEqualObjects(event.targetChanges[@1].mapping, expectedMapping);
}
- (void)testWillHandleSingleReset {
// Reset target
FSTWatchChange *change = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateReset
targetIDs:@[ @1 ]
cause:nil];
FSTWatchChangeAggregator *aggregator =
[self aggregatorWithTargets:@[ @1 ] outstanding:_noPendingResponses changes:@[ change ]];
FSTRemoteEvent *event = [aggregator remoteEvent];
XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
XCTAssertEqual(event.documentUpdates.count, 0);
XCTAssertEqual(event.targetChanges.count, 1);
// Reset mapping is empty
FSTResetMapping *expectedMapping = [FSTResetMapping mappingWithDocuments:@[]];
XCTAssertEqualObjects(event.targetChanges[@1].mapping, expectedMapping);
}
- (void)testWillHandleTargetAddAndRemovalInSameBatch {
FSTDocument *doc1a = FSTTestDoc(@"docs/1", 1, @{ @"value" : @1 }, NO);
FSTDocument *doc1b = FSTTestDoc(@"docs/1", 1, @{ @"value" : @2 }, NO);
FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
removedTargetIDs:@[ @2 ]
documentKey:doc1a.key
document:doc1a];
FSTWatchChange *change2 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @2 ]
removedTargetIDs:@[ @1 ]
documentKey:doc1b.key
document:doc1b];
FSTWatchChangeAggregator *aggregator = [self aggregatorWithTargets:@[ @1, @2 ]
outstanding:_noPendingResponses
changes:@[ change1, change2 ]];
FSTRemoteEvent *event = [aggregator remoteEvent];
XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
XCTAssertEqual(event.documentUpdates.count, 1);
XCTAssertEqualObjects(event.documentUpdates[doc1b.key], doc1b);
XCTAssertEqual(event.targetChanges.count, 2);
FSTUpdateMapping *mapping1 =
[FSTUpdateMapping mappingWithAddedDocuments:@[] removedDocuments:@[ doc1b ]];
XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
FSTUpdateMapping *mapping2 =
[FSTUpdateMapping mappingWithAddedDocuments:@[ doc1b ] removedDocuments:@[]];
XCTAssertEqualObjects(event.targetChanges[@2].mapping, mapping2);
}
- (void)testTargetCurrentChangeWillMarkTheTargetCurrent {
FSTWatchChange *change = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
targetIDs:@[ @1 ]
resumeToken:_resumeToken1];
FSTWatchChangeAggregator *aggregator =
[self aggregatorWithTargets:@[ @1 ] outstanding:_noPendingResponses changes:@[ change ]];
FSTRemoteEvent *event = [aggregator remoteEvent];
XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
XCTAssertEqual(event.documentUpdates.count, 0);
XCTAssertEqual(event.targetChanges.count, 1);
FSTTargetChange *targetChange = event.targetChanges[@1];
XCTAssertEqualObjects(targetChange.mapping, [[FSTUpdateMapping alloc] init]);
XCTAssertEqual(targetChange.currentStatusUpdate, FSTCurrentStatusUpdateMarkCurrent);
XCTAssertEqualObjects(targetChange.resumeToken, _resumeToken1);
}
- (void)testTargetAddedChangeWillResetPreviousState {
FSTDocument *doc1 = FSTTestDoc(@"docs/1", 1, @{ @"value" : @1 }, NO);
FSTDocument *doc2 = FSTTestDoc(@"docs/2", 2, @{ @"value" : @2 }, NO);
FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1, @3 ]
removedTargetIDs:@[ @2 ]
documentKey:doc1.key
document:doc1];
FSTWatchChange *change2 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
targetIDs:@[ @1, @2, @3 ]
resumeToken:_resumeToken1];
FSTWatchChange *change3 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateRemoved
targetIDs:@[ @1 ]
cause:nil];
FSTWatchChange *change4 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateRemoved
targetIDs:@[ @2 ]
cause:nil];
FSTWatchChange *change5 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateAdded
targetIDs:@[ @1 ]
cause:nil];
FSTWatchChange *change6 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
removedTargetIDs:@[ @3 ]
documentKey:doc2.key
document:doc2];
NSDictionary<NSNumber *, NSNumber *> *pendingResponses = @{ @1 : @2, @2 : @1 };
FSTWatchChangeAggregator *aggregator =
[self aggregatorWithTargets:@[ @1, @3 ]
outstanding:pendingResponses
changes:@[ change1, change2, change3, change4, change5, change6 ]];
FSTRemoteEvent *event = [aggregator remoteEvent];
XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
XCTAssertEqual(event.documentUpdates.count, 2);
XCTAssertEqualObjects(event.documentUpdates[doc1.key], doc1);
XCTAssertEqualObjects(event.documentUpdates[doc2.key], doc2);
// target 1 and 3 are affected (1 because of re-add), target 2 is not because of remove
XCTAssertEqual(event.targetChanges.count, 2);
// doc1 was before the remove, so it does not show up in the mapping
FSTUpdateMapping *mapping1 =
[FSTUpdateMapping mappingWithAddedDocuments:@[ doc2 ] removedDocuments:@[]];
XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
// Current was before the remove
XCTAssertEqual(event.targetChanges[@1].currentStatusUpdate, FSTCurrentStatusUpdateNone);
// Doc1 was before the remove
FSTUpdateMapping *mapping3 =
[FSTUpdateMapping mappingWithAddedDocuments:@[ doc1 ] removedDocuments:@[ doc2 ]];
XCTAssertEqualObjects(event.targetChanges[@3].mapping, mapping3);
// Current was before the remove
XCTAssertEqual(event.targetChanges[@3].currentStatusUpdate, FSTCurrentStatusUpdateMarkCurrent);
XCTAssertEqualObjects(event.targetChanges[@3].resumeToken, _resumeToken1);
}
- (void)testNoChangeWillStillMarkTheAffectedTargets {
FSTWatchChange *change = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateNoChange
targetIDs:@[ @1 ]
resumeToken:_resumeToken1];
FSTWatchChangeAggregator *aggregator =
[self aggregatorWithTargets:@[ @1 ] outstanding:_noPendingResponses changes:@[ change ]];
FSTRemoteEvent *event = [aggregator remoteEvent];
XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
XCTAssertEqual(event.documentUpdates.count, 0);
XCTAssertEqual(event.targetChanges.count, 1);
XCTAssertEqualObjects(event.targetChanges[@1].mapping, [[FSTUpdateMapping alloc] init]);
XCTAssertEqual(event.targetChanges[@1].currentStatusUpdate, FSTCurrentStatusUpdateNone);
XCTAssertEqualObjects(event.targetChanges[@1].resumeToken, _resumeToken1);
}
- (void)testExistenceFiltersWillReplacePreviousExistenceFilters {
FSTExistenceFilter *filter1 = [FSTExistenceFilter filterWithCount:1];
FSTExistenceFilter *filter2 = [FSTExistenceFilter filterWithCount:2];
FSTWatchChange *change1 = [FSTExistenceFilterWatchChange changeWithFilter:filter1 targetID:1];
FSTWatchChange *change2 = [FSTExistenceFilterWatchChange changeWithFilter:filter1 targetID:2];
// replace filter1 for target 2
FSTWatchChange *change3 = [FSTExistenceFilterWatchChange changeWithFilter:filter2 targetID:2];
FSTWatchChangeAggregator *aggregator =
[self aggregatorWithTargets:@[ @1, @2 ]
outstanding:_noPendingResponses
changes:@[ change1, change2, change3 ]];
FSTRemoteEvent *event = [aggregator remoteEvent];
XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
XCTAssertEqual(event.documentUpdates.count, 0);
XCTAssertEqual(event.targetChanges.count, 0);
XCTAssertEqual(aggregator.existenceFilters.count, 2);
XCTAssertEqual(aggregator.existenceFilters[@1], filter1);
XCTAssertEqual(aggregator.existenceFilters[@2], filter2);
}
- (void)testExistenceFilterMismatchResetsTarget {
FSTDocument *doc1 = FSTTestDoc(@"docs/1", 1, @{ @"value" : @1 }, NO);
FSTDocument *doc2 = FSTTestDoc(@"docs/2", 2, @{ @"value" : @2 }, NO);
FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
removedTargetIDs:@[]
documentKey:doc1.key
document:doc1];
FSTWatchChange *change2 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
removedTargetIDs:@[]
documentKey:doc2.key
document:doc2];
FSTWatchChange *change3 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
targetIDs:@[ @1 ]
resumeToken:_resumeToken1];
FSTWatchChangeAggregator *aggregator =
[self aggregatorWithTargets:@[ @1 ]
outstanding:_noPendingResponses
changes:@[ change1, change2, change3 ]];
FSTRemoteEvent *event = [aggregator remoteEvent];
XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
XCTAssertEqual(event.documentUpdates.count, 2);
XCTAssertEqualObjects(event.documentUpdates[doc1.key], doc1);
XCTAssertEqualObjects(event.documentUpdates[doc2.key], doc2);
XCTAssertEqual(event.targetChanges.count, 1);
FSTUpdateMapping *mapping1 =
[FSTUpdateMapping mappingWithAddedDocuments:@[ doc1, doc2 ] removedDocuments:@[]];
XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
XCTAssertEqualObjects(event.targetChanges[@1].snapshotVersion, FSTTestVersion(3));
XCTAssertEqual(event.targetChanges[@1].currentStatusUpdate, FSTCurrentStatusUpdateMarkCurrent);
XCTAssertEqualObjects(event.targetChanges[@1].resumeToken, _resumeToken1);
[event handleExistenceFilterMismatchForTargetID:@1];
// Mapping is reset
XCTAssertEqualObjects(event.targetChanges[@1].mapping, [[FSTResetMapping alloc] init]);
// Reset the resume snapshot
XCTAssertEqualObjects(event.targetChanges[@1].snapshotVersion, FSTTestVersion(0));
// Target needs to be set to not current
XCTAssertEqual(event.targetChanges[@1].currentStatusUpdate, FSTCurrentStatusUpdateMarkNotCurrent);
XCTAssertEqual(event.targetChanges[@1].resumeToken.length, 0);
}
- (void)testDocumentUpdate {
FSTDocument *doc1 = FSTTestDoc(@"docs/1", 1, @{ @"value" : @1 }, NO);
FSTDeletedDocument *deletedDoc1 =
[FSTDeletedDocument documentWithKey:doc1.key version:FSTTestVersion(3)];
FSTDocument *doc2 = FSTTestDoc(@"docs/2", 2, @{ @"value" : @2 }, NO);
FSTDocument *doc3 = FSTTestDoc(@"docs/3", 3, @{ @"value" : @3 }, NO);
FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
removedTargetIDs:@[]
documentKey:doc1.key
document:doc1];
FSTWatchChange *change2 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
removedTargetIDs:@[]
documentKey:doc2.key
document:doc2];
FSTWatchChangeAggregator *aggregator = [self aggregatorWithTargets:@[ @1 ]
outstanding:_noPendingResponses
changes:@[ change1, change2 ]];
FSTRemoteEvent *event = [aggregator remoteEvent];
XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
XCTAssertEqual(event.documentUpdates.count, 2);
XCTAssertEqualObjects(event.documentUpdates[doc1.key], doc1);
XCTAssertEqualObjects(event.documentUpdates[doc2.key], doc2);
// Update doc1
[event addDocumentUpdate:deletedDoc1];
[event addDocumentUpdate:doc3];
XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
XCTAssertEqual(event.documentUpdates.count, 3);
// doc1 is replaced
XCTAssertEqualObjects(event.documentUpdates[doc1.key], deletedDoc1);
// doc2 is untouched
XCTAssertEqualObjects(event.documentUpdates[doc2.key], doc2);
// doc3 is new
XCTAssertEqualObjects(event.documentUpdates[doc3.key], doc3);
// Target is unchanged
XCTAssertEqual(event.targetChanges.count, 1);
FSTUpdateMapping *mapping1 =
[FSTUpdateMapping mappingWithAddedDocuments:@[ doc1, doc2 ] removedDocuments:@[]];
XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
}
- (void)testResumeTokensHandledPerTarget {
NSData *resumeToken2 = [@"resume2" dataUsingEncoding:NSUTF8StringEncoding];
FSTWatchChange *change1 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
targetIDs:@[ @1 ]
resumeToken:_resumeToken1];
FSTWatchChange *change2 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
targetIDs:@[ @2 ]
resumeToken:resumeToken2];
FSTWatchChangeAggregator *aggregator = [self aggregatorWithTargets:@[ @1, @2 ]
outstanding:_noPendingResponses
changes:@[ change1, change2 ]];
FSTRemoteEvent *event = [aggregator remoteEvent];
XCTAssertEqual(event.targetChanges.count, 2);
FSTUpdateMapping *mapping1 =
[FSTUpdateMapping mappingWithAddedDocuments:@[] removedDocuments:@[]];
XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
XCTAssertEqualObjects(event.targetChanges[@1].snapshotVersion, FSTTestVersion(3));
XCTAssertEqual(event.targetChanges[@1].currentStatusUpdate, FSTCurrentStatusUpdateMarkCurrent);
XCTAssertEqualObjects(event.targetChanges[@1].resumeToken, _resumeToken1);
XCTAssertEqualObjects(event.targetChanges[@2].mapping, mapping1);
XCTAssertEqualObjects(event.targetChanges[@2].snapshotVersion, FSTTestVersion(3));
XCTAssertEqual(event.targetChanges[@2].currentStatusUpdate, FSTCurrentStatusUpdateMarkCurrent);
XCTAssertEqualObjects(event.targetChanges[@2].resumeToken, resumeToken2);
}
- (void)testLastResumeTokenWins {
NSData *resumeToken2 = [@"resume2" dataUsingEncoding:NSUTF8StringEncoding];
NSData *resumeToken3 = [@"resume3" dataUsingEncoding:NSUTF8StringEncoding];
FSTWatchChange *change1 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
targetIDs:@[ @1 ]
resumeToken:_resumeToken1];
FSTWatchChange *change2 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateReset
targetIDs:@[ @1 ]
resumeToken:resumeToken2];
FSTWatchChange *change3 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateReset
targetIDs:@[ @2 ]
resumeToken:resumeToken3];
FSTWatchChangeAggregator *aggregator =
[self aggregatorWithTargets:@[ @1, @2 ]
outstanding:_noPendingResponses
changes:@[ change1, change2, change3 ]];
FSTRemoteEvent *event = [aggregator remoteEvent];
XCTAssertEqual(event.targetChanges.count, 2);
FSTResetMapping *mapping1 = [FSTResetMapping mappingWithDocuments:@[]];
XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
XCTAssertEqualObjects(event.targetChanges[@1].snapshotVersion, FSTTestVersion(3));
XCTAssertEqual(event.targetChanges[@1].currentStatusUpdate, FSTCurrentStatusUpdateMarkCurrent);
XCTAssertEqualObjects(event.targetChanges[@1].resumeToken, resumeToken2);
XCTAssertEqualObjects(event.targetChanges[@2].mapping, mapping1);
XCTAssertEqualObjects(event.targetChanges[@2].snapshotVersion, FSTTestVersion(3));
XCTAssertEqual(event.targetChanges[@2].currentStatusUpdate, FSTCurrentStatusUpdateNone);
XCTAssertEqualObjects(event.targetChanges[@2].resumeToken, resumeToken3);
}
@end
NS_ASSUME_NONNULL_END