From 729b8d176c75ecc0cbbd137cc6811116a64e310a Mon Sep 17 00:00:00 2001 From: Gil Date: Wed, 31 Jan 2018 11:23:55 -0800 Subject: 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 where needed * Avoid using C++ keywords as variables * Remove #if __cplusplus guards --- .../Example/Tests/Core/FSTQueryListenerTests.mm | 487 +++++++++++++++++++++ 1 file changed, 487 insertions(+) create mode 100644 Firestore/Example/Tests/Core/FSTQueryListenerTests.mm (limited to 'Firestore/Example/Tests/Core/FSTQueryListenerTests.mm') diff --git a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm new file mode 100644 index 0000000..4856b5f --- /dev/null +++ b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm @@ -0,0 +1,487 @@ +/* + * 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/Core/FSTEventManager.h" + +#import + +#import "Firestore/Source/Core/FSTQuery.h" +#import "Firestore/Source/Core/FSTView.h" +#import "Firestore/Source/Model/FSTDocument.h" +#import "Firestore/Source/Model/FSTDocumentSet.h" +#import "Firestore/Source/Remote/FSTRemoteEvent.h" +#import "Firestore/Source/Util/FSTAsyncQueryListener.h" +#import "Firestore/Source/Util/FSTDispatchQueue.h" + +#import "Firestore/Example/Tests/Util/FSTHelpers.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FSTQueryListenerTests : XCTestCase +@property(nonatomic, strong, readonly) FSTDispatchQueue *asyncQueue; +@end + +@implementation FSTQueryListenerTests + +- (void)setUp { + _asyncQueue = [FSTDispatchQueue + queueWith:dispatch_queue_create("FSTQueryListenerTests Queue", DISPATCH_QUEUE_SERIAL)]; +} + +- (void)testRaisesCollectionEvents { + NSMutableArray *accum = [NSMutableArray array]; + NSMutableArray *otherAccum = [NSMutableArray array]; + + FSTQuery *query = FSTTestQuery(@"rooms"); + FSTDocument *doc1 = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, NO); + FSTDocument *doc2 = FSTTestDoc(@"rooms/Hades", 2, @{@"name" : @"Hades"}, NO); + FSTDocument *doc2prime = + FSTTestDoc(@"rooms/Hades", 3, @{@"name" : @"Hades", @"owner" : @"Jonny"}, NO); + + FSTQueryListener *listener = [self listenToQuery:query accumulatingSnapshots:accum]; + FSTQueryListener *otherListener = [self listenToQuery:query accumulatingSnapshots:otherAccum]; + + FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:[FSTDocumentKeySet keySet]]; + FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1, doc2 ], nil); + FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[ doc2prime ], nil); + + FSTDocumentViewChange *change1 = + [FSTDocumentViewChange changeWithDocument:doc1 type:FSTDocumentViewChangeTypeAdded]; + FSTDocumentViewChange *change2 = + [FSTDocumentViewChange changeWithDocument:doc2 type:FSTDocumentViewChangeTypeAdded]; + FSTDocumentViewChange *change3 = + [FSTDocumentViewChange changeWithDocument:doc2prime type:FSTDocumentViewChangeTypeModified]; + FSTDocumentViewChange *change4 = + [FSTDocumentViewChange changeWithDocument:doc2prime type:FSTDocumentViewChangeTypeAdded]; + + [listener queryDidChangeViewSnapshot:snap1]; + [listener queryDidChangeViewSnapshot:snap2]; + [otherListener queryDidChangeViewSnapshot:snap2]; + + XCTAssertEqualObjects(accum, (@[ snap1, snap2 ])); + XCTAssertEqualObjects(accum[0].documentChanges, (@[ change1, change2 ])); + XCTAssertEqualObjects(accum[1].documentChanges, (@[ change3 ])); + + FSTViewSnapshot *expectedSnap2 = [[FSTViewSnapshot alloc] + initWithQuery:snap2.query + documents:snap2.documents + oldDocuments:[FSTDocumentSet documentSetWithComparator:snap2.query.comparator] + documentChanges:@[ change1, change4 ] + fromCache:snap2.fromCache + hasPendingWrites:snap2.hasPendingWrites + syncStateChanged:YES]; + XCTAssertEqualObjects(otherAccum, (@[ expectedSnap2 ])); +} + +- (void)testRaisesErrorEvent { + NSMutableArray *accum = [NSMutableArray array]; + FSTQuery *query = FSTTestQuery(@"rooms/Eros"); + + FSTQueryListener *listener = [self listenToQuery:query + handler:^(FSTViewSnapshot *snapshot, NSError *error) { + [accum addObject:error]; + }]; + + NSError *testError = + [NSError errorWithDomain:@"com.google.firestore.test" code:42 userInfo:@{@"some" : @"info"}]; + [listener queryDidError:testError]; + + XCTAssertEqualObjects(accum, @[ testError ]); +} + +- (void)testRaisesEventForEmptyCollectionAfterSync { + NSMutableArray *accum = [NSMutableArray array]; + FSTQuery *query = FSTTestQuery(@"rooms"); + + FSTQueryListener *listener = [self listenToQuery:query accumulatingSnapshots:accum]; + + FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:[FSTDocumentKeySet keySet]]; + FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[], nil); + + FSTTargetChange *ackTarget = + [FSTTargetChange changeWithDocuments:@[] + currentStatusUpdate:FSTCurrentStatusUpdateMarkCurrent]; + FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[], ackTarget); + + [listener queryDidChangeViewSnapshot:snap1]; + XCTAssertEqualObjects(accum, @[]); + + [listener queryDidChangeViewSnapshot:snap2]; + XCTAssertEqualObjects(accum, @[ snap2 ]); +} + +- (void)testMutingAsyncListenerPreventsAllSubsequentEvents { + NSMutableArray *accum = [NSMutableArray array]; + + FSTQuery *query = FSTTestQuery(@"rooms/Eros"); + FSTDocument *doc1 = FSTTestDoc(@"rooms/Eros", 3, @{@"name" : @"Eros"}, NO); + FSTDocument *doc2 = FSTTestDoc(@"rooms/Eros", 4, @{@"name" : @"Eros2"}, NO); + + __block FSTAsyncQueryListener *listener = [[FSTAsyncQueryListener alloc] + initWithDispatchQueue:self.asyncQueue + snapshotHandler:^(FSTViewSnapshot *snapshot, NSError *error) { + [accum addObject:snapshot]; + [listener mute]; + }]; + + FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:[FSTDocumentKeySet keySet]]; + FSTViewSnapshot *viewSnapshot1 = FSTTestApplyChanges(view, @[ doc1 ], nil); + FSTViewSnapshot *viewSnapshot2 = FSTTestApplyChanges(view, @[ doc2 ], nil); + + FSTViewSnapshotHandler handler = listener.asyncSnapshotHandler; + handler(viewSnapshot1, nil); + handler(viewSnapshot2, nil); + + // Drain queue + XCTestExpectation *expectation = [self expectationWithDescription:@"Queue drained"]; + [self.asyncQueue dispatchAsync:^{ + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:4.0 + handler:^(NSError *_Nullable expectationError) { + if (expectationError) { + XCTFail(@"Error waiting for timeout: %@", expectationError); + } + }]; + + // We should get the first snapshot but not the second. + XCTAssertEqualObjects(accum, @[ viewSnapshot1 ]); +} + +- (void)testDoesNotRaiseEventsForMetadataChangesUnlessSpecified { + NSMutableArray *filteredAccum = [NSMutableArray array]; + NSMutableArray *fullAccum = [NSMutableArray array]; + + FSTQuery *query = FSTTestQuery(@"rooms"); + FSTDocument *doc1 = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, NO); + FSTDocument *doc2 = FSTTestDoc(@"rooms/Hades", 2, @{@"name" : @"Hades"}, NO); + + FSTListenOptions *options = [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:YES + includeDocumentMetadataChanges:NO + waitForSyncWhenOnline:NO]; + + FSTQueryListener *filteredListener = + [self listenToQuery:query accumulatingSnapshots:filteredAccum]; + FSTQueryListener *fullListener = + [self listenToQuery:query options:options accumulatingSnapshots:fullAccum]; + + FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:[FSTDocumentKeySet keySet]]; + FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1 ], nil); + + FSTTargetChange *ackTarget = + [FSTTargetChange changeWithDocuments:@[ doc1 ] + currentStatusUpdate:FSTCurrentStatusUpdateMarkCurrent]; + FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[], ackTarget); + FSTViewSnapshot *snap3 = FSTTestApplyChanges(view, @[ doc2 ], nil); + + [filteredListener queryDidChangeViewSnapshot:snap1]; // local event + [filteredListener queryDidChangeViewSnapshot:snap2]; // no event + [filteredListener queryDidChangeViewSnapshot:snap3]; // doc2 update + + [fullListener queryDidChangeViewSnapshot:snap1]; // local event + [fullListener queryDidChangeViewSnapshot:snap2]; // state change event + [fullListener queryDidChangeViewSnapshot:snap3]; // doc2 update + + XCTAssertEqualObjects(filteredAccum, (@[ snap1, snap3 ])); + XCTAssertEqualObjects(fullAccum, (@[ snap1, snap2, snap3 ])); +} + +- (void)testRaisesDocumentMetadataEventsOnlyWhenSpecified { + NSMutableArray *filteredAccum = [NSMutableArray array]; + NSMutableArray *fullAccum = [NSMutableArray array]; + + FSTQuery *query = FSTTestQuery(@"rooms"); + FSTDocument *doc1 = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, YES); + FSTDocument *doc2 = FSTTestDoc(@"rooms/Hades", 2, @{@"name" : @"Hades"}, NO); + FSTDocument *doc1Prime = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, NO); + FSTDocument *doc3 = FSTTestDoc(@"rooms/Other", 3, @{@"name" : @"Other"}, NO); + + FSTListenOptions *options = [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:NO + includeDocumentMetadataChanges:YES + waitForSyncWhenOnline:NO]; + + FSTQueryListener *filteredListener = + [self listenToQuery:query accumulatingSnapshots:filteredAccum]; + FSTQueryListener *fullListener = + [self listenToQuery:query options:options accumulatingSnapshots:fullAccum]; + + FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:[FSTDocumentKeySet keySet]]; + FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1, doc2 ], nil); + FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[ doc1Prime ], nil); + FSTViewSnapshot *snap3 = FSTTestApplyChanges(view, @[ doc3 ], nil); + + FSTDocumentViewChange *change1 = + [FSTDocumentViewChange changeWithDocument:doc1 type:FSTDocumentViewChangeTypeAdded]; + FSTDocumentViewChange *change2 = + [FSTDocumentViewChange changeWithDocument:doc2 type:FSTDocumentViewChangeTypeAdded]; + FSTDocumentViewChange *change3 = + [FSTDocumentViewChange changeWithDocument:doc1Prime type:FSTDocumentViewChangeTypeMetadata]; + FSTDocumentViewChange *change4 = + [FSTDocumentViewChange changeWithDocument:doc3 type:FSTDocumentViewChangeTypeAdded]; + + [filteredListener queryDidChangeViewSnapshot:snap1]; + [filteredListener queryDidChangeViewSnapshot:snap2]; + [filteredListener queryDidChangeViewSnapshot:snap3]; + [fullListener queryDidChangeViewSnapshot:snap1]; + [fullListener queryDidChangeViewSnapshot:snap2]; + [fullListener queryDidChangeViewSnapshot:snap3]; + + XCTAssertEqualObjects(filteredAccum, (@[ snap1, snap3 ])); + XCTAssertEqualObjects(filteredAccum[0].documentChanges, (@[ change1, change2 ])); + XCTAssertEqualObjects(filteredAccum[1].documentChanges, (@[ change4 ])); + + XCTAssertEqualObjects(fullAccum, (@[ snap1, snap2, snap3 ])); + XCTAssertEqualObjects(fullAccum[0].documentChanges, (@[ change1, change2 ])); + XCTAssertEqualObjects(fullAccum[1].documentChanges, (@[ change3 ])); + XCTAssertEqualObjects(fullAccum[2].documentChanges, (@[ change4 ])); +} + +- (void)testRaisesQueryMetadataEventsOnlyWhenHasPendingWritesOnTheQueryChanges { + NSMutableArray *fullAccum = [NSMutableArray array]; + + FSTQuery *query = FSTTestQuery(@"rooms"); + FSTDocument *doc1 = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, YES); + FSTDocument *doc2 = FSTTestDoc(@"rooms/Hades", 2, @{@"name" : @"Hades"}, YES); + FSTDocument *doc1Prime = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, NO); + FSTDocument *doc2Prime = FSTTestDoc(@"rooms/Hades", 2, @{@"name" : @"Hades"}, NO); + FSTDocument *doc3 = FSTTestDoc(@"rooms/Other", 3, @{@"name" : @"Other"}, NO); + + FSTListenOptions *options = [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:YES + includeDocumentMetadataChanges:NO + waitForSyncWhenOnline:NO]; + FSTQueryListener *fullListener = + [self listenToQuery:query options:options accumulatingSnapshots:fullAccum]; + + FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:[FSTDocumentKeySet keySet]]; + 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); + + [fullListener queryDidChangeViewSnapshot:snap1]; + [fullListener queryDidChangeViewSnapshot:snap2]; // Emits no events. + [fullListener queryDidChangeViewSnapshot:snap3]; + [fullListener queryDidChangeViewSnapshot:snap4]; // Metadata change event. + + FSTViewSnapshot *expectedSnap4 = [[FSTViewSnapshot alloc] initWithQuery:snap4.query + documents:snap4.documents + oldDocuments:snap3.documents + documentChanges:@[] + fromCache:snap4.fromCache + hasPendingWrites:NO + syncStateChanged:snap4.syncStateChanged]; + XCTAssertEqualObjects(fullAccum, (@[ snap1, snap3, expectedSnap4 ])); +} + +- (void)testMetadataOnlyDocumentChangesAreFilteredOutWhenIncludeDocumentMetadataChangesIsFalse { + NSMutableArray *filteredAccum = [NSMutableArray array]; + + FSTQuery *query = FSTTestQuery(@"rooms"); + FSTDocument *doc1 = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, YES); + FSTDocument *doc2 = FSTTestDoc(@"rooms/Hades", 2, @{@"name" : @"Hades"}, NO); + FSTDocument *doc1Prime = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, NO); + FSTDocument *doc3 = FSTTestDoc(@"rooms/Other", 3, @{@"name" : @"Other"}, NO); + + FSTQueryListener *filteredListener = + [self listenToQuery:query accumulatingSnapshots:filteredAccum]; + + FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:[FSTDocumentKeySet keySet]]; + FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1, doc2 ], nil); + FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[ doc1Prime, doc3 ], nil); + + FSTDocumentViewChange *change3 = + [FSTDocumentViewChange changeWithDocument:doc3 type:FSTDocumentViewChangeTypeAdded]; + + [filteredListener queryDidChangeViewSnapshot:snap1]; + [filteredListener queryDidChangeViewSnapshot:snap2]; + + FSTViewSnapshot *expectedSnap2 = [[FSTViewSnapshot alloc] initWithQuery:snap2.query + documents:snap2.documents + oldDocuments:snap1.documents + documentChanges:@[ change3 ] + fromCache:snap2.isFromCache + hasPendingWrites:snap2.hasPendingWrites + syncStateChanged:snap2.syncStateChanged]; + XCTAssertEqualObjects(filteredAccum, (@[ snap1, expectedSnap2 ])); +} + +- (void)testWillWaitForSyncIfOnline { + NSMutableArray *events = [NSMutableArray array]; + + FSTQuery *query = FSTTestQuery(@"rooms"); + FSTDocument *doc1 = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, NO); + FSTDocument *doc2 = FSTTestDoc(@"rooms/Hades", 2, @{@"name" : @"Hades"}, NO); + FSTQueryListener *listener = + [self listenToQuery:query + options:[[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:NO + includeDocumentMetadataChanges:NO + waitForSyncWhenOnline:YES] + accumulatingSnapshots:events]; + + FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:[FSTDocumentKeySet keySet]]; + FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1 ], nil); + FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[ doc2 ], nil); + FSTViewSnapshot *snap3 = + FSTTestApplyChanges(view, @[], + [FSTTargetChange changeWithDocuments:@[ doc1, doc2 ] + currentStatusUpdate:FSTCurrentStatusUpdateMarkCurrent]); + + [listener applyChangedOnlineState:FSTOnlineStateHealthy]; // no event + [listener queryDidChangeViewSnapshot:snap1]; + [listener applyChangedOnlineState:FSTOnlineStateUnknown]; + [listener applyChangedOnlineState:FSTOnlineStateHealthy]; + [listener queryDidChangeViewSnapshot:snap2]; + [listener queryDidChangeViewSnapshot:snap3]; + + FSTDocumentViewChange *change1 = + [FSTDocumentViewChange changeWithDocument:doc1 type:FSTDocumentViewChangeTypeAdded]; + FSTDocumentViewChange *change2 = + [FSTDocumentViewChange changeWithDocument:doc2 type:FSTDocumentViewChangeTypeAdded]; + FSTViewSnapshot *expectedSnap = [[FSTViewSnapshot alloc] + initWithQuery:snap3.query + documents:snap3.documents + oldDocuments:[FSTDocumentSet documentSetWithComparator:snap3.query.comparator] + documentChanges:@[ change1, change2 ] + fromCache:NO + hasPendingWrites:NO + syncStateChanged:YES]; + XCTAssertEqualObjects(events, (@[ expectedSnap ])); +} + +- (void)testWillRaiseInitialEventWhenGoingOffline { + NSMutableArray *events = [NSMutableArray array]; + + FSTQuery *query = FSTTestQuery(@"rooms"); + FSTDocument *doc1 = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, NO); + FSTDocument *doc2 = FSTTestDoc(@"rooms/Hades", 2, @{@"name" : @"Hades"}, NO); + FSTQueryListener *listener = + [self listenToQuery:query + options:[[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:NO + includeDocumentMetadataChanges:NO + waitForSyncWhenOnline:YES] + accumulatingSnapshots:events]; + + FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:[FSTDocumentKeySet keySet]]; + FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1 ], nil); + FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[ doc2 ], nil); + + [listener applyChangedOnlineState:FSTOnlineStateHealthy]; // no event + [listener queryDidChangeViewSnapshot:snap1]; // no event + [listener applyChangedOnlineState:FSTOnlineStateFailed]; // event + [listener applyChangedOnlineState:FSTOnlineStateUnknown]; // no event + [listener applyChangedOnlineState:FSTOnlineStateFailed]; // no event + [listener queryDidChangeViewSnapshot:snap2]; // another event + + FSTDocumentViewChange *change1 = + [FSTDocumentViewChange changeWithDocument:doc1 type:FSTDocumentViewChangeTypeAdded]; + FSTDocumentViewChange *change2 = + [FSTDocumentViewChange changeWithDocument:doc2 type:FSTDocumentViewChangeTypeAdded]; + FSTViewSnapshot *expectedSnap1 = [[FSTViewSnapshot alloc] + initWithQuery:query + documents:snap1.documents + oldDocuments:[FSTDocumentSet documentSetWithComparator:snap1.query.comparator] + documentChanges:@[ change1 ] + fromCache:YES + hasPendingWrites:NO + syncStateChanged:YES]; + FSTViewSnapshot *expectedSnap2 = [[FSTViewSnapshot alloc] initWithQuery:query + documents:snap2.documents + oldDocuments:snap1.documents + documentChanges:@[ change2 ] + fromCache:YES + hasPendingWrites:NO + syncStateChanged:NO]; + XCTAssertEqualObjects(events, (@[ expectedSnap1, expectedSnap2 ])); +} + +- (void)testWillRaiseInitialEventWhenGoingOfflineAndThereAreNoDocs { + NSMutableArray *events = [NSMutableArray array]; + + FSTQuery *query = FSTTestQuery(@"rooms"); + FSTQueryListener *listener = [self listenToQuery:query + options:[FSTListenOptions defaultOptions] + accumulatingSnapshots:events]; + + FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:[FSTDocumentKeySet keySet]]; + FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[], nil); + + [listener applyChangedOnlineState:FSTOnlineStateHealthy]; // no event + [listener queryDidChangeViewSnapshot:snap1]; // no event + [listener applyChangedOnlineState:FSTOnlineStateFailed]; // event + + FSTViewSnapshot *expectedSnap = [[FSTViewSnapshot alloc] + initWithQuery:query + documents:snap1.documents + oldDocuments:[FSTDocumentSet documentSetWithComparator:snap1.query.comparator] + documentChanges:@[] + fromCache:YES + hasPendingWrites:NO + syncStateChanged:YES]; + XCTAssertEqualObjects(events, (@[ expectedSnap ])); +} + +- (void)testWillRaiseInitialEventWhenStartingOfflineAndThereAreNoDocs { + NSMutableArray *events = [NSMutableArray array]; + + FSTQuery *query = FSTTestQuery(@"rooms"); + FSTQueryListener *listener = [self listenToQuery:query + options:[FSTListenOptions defaultOptions] + accumulatingSnapshots:events]; + + FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:[FSTDocumentKeySet keySet]]; + FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[], nil); + + [listener applyChangedOnlineState:FSTOnlineStateFailed]; // no event + [listener queryDidChangeViewSnapshot:snap1]; // event + + FSTViewSnapshot *expectedSnap = [[FSTViewSnapshot alloc] + initWithQuery:query + documents:snap1.documents + oldDocuments:[FSTDocumentSet documentSetWithComparator:snap1.query.comparator] + documentChanges:@[] + fromCache:YES + hasPendingWrites:NO + syncStateChanged:YES]; + XCTAssertEqualObjects(events, (@[ expectedSnap ])); +} + +- (FSTQueryListener *)listenToQuery:(FSTQuery *)query handler:(FSTViewSnapshotHandler)handler { + return [[FSTQueryListener alloc] initWithQuery:query + options:[FSTListenOptions defaultOptions] + viewSnapshotHandler:handler]; +} + +- (FSTQueryListener *)listenToQuery:(FSTQuery *)query + options:(FSTListenOptions *)options + accumulatingSnapshots:(NSMutableArray *)values { + return [[FSTQueryListener alloc] initWithQuery:query + options:options + viewSnapshotHandler:^(FSTViewSnapshot *snapshot, NSError *error) { + [values addObject:snapshot]; + }]; +} + +- (FSTQueryListener *)listenToQuery:(FSTQuery *)query + accumulatingSnapshots:(NSMutableArray *)values { + return [self listenToQuery:query + options:[FSTListenOptions defaultOptions] + accumulatingSnapshots:values]; +} + +@end + +NS_ASSUME_NONNULL_END -- cgit v1.2.3