mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-10 22:45:59 +05:45
* lazy replacing FST(Firebase)CredentialsProvider by (Firebase)CredentialsProvider * lazy replacing FSTUser by User * adding error-code parameter to TokenListener * actually use const user& instead of pointer; also add an error util * add HashUser and pass into the unordered_map * use User in test * use c++ CredentialsProvider and subclass in test * fix unit test * use explicit capture in lambda instead of capture all by reference * cache currentUser explicitly when reset sync engineer test driver * objc object should be captured by value in lambda * replacing Auth/FSTUser by C++ auth implementation * address changes * replacing FSTGetTokenResult by C++ Token implementation * address changes * fix unintentional change in merging * patch the change in objc Auth up-stream * somehow, the lambda-version of set-user-change-listener does not work... fallback to block * address changes * fix another const& v.s. dispatch bug * fix more const& v.s. dispatch bug zxu123 committed * fix a bad sync line * address changes * address change * address change * fix upstream change from merge * fix upstream changes * Suggested fixes for cpp/port_auth (#846) * Get rid of MockDatastore factory This avoids the need to statically allocate (and leak) a credentials provider * Use absl::make_unique std::make_unique technically does not exist until C++14. * #include <utility> for std::move * Use std::future for the initial user * fix style
325 lines
11 KiB
Plaintext
325 lines
11 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/Example/Tests/Util/FSTIntegrationTestCase.h"
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#import <FirebaseCore/FIRLogger.h>
|
|
#import <FirebaseFirestore/FirebaseFirestore-umbrella.h>
|
|
#import <GRPCClient/GRPCCall+ChannelArg.h>
|
|
#import <GRPCClient/GRPCCall+Tests.h>
|
|
|
|
#include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h"
|
|
#include "Firestore/core/src/firebase/firestore/model/database_id.h"
|
|
#include "Firestore/core/src/firebase/firestore/util/autoid.h"
|
|
#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
|
|
#include "absl/memory/memory.h"
|
|
|
|
#import "Firestore/Source/API/FIRFirestore+Internal.h"
|
|
#import "Firestore/Source/Core/FSTFirestoreClient.h"
|
|
#import "Firestore/Source/Local/FSTLevelDB.h"
|
|
#import "Firestore/Source/Util/FSTDispatchQueue.h"
|
|
|
|
#import "Firestore/Example/Tests/Util/FSTEventAccumulator.h"
|
|
|
|
namespace util = firebase::firestore::util;
|
|
using firebase::firestore::auth::CredentialsProvider;
|
|
using firebase::firestore::auth::EmptyCredentialsProvider;
|
|
using firebase::firestore::model::DatabaseId;
|
|
using firebase::firestore::util::CreateAutoId;
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
@interface FIRFirestore (Testing)
|
|
@property(nonatomic, strong) FSTDispatchQueue *workerDispatchQueue;
|
|
@end
|
|
|
|
@implementation FSTIntegrationTestCase {
|
|
NSMutableArray<FIRFirestore *> *_firestores;
|
|
}
|
|
|
|
- (void)setUp {
|
|
[super setUp];
|
|
|
|
[self clearPersistence];
|
|
|
|
_firestores = [NSMutableArray array];
|
|
self.db = [self firestore];
|
|
self.eventAccumulator = [FSTEventAccumulator accumulatorForTest:self];
|
|
}
|
|
|
|
- (void)tearDown {
|
|
@try {
|
|
for (FIRFirestore *firestore in _firestores) {
|
|
[self shutdownFirestore:firestore];
|
|
}
|
|
} @finally {
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
[GRPCCall closeOpenConnections];
|
|
#pragma clang diagnostic pop
|
|
_firestores = nil;
|
|
[super tearDown];
|
|
}
|
|
}
|
|
|
|
- (void)clearPersistence {
|
|
NSString *levelDBDir = [FSTLevelDB documentsDirectory];
|
|
NSError *error;
|
|
if (![[NSFileManager defaultManager] removeItemAtPath:levelDBDir error:&error]) {
|
|
// file not found is okay.
|
|
XCTAssertTrue(
|
|
[error.domain isEqualToString:NSCocoaErrorDomain] && error.code == NSFileNoSuchFileError,
|
|
@"Failed to clear LevelDB Persistence: %@", error);
|
|
}
|
|
}
|
|
|
|
- (FIRFirestore *)firestore {
|
|
return [self firestoreWithProjectID:[FSTIntegrationTestCase projectID]];
|
|
}
|
|
|
|
+ (NSString *)projectID {
|
|
NSString *project = [[NSProcessInfo processInfo] environment][@"PROJECT_ID"];
|
|
if (!project) {
|
|
project = @"test-db";
|
|
}
|
|
return project;
|
|
}
|
|
|
|
+ (FIRFirestoreSettings *)settings {
|
|
FIRFirestoreSettings *settings = [[FIRFirestoreSettings alloc] init];
|
|
NSString *host = [[NSProcessInfo processInfo] environment][@"DATASTORE_HOST"];
|
|
settings.sslEnabled = YES;
|
|
if (!host) {
|
|
// If host is nil, there is no GoogleService-Info.plist. Check if a hexa integration test
|
|
// configuration is configured. The first bundle location is used by bazel builds. The
|
|
// second is used for github clones.
|
|
host = @"localhost:8081";
|
|
settings.sslEnabled = YES;
|
|
NSString *certsPath =
|
|
[[NSBundle mainBundle] pathForResource:@"PlugIns/IntegrationTests.xctest/CAcert"
|
|
ofType:@"pem"];
|
|
if (certsPath == nil) {
|
|
certsPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"CAcert" ofType:@"pem"];
|
|
}
|
|
unsigned long long fileSize =
|
|
[[[NSFileManager defaultManager] attributesOfItemAtPath:certsPath error:nil] fileSize];
|
|
|
|
if (fileSize == 0) {
|
|
NSLog(
|
|
@"The cert is not properly configured. Make sure setup_integration_tests.py "
|
|
"has been run.");
|
|
}
|
|
[GRPCCall useTestCertsPath:certsPath testName:@"test_cert_2" forHost:host];
|
|
}
|
|
settings.host = host;
|
|
settings.persistenceEnabled = YES;
|
|
NSLog(@"Configured integration test for %@ with SSL: %@", settings.host,
|
|
settings.sslEnabled ? @"YES" : @"NO");
|
|
return settings;
|
|
}
|
|
|
|
- (FIRFirestore *)firestoreWithProjectID:(NSString *)projectID {
|
|
NSString *persistenceKey = [NSString stringWithFormat:@"db%lu", (unsigned long)_firestores.count];
|
|
|
|
FSTDispatchQueue *workerDispatchQueue = [FSTDispatchQueue
|
|
queueWith:dispatch_queue_create("com.google.firebase.firestore", DISPATCH_QUEUE_SERIAL)];
|
|
|
|
FIRSetLoggerLevel(FIRLoggerLevelDebug);
|
|
// HACK: FIRFirestore expects a non-nil app, but for tests we cheat.
|
|
FIRApp *app = nil;
|
|
std::unique_ptr<CredentialsProvider> credentials_provider =
|
|
absl::make_unique<firebase::firestore::auth::EmptyCredentialsProvider>();
|
|
|
|
FIRFirestore *firestore = [[FIRFirestore alloc] initWithProjectID:util::MakeStringView(projectID)
|
|
database:DatabaseId::kDefault
|
|
persistenceKey:persistenceKey
|
|
credentialsProvider:std::move(credentials_provider)
|
|
workerDispatchQueue:workerDispatchQueue
|
|
firebaseApp:app];
|
|
|
|
firestore.settings = [FSTIntegrationTestCase settings];
|
|
|
|
[_firestores addObject:firestore];
|
|
return firestore;
|
|
}
|
|
|
|
- (void)shutdownFirestore:(FIRFirestore *)firestore {
|
|
[firestore shutdownWithCompletion:[self completionForExpectationWithName:@"shutdown"]];
|
|
[self awaitExpectations];
|
|
}
|
|
|
|
- (NSString *)documentPath {
|
|
std::string autoId = CreateAutoId();
|
|
return [NSString stringWithFormat:@"test-collection/%s", autoId.c_str()];
|
|
}
|
|
|
|
- (FIRDocumentReference *)documentRef {
|
|
return [self.db documentWithPath:[self documentPath]];
|
|
}
|
|
|
|
- (FIRCollectionReference *)collectionRef {
|
|
std::string autoId = CreateAutoId();
|
|
NSString *collectionName = [NSString stringWithFormat:@"test-collection-%s", autoId.c_str()];
|
|
return [self.db collectionWithPath:collectionName];
|
|
}
|
|
|
|
- (FIRCollectionReference *)collectionRefWithDocuments:
|
|
(NSDictionary<NSString *, NSDictionary<NSString *, id> *> *)documents {
|
|
FIRCollectionReference *collection = [self collectionRef];
|
|
// Use a different instance to write the documents
|
|
[self writeAllDocuments:documents
|
|
toCollection:[[self firestore] collectionWithPath:collection.path]];
|
|
return collection;
|
|
}
|
|
|
|
- (void)writeAllDocuments:(NSDictionary<NSString *, NSDictionary<NSString *, id> *> *)documents
|
|
toCollection:(FIRCollectionReference *)collection {
|
|
[documents enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSDictionary<NSString *, id> *value,
|
|
BOOL *stop) {
|
|
FIRDocumentReference *ref = [collection documentWithPath:key];
|
|
[self writeDocumentRef:ref data:value];
|
|
}];
|
|
}
|
|
|
|
- (void)readerAndWriterOnDocumentRef:(void (^)(NSString *path,
|
|
FIRDocumentReference *readerRef,
|
|
FIRDocumentReference *writerRef))action {
|
|
FIRFirestore *reader = self.db; // for clarity
|
|
FIRFirestore *writer = [self firestore];
|
|
|
|
NSString *path = [self documentPath];
|
|
FIRDocumentReference *readerRef = [reader documentWithPath:path];
|
|
FIRDocumentReference *writerRef = [writer documentWithPath:path];
|
|
action(path, readerRef, writerRef);
|
|
}
|
|
|
|
- (FIRDocumentSnapshot *)readDocumentForRef:(FIRDocumentReference *)ref {
|
|
__block FIRDocumentSnapshot *result;
|
|
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"getData"];
|
|
[ref getDocumentWithCompletion:^(FIRDocumentSnapshot *doc, NSError *_Nullable error) {
|
|
XCTAssertNil(error);
|
|
result = doc;
|
|
[expectation fulfill];
|
|
}];
|
|
[self awaitExpectations];
|
|
|
|
return result;
|
|
}
|
|
|
|
- (FIRQuerySnapshot *)readDocumentSetForRef:(FIRQuery *)query {
|
|
__block FIRQuerySnapshot *result;
|
|
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"getData"];
|
|
[query getDocumentsWithCompletion:^(FIRQuerySnapshot *documentSet, NSError *error) {
|
|
XCTAssertNil(error);
|
|
result = documentSet;
|
|
[expectation fulfill];
|
|
}];
|
|
[self awaitExpectations];
|
|
|
|
return result;
|
|
}
|
|
|
|
- (FIRDocumentSnapshot *)readSnapshotForRef:(FIRDocumentReference *)ref
|
|
requireOnline:(BOOL)requireOnline {
|
|
__block FIRDocumentSnapshot *result;
|
|
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"listener"];
|
|
id<FIRListenerRegistration> listener = [ref
|
|
addSnapshotListenerWithOptions:[[FIRDocumentListenOptions options] includeMetadataChanges:YES]
|
|
listener:^(FIRDocumentSnapshot *snapshot, NSError *error) {
|
|
XCTAssertNil(error);
|
|
if (!requireOnline || !snapshot.metadata.fromCache) {
|
|
result = snapshot;
|
|
[expectation fulfill];
|
|
}
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
[listener remove];
|
|
|
|
return result;
|
|
}
|
|
|
|
- (void)writeDocumentRef:(FIRDocumentReference *)ref data:(NSDictionary<NSString *, id> *)data {
|
|
[ref setData:data completion:[self completionForExpectationWithName:@"setData"]];
|
|
[self awaitExpectations];
|
|
}
|
|
|
|
- (void)updateDocumentRef:(FIRDocumentReference *)ref data:(NSDictionary<id, id> *)data {
|
|
[ref updateData:data completion:[self completionForExpectationWithName:@"updateData"]];
|
|
[self awaitExpectations];
|
|
}
|
|
|
|
- (void)deleteDocumentRef:(FIRDocumentReference *)ref {
|
|
[ref deleteDocumentWithCompletion:[self completionForExpectationWithName:@"deleteDocument"]];
|
|
[self awaitExpectations];
|
|
}
|
|
|
|
- (void)disableNetwork {
|
|
[self.db.client
|
|
disableNetworkWithCompletion:[self completionForExpectationWithName:@"Disable Network."]];
|
|
[self awaitExpectations];
|
|
}
|
|
|
|
- (void)enableNetwork {
|
|
[self.db.client
|
|
enableNetworkWithCompletion:[self completionForExpectationWithName:@"Enable Network."]];
|
|
[self awaitExpectations];
|
|
}
|
|
|
|
- (FSTDispatchQueue *)queueForFirestore:(FIRFirestore *)firestore {
|
|
return firestore.workerDispatchQueue;
|
|
}
|
|
|
|
- (void)waitUntil:(BOOL (^)())predicate {
|
|
NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
|
|
double waitSeconds = [self defaultExpectationWaitSeconds];
|
|
while (!predicate() && ([NSDate timeIntervalSinceReferenceDate] - start < waitSeconds)) {
|
|
// This waits for the next event or until the 100ms timeout is reached
|
|
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
|
|
beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
|
}
|
|
if (!predicate()) {
|
|
XCTFail(@"Timeout");
|
|
}
|
|
}
|
|
|
|
extern "C" NSArray<NSDictionary<NSString *, id> *> *FIRQuerySnapshotGetData(
|
|
FIRQuerySnapshot *docs) {
|
|
NSMutableArray<NSDictionary<NSString *, id> *> *result = [NSMutableArray array];
|
|
for (FIRDocumentSnapshot *doc in docs.documents) {
|
|
[result addObject:doc.data];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
extern "C" NSArray<NSString *> *FIRQuerySnapshotGetIDs(FIRQuerySnapshot *docs) {
|
|
NSMutableArray<NSString *> *result = [NSMutableArray array];
|
|
for (FIRDocumentSnapshot *doc in docs.documents) {
|
|
[result addObject:doc.documentID];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@end
|
|
|
|
NS_ASSUME_NONNULL_END
|