Skip to content

Moving the StreamTest to integration tests #391

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Firestore/Example/Firestore.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; };
AFE6114F0D4DAECBA7B7C089 /* Pods_Firestore_IntegrationTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B2FA635DF5D116A67A7441CD /* Pods_Firestore_IntegrationTests.framework */; };
C4E749275AD0FBDF9F4716A8 /* Pods_SwiftBuildTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 32AD40BF6B0E849B07FFD05E /* Pods_SwiftBuildTest.framework */; };
D5B2532E4676014F57A7EAB9 /* FSTStreamTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D5B25C0D4AADFCA3ADB883E4 /* FSTStreamTests.m */; };
DE03B2C91F2149D600A30B9C /* FSTTransactionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1C61F0D48AC0013853F /* FSTTransactionTests.m */; };
DE03B2D41F2149D600A30B9C /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; };
DE03B2D51F2149D600A30B9C /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; };
Expand Down Expand Up @@ -119,7 +120,6 @@
DE51B1F31F0D491B0013853F /* FSTDatastoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1B31F0D48AC0013853F /* FSTDatastoreTests.m */; };
DE51B1F41F0D491B0013853F /* FSTRemoteEventTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1B41F0D48AC0013853F /* FSTRemoteEventTests.m */; };
DE51B1F61F0D491B0013853F /* FSTSerializerBetaTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1B61F0D48AC0013853F /* FSTSerializerBetaTests.m */; };
DE51B1F71F0D491B0013853F /* FSTStreamTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1B71F0D48AC0013853F /* FSTStreamTests.m */; };
DE51B1F81F0D491F0013853F /* FSTWatchChange+Testing.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1B91F0D48AC0013853F /* FSTWatchChange+Testing.m */; };
DE51B1F91F0D491F0013853F /* FSTWatchChangeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1BA1F0D48AC0013853F /* FSTWatchChangeTests.m */; };
DE51B1FA1F0D492C0013853F /* FSTLevelDBSpecTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1941F0D48AC0013853F /* FSTLevelDBSpecTests.m */; };
Expand Down Expand Up @@ -222,6 +222,7 @@
B2FA635DF5D116A67A7441CD /* Pods_Firestore_IntegrationTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_IntegrationTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CE00BABB5A3AAB44A4C209E2 /* Pods-Firestore_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests/Pods-Firestore_Tests.debug.xcconfig"; sourceTree = "<group>"; };
D3CC3DC5338DCAF43A211155 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
D5B25C0D4AADFCA3ADB883E4 /* FSTStreamTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSTStreamTests.m; sourceTree = "<group>"; };
DB17FEDFB80770611A935A60 /* Pods-Firestore_IntegrationTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_IntegrationTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_IntegrationTests/Pods-Firestore_IntegrationTests.release.xcconfig"; sourceTree = "<group>"; };
DE03B2E91F2149D600A30B9C /* Firestore_IntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Firestore_IntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
DE03B3621F215E1600A30B9C /* CAcert.pem */ = {isa = PBXFileReference; lastKnownFileType = text; path = CAcert.pem; sourceTree = "<group>"; };
Expand Down Expand Up @@ -292,7 +293,6 @@
DE51B1B31F0D48AC0013853F /* FSTDatastoreTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTDatastoreTests.m; sourceTree = "<group>"; };
DE51B1B41F0D48AC0013853F /* FSTRemoteEventTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTRemoteEventTests.m; sourceTree = "<group>"; };
DE51B1B61F0D48AC0013853F /* FSTSerializerBetaTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTSerializerBetaTests.m; sourceTree = "<group>"; };
DE51B1B71F0D48AC0013853F /* FSTStreamTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTStreamTests.m; sourceTree = "<group>"; };
DE51B1B81F0D48AC0013853F /* FSTWatchChange+Testing.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FSTWatchChange+Testing.h"; sourceTree = "<group>"; };
DE51B1B91F0D48AC0013853F /* FSTWatchChange+Testing.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "FSTWatchChange+Testing.m"; sourceTree = "<group>"; };
DE51B1BA1F0D48AC0013853F /* FSTWatchChangeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTWatchChangeTests.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -622,7 +622,6 @@
DE51B1B31F0D48AC0013853F /* FSTDatastoreTests.m */,
DE51B1B41F0D48AC0013853F /* FSTRemoteEventTests.m */,
DE51B1B61F0D48AC0013853F /* FSTSerializerBetaTests.m */,
DE51B1B71F0D48AC0013853F /* FSTStreamTests.m */,
DE51B1B81F0D48AC0013853F /* FSTWatchChange+Testing.h */,
DE51B1B91F0D48AC0013853F /* FSTWatchChange+Testing.m */,
DE51B1BA1F0D48AC0013853F /* FSTWatchChangeTests.m */,
Expand All @@ -639,6 +638,7 @@
DE51B1C51F0D48AC0013853F /* FSTSmokeTests.m */,
DE51B1C61F0D48AC0013853F /* FSTTransactionTests.m */,
DE51B1C71F0D48AC0013853F /* Util */,
D5B25C0D4AADFCA3ADB883E4 /* FSTStreamTests.m */,
);
path = Integration;
sourceTree = "<group>";
Expand Down Expand Up @@ -1162,7 +1162,6 @@
DE51B1E91F0D490D0013853F /* FSTLevelDBMutationQueueTests.mm in Sources */,
DE51B1E61F0D490D0013853F /* FSTRemoteDocumentCacheTests.m in Sources */,
DE51B1D91F0D490D0013853F /* FSTEagerGarbageCollectorTests.m in Sources */,
DE51B1F71F0D491B0013853F /* FSTStreamTests.m in Sources */,
DE51B1E21F0D490D0013853F /* FSTMutationQueueTests.m in Sources */,
DE51B1E81F0D490D0013853F /* FSTLevelDBKeyTests.mm in Sources */,
DE51B1E31F0D490D0013853F /* FSTPersistenceTestHelpers.m in Sources */,
Expand Down Expand Up @@ -1199,6 +1198,7 @@
DE03B2F21F214BAA00A30B9C /* FIRListenerRegistrationTests.m in Sources */,
DE03B2C91F2149D600A30B9C /* FSTTransactionTests.m in Sources */,
54DA12B11F315F3800DD57A1 /* FIRValidationTests.m in Sources */,
D5B2532E4676014F57A7EAB9 /* FSTStreamTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
261 changes: 261 additions & 0 deletions Firestore/Example/Tests/Integration/FSTStreamTests.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
/*
* 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 <XCTest/XCTest.h>

#import <Firestore/FIRFirestoreSettings.h>

#import "Auth/FSTEmptyCredentialsProvider.h"
#import "Core/FSTDatabaseInfo.h"
#import "FSTHelpers.h"
#import "FSTIntegrationTestCase.h"
#import "Model/FSTDatabaseID.h"
#import "Remote/FSTDatastore.h"
#import "Util/FSTAssert.h"
#import "Util/FSTDispatchQueue.h"

/** Exposes otherwise private methods for testing. */
@interface FSTStream (Testing)
- (void)writesFinishedWithError:(NSError *_Nullable)error;
@end

/**
* Implements FSTWatchStreamDelegate and FSTWriteStreamDelegate and supports waiting on callbacks
* via `fulfillOnCallback`.
*/
@interface FSTStreamStatusDelegate : NSObject <FSTWatchStreamDelegate, FSTWriteStreamDelegate>

- (instancetype)initWithTestCase:(XCTestCase *)testCase
queue:(FSTDispatchQueue *)dispatchQueue NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;

@property(nonatomic, weak, readonly) XCTestCase *testCase;
@property(nonatomic, strong, readonly) FSTDispatchQueue *dispatchQueue;
@property(nonatomic, readonly) NSMutableArray<NSString *> *states;
@property(nonatomic, strong) XCTestExpectation *expectation;

@end

@implementation FSTStreamStatusDelegate

- (instancetype)initWithTestCase:(XCTestCase *)testCase queue:(FSTDispatchQueue *)dispatchQueue {
if (self = [super init]) {
_testCase = testCase;
_dispatchQueue = dispatchQueue;
_states = [NSMutableArray new];
}

return self;
}

- (void)watchStreamDidOpen {
[_states addObject:@"watchStreamDidOpen"];
[_expectation fulfill];
_expectation = nil;
}

- (void)writeStreamDidOpen {
[_states addObject:@"writeStreamDidOpen"];
[_expectation fulfill];
_expectation = nil;
}

- (void)writeStreamDidCompleteHandshake {
[_states addObject:@"writeStreamDidCompleteHandshake"];
[_expectation fulfill];
_expectation = nil;
}

- (void)writeStreamDidClose:(NSError *_Nullable)error {
[_states addObject:@"writeStreamDidClose"];
[_expectation fulfill];
_expectation = nil;
}

- (void)watchStreamDidClose:(NSError *_Nullable)error {
[_states addObject:@"watchStreamDidClose"];
[_expectation fulfill];
_expectation = nil;
}

- (void)watchStreamDidChange:(FSTWatchChange *)change
snapshotVersion:(FSTSnapshotVersion *)snapshotVersion {
[_states addObject:@"watchStreamDidChange"];
[_expectation fulfill];
_expectation = nil;
}

- (void)writeStreamDidReceiveResponseWithVersion:(FSTSnapshotVersion *)commitVersion
mutationResults:(NSArray<FSTMutationResult *> *)results {
[_states addObject:@"writeStreamDidReceiveResponseWithVersion"];
[_expectation fulfill];
_expectation = nil;
}

/**
* Executes 'block' using the provided FSTDispatchQueue and waits for any callback on this delegate
* to be called.
*/
- (void)awaitNotificationFromBlock:(void (^)(void))block {
FSTAssert(_expectation == nil, @"Previous expectation still active");
XCTestExpectation *expectation =
[self.testCase expectationWithDescription:@"awaitCallbackInBlock"];
_expectation = expectation;
[self.dispatchQueue dispatchAsync:block];
[self.testCase awaitExpectations];
}

@end

@interface FSTStreamTests : XCTestCase

@end

@implementation FSTStreamTests {
dispatch_queue_t _testQueue;
FSTDatabaseInfo *_databaseInfo;
FSTEmptyCredentialsProvider *_credentials;
FSTStreamStatusDelegate *_delegate;
FSTDispatchQueue *_workerDispatchQueue;

/** Single mutation to send to the write stream. */
NSArray<FSTMutation *> *_mutations;
}

- (void)setUp {
[super setUp];

_mutations = @[ FSTTestSetMutation(@"foo/bar", @{}) ];

FIRFirestoreSettings *settings = [FSTIntegrationTestCase settings];
FSTDatabaseID *databaseID =
[FSTDatabaseID databaseIDWithProject:[FSTIntegrationTestCase projectID]
database:kDefaultDatabaseID];

_databaseInfo = [FSTDatabaseInfo databaseInfoWithDatabaseID:databaseID
persistenceKey:@"test-key"
host:settings.host
sslEnabled:settings.sslEnabled];
_testQueue = dispatch_queue_create("FSTStreamTestWorkerQueue", DISPATCH_QUEUE_SERIAL);
_workerDispatchQueue = [FSTDispatchQueue queueWith:_testQueue];
_credentials = [[FSTEmptyCredentialsProvider alloc] init];
}

- (FSTWriteStream *)setUpWriteStream {
FSTDatastore *datastore = [[FSTDatastore alloc] initWithDatabaseInfo:_databaseInfo
workerDispatchQueue:_workerDispatchQueue
credentials:_credentials];

_delegate = [[FSTStreamStatusDelegate alloc] initWithTestCase:self queue:_workerDispatchQueue];
return [datastore createWriteStreamWithDelegate:_delegate];
}

- (FSTWatchStream *)setUpWatchStream {
FSTDatastore *datastore = [[FSTDatastore alloc] initWithDatabaseInfo:_databaseInfo
workerDispatchQueue:_workerDispatchQueue
credentials:_credentials];

_delegate = [[FSTStreamStatusDelegate alloc] initWithTestCase:self queue:_workerDispatchQueue];
return [datastore createWatchStreamWithDelegate:_delegate];
}

/**
* Drains the test queue and asserts that all the observed callbacks (up to this point) match
* 'expectedStates'. Clears the list of observed callbacks on completion.
*/
- (void)verifyDelegateObservedStates:(NSArray<NSString *> *)expectedStates {
// Drain queue
dispatch_sync(_testQueue, ^{
});

XCTAssertEqualObjects(_delegate.states, expectedStates);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this also remove the states accumulated in the delegate so that subsequent assertions only need to assert about the new states?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, we can do this here, but it's not technically needed since delegate is scoped to the test.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delegate is scoped to the test but not clearing this means that you can't reasonably make more than one assertion. I'm also fine just leaving this alone until we need to do that, but just wanted the intent of my comment to be clear.

[_delegate.states removeAllObjects];
}

/** Verifies that the watch stream does not issue an onClose callback after a call to stop(). */
- (void)testWatchStreamStopBeforeHandshake {
FSTWatchStream *watchStream = [self setUpWatchStream];

[_delegate awaitNotificationFromBlock:^{
[watchStream start];
}];

// Stop must not call watchStreamDidClose because the full implementation of the delegate could
// attempt to restart the stream in the event it had pending watches.
[_workerDispatchQueue dispatchAsync:^{
[watchStream stop];
}];

// Simulate a final callback from GRPC
[watchStream writesFinishedWithError:nil];

[self verifyDelegateObservedStates:@[ @"watchStreamDidOpen" ]];
}

/** Verifies that the write stream does not issue an onClose callback after a call to stop(). */
- (void)testWriteStreamStopBeforeHandshake {
FSTWriteStream *writeStream = [self setUpWriteStream];

[_delegate awaitNotificationFromBlock:^{
[writeStream start];
}];

// Don't start the handshake.

// Stop must not call watchStreamDidClose because the full implementation of the delegate could
// attempt to restart the stream in the event it had pending watches.
[_workerDispatchQueue dispatchAsync:^{
[writeStream stop];
}];

// Simulate a final callback from GRPC
[writeStream writesFinishedWithError:nil];

[self verifyDelegateObservedStates:@[ @"writeStreamDidOpen" ]];
}

- (void)testWriteStreamStopAfterHandshake {
FSTWriteStream *writeStream = [self setUpWriteStream];

[_delegate awaitNotificationFromBlock:^{
[writeStream start];
}];

// Writing before the handshake should throw
dispatch_sync(_testQueue, ^{
XCTAssertThrows([writeStream writeMutations:_mutations]);
});

[_delegate awaitNotificationFromBlock:^{
[writeStream writeHandshake];
}];

// Now writes should succeed
[_delegate awaitNotificationFromBlock:^{
[writeStream writeMutations:_mutations];
}];

[_workerDispatchQueue dispatchAsync:^{
[writeStream stop];
}];

[self verifyDelegateObservedStates:@[
@"writeStreamDidOpen", @"writeStreamDidCompleteHandshake",
@"writeStreamDidReceiveResponseWithVersion"
]];
}

@end
Loading