Files
firebase-ios-sdk/Firestore/Example/Tests/Remote/FSTRemoteEventTests.mm
T
Gil 729b8d176c Move all Firestore Objective-C to Objective-C++ (#734)
* Move all Firestore files to Objective-C++
* Update project file references
* Don't use module imports from Objective-C++
* Use extern "C" for C-accessible globals
* Work around more stringent type checking in Objective-C++
  * NSMutableDictionary ivars aren't implicitly casted to NSDictionary
  * FSTMaybeDocument callback can't be passed a function that accepts
    FSTDocument
  * NSComparisonResult can't be multiplied by -1 without casting
* Add a #include <inttypes.h> where needed
* Avoid using C++ keywords as variables
* Remove #if __cplusplus guards
2018-01-31 11:23:55 -08:00

557 lines
29 KiB
Plaintext

/*
* 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