diff --git a/firestore/integration_test_internal/src/firestore_integration_test.cc b/firestore/integration_test_internal/src/firestore_integration_test.cc index df1854044..e95db2551 100644 --- a/firestore/integration_test_internal/src/firestore_integration_test.cc +++ b/firestore/integration_test_internal/src/firestore_integration_test.cc @@ -116,11 +116,19 @@ Firestore* FirestoreIntegrationTest::TestFirestore( return TestFirestoreWithProjectId(name, /*project_id=*/""); } +Firestore* FirestoreIntegrationTest::TestFirestoreWithDatabaseId( + const std::string& name, const std::string& database_id) const { + return TestFirestoreWithProjectId(name, /*project_id=*/"", database_id); +} + Firestore* FirestoreIntegrationTest::TestFirestoreWithProjectId( - const std::string& name, const std::string& project_id) const { + const std::string& name, + const std::string& project_id, + const std::string& database_id) const { for (const auto& entry : firestores_) { const FirestoreInfo& firestore_info = entry.second; - if (firestore_info.name() == name) { + if (firestore_info.name() == name && + firestore_info.database_id() == database_id) { return firestore_info.firestore(); } } @@ -129,9 +137,9 @@ Firestore* FirestoreIntegrationTest::TestFirestoreWithProjectId( if (apps_.find(app) == apps_.end()) { apps_[app] = std::unique_ptr(app); } - - Firestore* db = new Firestore(CreateTestFirestoreInternal(app)); - firestores_[db] = FirestoreInfo(name, std::unique_ptr(db)); + Firestore* db = new Firestore(CreateTestFirestoreInternal(app, database_id)); + firestores_[db] = + FirestoreInfo(name, database_id, std::unique_ptr(db)); firestore::LocateEmulator(db); return db; diff --git a/firestore/integration_test_internal/src/firestore_integration_test.h b/firestore/integration_test_internal/src/firestore_integration_test.h index 249fbe3b0..71010583f 100644 --- a/firestore/integration_test_internal/src/firestore_integration_test.h +++ b/firestore/integration_test_internal/src/firestore_integration_test.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "app/meta/move.h" @@ -49,7 +50,8 @@ const int kCheckIntervalMillis = 100; // The timeout of waiting for a Future or a listener. const int kTimeOutMillis = 15000; -FirestoreInternal* CreateTestFirestoreInternal(App* app); +FirestoreInternal* CreateTestFirestoreInternal( + App* app, const std::string& database_id = kDefaultDatabase); App* GetApp(); App* GetApp(const char* name, const std::string& override_project_id); @@ -252,9 +254,16 @@ class FirestoreIntegrationTest : public testing::Test { Firestore* TestFirestore(const std::string& name = kDefaultAppName) const; // Returns a Firestore instance for an app with the given `name`, associated - // with the database with the given `project_id`. - Firestore* TestFirestoreWithProjectId(const std::string& name, - const std::string& project_id) const; + // with the database with the given `project_id` and default `database_id`. + Firestore* TestFirestoreWithProjectId( + const std::string& name, + const std::string& project_id, + const std::string& database_id = kDefaultDatabase) const; + + // Returns a Firestore instance for an app with the given `name`, associated + // with the database with the given `database_id`. + Firestore* TestFirestoreWithDatabaseId(const std::string& name, + const std::string& database_id) const; // Deletes the given `Firestore` instance, which must have been returned by a // previous invocation of `TestFirestore()`, and removes it from the cache of @@ -419,15 +428,22 @@ class FirestoreIntegrationTest : public testing::Test { class FirestoreInfo { public: FirestoreInfo() = default; - FirestoreInfo(const std::string& name, std::unique_ptr firestore) - : name_(name), firestore_(std::move(firestore)) {} + FirestoreInfo(const std::string& name, + const std::string& database_id, + std::unique_ptr firestore) + : name_(name), + database_id_(database_id), + firestore_(std::move(firestore)) {} const std::string& name() const { return name_; } Firestore* firestore() const { return firestore_.get(); } + const std::string& database_id() const { return database_id_; } + void ReleaseFirestore() { firestore_.release(); } private: std::string name_; + std::string database_id_; std::unique_ptr firestore_; }; diff --git a/firestore/integration_test_internal/src/firestore_test.cc b/firestore/integration_test_internal/src/firestore_test.cc index 0569448f7..b82c3aa63 100644 --- a/firestore/integration_test_internal/src/firestore_test.cc +++ b/firestore/integration_test_internal/src/firestore_test.cc @@ -23,7 +23,9 @@ #if defined(__ANDROID__) #include "android/firestore_integration_test_android.h" +#include "firestore/src/android/converter_android.h" #include "firestore/src/android/exception_android.h" +#include "firestore/src/android/firestore_android.h" #include "firestore/src/android/jni_runnable_android.h" #include "firestore/src/jni/env.h" #include "firestore/src/jni/ownership.h" @@ -40,11 +42,14 @@ #include "util/future_test_util.h" #if !defined(__ANDROID__) #include "Firestore/core/src/util/autoid.h" +#include "firestore/src/main/converter_main.h" +#include "firestore/src/main/firestore_main.h" #else #include "android/util_autoid.h" #endif // !defined(__ANDROID__) #include "Firestore/core/src/util/firestore_exceptions.h" #include "firebase_test_framework.h" +#include "util/locate_emulator.h" // These test cases are in sync with native iOS client SDK test // Firestore/Example/Tests/Integration/API/FIRDatabaseTests.mm @@ -60,7 +65,14 @@ using ::firebase::auth::Auth; using ::testing::ContainerEq; using ::testing::HasSubstr; -TEST_F(FirestoreIntegrationTest, GetInstance) { +class FirestoreTest : public FirestoreIntegrationTest { + protected: + const std::string& GetFirestoreDatabaseId(Firestore* firestore) { + return GetInternal(firestore)->database_name(); + } +}; + +TEST_F(FirestoreTest, GetInstance) { // Create App. App* app = this->app(); EXPECT_NE(nullptr, app); @@ -87,8 +99,22 @@ TEST_F(FirestoreIntegrationTest, GetInstance) { delete auth; } +TEST_F(FirestoreTest, GetInstanceWithDynamicDatabaseId) { + App* app = this->app(); + EXPECT_NE(nullptr, app); + + InitResult init_result; + auto instance = std::unique_ptr( + Firestore::GetInstance(app, "foo", &init_result)); + ASSERT_EQ(kInitResultSuccess, init_result); + + EXPECT_NE(nullptr, instance); + EXPECT_EQ(app, instance->app()); + EXPECT_EQ(GetFirestoreDatabaseId(instance.get()), "foo"); +} + // Sanity test for stubs. -TEST_F(FirestoreIntegrationTest, TestCanCreateCollectionAndDocumentReferences) { +TEST_F(FirestoreTest, TestCanCreateCollectionAndDocumentReferences) { Firestore* db = TestFirestore(); CollectionReference c = db->Collection("a/b/c").Document("d").Parent(); DocumentReference d = db->Document("a/b").Collection("c/d/e").Parent(); @@ -102,7 +128,7 @@ TEST_F(FirestoreIntegrationTest, TestCanCreateCollectionAndDocumentReferences) { // If any of these assert, the test will fail. } -TEST_F(FirestoreIntegrationTest, TestCanReadNonExistentDocuments) { +TEST_F(FirestoreTest, TestCanReadNonExistentDocuments) { DocumentReference doc = Collection("rooms").Document(); DocumentSnapshot snap = ReadDocument(doc); @@ -110,7 +136,7 @@ TEST_F(FirestoreIntegrationTest, TestCanReadNonExistentDocuments) { EXPECT_THAT(snap.GetData(), ContainerEq(MapFieldValue())); } -TEST_F(FirestoreIntegrationTest, TestCanUpdateAnExistingDocument) { +TEST_F(FirestoreTest, TestCanUpdateAnExistingDocument) { DocumentReference document = Collection("rooms").Document("eros"); Await(document.Set(MapFieldValue{ {"desc", FieldValue::String("Description")}, @@ -131,7 +157,7 @@ TEST_F(FirestoreIntegrationTest, TestCanUpdateAnExistingDocument) { {"email", FieldValue::String("new@xyz.com")}})}})); } -TEST_F(FirestoreIntegrationTest, TestCanUpdateAnUnknownDocument) { +TEST_F(FirestoreTest, TestCanUpdateAnUnknownDocument) { DocumentReference writer_reference = TestFirestore("writer")->Collection("collection").Document(); DocumentReference reader_reference = TestFirestore("reader") @@ -165,7 +191,7 @@ TEST_F(FirestoreIntegrationTest, TestCanUpdateAnUnknownDocument) { EXPECT_FALSE(reader_snapshot.metadata().is_from_cache()); } -TEST_F(FirestoreIntegrationTest, TestCanOverwriteAnExistingDocumentUsingSet) { +TEST_F(FirestoreTest, TestCanOverwriteAnExistingDocumentUsingSet) { DocumentReference document = Collection("rooms").Document(); Await(document.Set(MapFieldValue{ {"desc", FieldValue::String("Description")}, @@ -186,8 +212,7 @@ TEST_F(FirestoreIntegrationTest, TestCanOverwriteAnExistingDocumentUsingSet) { FieldValue::Map({{"name", FieldValue::String("Sebastian")}})}})); } -TEST_F(FirestoreIntegrationTest, - TestCanMergeDataWithAnExistingDocumentUsingSet) { +TEST_F(FirestoreTest, TestCanMergeDataWithAnExistingDocumentUsingSet) { DocumentReference document = Collection("rooms").Document("eros"); Await(document.Set(MapFieldValue{ {"desc", FieldValue::String("Description")}, @@ -212,7 +237,7 @@ TEST_F(FirestoreIntegrationTest, {"email", FieldValue::String("abc@xyz.com")}})}})); } -TEST_F(FirestoreIntegrationTest, TestCanMergeServerTimestamps) { +TEST_F(FirestoreTest, TestCanMergeServerTimestamps) { DocumentReference document = Collection("rooms").Document("eros"); Await(document.Set(MapFieldValue{{"untouched", FieldValue::Boolean(true)}})); Await(document.Set( @@ -226,7 +251,7 @@ TEST_F(FirestoreIntegrationTest, TestCanMergeServerTimestamps) { EXPECT_TRUE(snapshot.Get("nested.time").is_timestamp()); } -TEST_F(FirestoreIntegrationTest, TestCanMergeEmptyObject) { +TEST_F(FirestoreTest, TestCanMergeEmptyObject) { DocumentReference document = Document(); EventAccumulator accumulator; ListenerRegistration registration = @@ -262,7 +287,7 @@ TEST_F(FirestoreIntegrationTest, TestCanMergeEmptyObject) { registration.Remove(); } -TEST_F(FirestoreIntegrationTest, TestCanDeleteFieldUsingMerge) { +TEST_F(FirestoreTest, TestCanDeleteFieldUsingMerge) { DocumentReference document = Collection("rooms").Document("eros"); Await(document.Set(MapFieldValue{ {"untouched", FieldValue::Boolean(true)}, @@ -287,7 +312,7 @@ TEST_F(FirestoreIntegrationTest, TestCanDeleteFieldUsingMerge) { EXPECT_FALSE(snapshot.Get("nested.foo").is_valid()); } -TEST_F(FirestoreIntegrationTest, TestCanDeleteFieldUsingMergeFields) { +TEST_F(FirestoreTest, TestCanDeleteFieldUsingMergeFields) { DocumentReference document = Collection("rooms").Document("eros"); Await(document.Set(MapFieldValue{ {"untouched", FieldValue::Boolean(true)}, @@ -314,7 +339,7 @@ TEST_F(FirestoreIntegrationTest, TestCanDeleteFieldUsingMergeFields) { FieldValue::Map({{"untouched", FieldValue::Boolean(true)}})}})); } -TEST_F(FirestoreIntegrationTest, TestCanSetServerTimestampsUsingMergeFields) { +TEST_F(FirestoreTest, TestCanSetServerTimestampsUsingMergeFields) { DocumentReference document = Collection("rooms").Document("eros"); Await(document.Set(MapFieldValue{ {"untouched", FieldValue::Boolean(true)}, @@ -335,7 +360,7 @@ TEST_F(FirestoreIntegrationTest, TestCanSetServerTimestampsUsingMergeFields) { EXPECT_TRUE(snapshot.Get("nested.foo").is_timestamp()); } -TEST_F(FirestoreIntegrationTest, TestMergeReplacesArrays) { +TEST_F(FirestoreTest, TestMergeReplacesArrays) { DocumentReference document = Collection("rooms").Document("eros"); Await(document.Set(MapFieldValue{ {"untouched", FieldValue::Boolean(true)}, @@ -363,8 +388,7 @@ TEST_F(FirestoreIntegrationTest, TestMergeReplacesArrays) { {{"data", FieldValue::String("new")}})})}})); } -TEST_F(FirestoreIntegrationTest, - TestCanDeepMergeDataWithAnExistingDocumentUsingSet) { +TEST_F(FirestoreTest, TestCanDeepMergeDataWithAnExistingDocumentUsingSet) { DocumentReference document = Collection("rooms").Document("eros"); Await(document.Set(MapFieldValue{ {"owner.data", @@ -391,7 +415,7 @@ TEST_F(FirestoreIntegrationTest, #if defined(__ANDROID__) && FIRESTORE_HAVE_EXCEPTIONS // TODO(b/136012313): iOS currently doesn't rethrow native exceptions as C++ // exceptions. -TEST_F(FirestoreIntegrationTest, TestFieldMaskCannotContainMissingFields) { +TEST_F(FirestoreTest, TestFieldMaskCannotContainMissingFields) { DocumentReference document = Collection("rooms").Document(); try { document.Set(MapFieldValue{{"desc", FieldValue::String("NewDescription")}}, @@ -406,7 +430,7 @@ TEST_F(FirestoreIntegrationTest, TestFieldMaskCannotContainMissingFields) { } #endif // defined(__ANDROID__) && FIRESTORE_HAVE_EXCEPTIONS -TEST_F(FirestoreIntegrationTest, TestFieldsNotInFieldMaskAreIgnored) { +TEST_F(FirestoreTest, TestFieldsNotInFieldMaskAreIgnored) { DocumentReference document = Collection("rooms").Document("eros"); Await(document.Set(MapFieldValue{ {"desc", FieldValue::String("Description")}, @@ -428,7 +452,7 @@ TEST_F(FirestoreIntegrationTest, TestFieldsNotInFieldMaskAreIgnored) { {"email", FieldValue::String("abc@xyz.com")}})}})); } -TEST_F(FirestoreIntegrationTest, TestFieldDeletesNotInFieldMaskAreIgnored) { +TEST_F(FirestoreTest, TestFieldDeletesNotInFieldMaskAreIgnored) { DocumentReference document = Collection("rooms").Document("eros"); Await(document.Set(MapFieldValue{ {"desc", FieldValue::String("Description")}, @@ -450,7 +474,7 @@ TEST_F(FirestoreIntegrationTest, TestFieldDeletesNotInFieldMaskAreIgnored) { {"email", FieldValue::String("abc@xyz.com")}})}})); } -TEST_F(FirestoreIntegrationTest, TestFieldTransformsNotInFieldMaskAreIgnored) { +TEST_F(FirestoreTest, TestFieldTransformsNotInFieldMaskAreIgnored) { DocumentReference document = Collection("rooms").Document("eros"); Await(document.Set(MapFieldValue{ {"desc", FieldValue::String("Description")}, @@ -472,7 +496,7 @@ TEST_F(FirestoreIntegrationTest, TestFieldTransformsNotInFieldMaskAreIgnored) { {"email", FieldValue::String("abc@xyz.com")}})}})); } -TEST_F(FirestoreIntegrationTest, TestCanSetEmptyFieldMask) { +TEST_F(FirestoreTest, TestCanSetEmptyFieldMask) { DocumentReference document = Collection("rooms").Document("eros"); Await(document.Set(MapFieldValue{ {"desc", FieldValue::String("Description")}, @@ -493,7 +517,7 @@ TEST_F(FirestoreIntegrationTest, TestCanSetEmptyFieldMask) { {"email", FieldValue::String("abc@xyz.com")}})}})); } -TEST_F(FirestoreIntegrationTest, TestCanSpecifyFieldsMultipleTimesInFieldMask) { +TEST_F(FirestoreTest, TestCanSpecifyFieldsMultipleTimesInFieldMask) { DocumentReference document = Collection("rooms").Document("eros"); Await(document.Set(MapFieldValue{ {"desc", FieldValue::String("Description")}, @@ -518,7 +542,7 @@ TEST_F(FirestoreIntegrationTest, TestCanSpecifyFieldsMultipleTimesInFieldMask) { {"email", FieldValue::String("new@new.com")}})}})); } -TEST_F(FirestoreIntegrationTest, TestCanDeleteAFieldWithAnUpdate) { +TEST_F(FirestoreTest, TestCanDeleteAFieldWithAnUpdate) { DocumentReference document = Collection("rooms").Document("eros"); Await(document.Set(MapFieldValue{ {"desc", FieldValue::String("Description")}, @@ -535,7 +559,7 @@ TEST_F(FirestoreIntegrationTest, TestCanDeleteAFieldWithAnUpdate) { FieldValue::Map({{"name", FieldValue::String("Jonny")}})}})); } -TEST_F(FirestoreIntegrationTest, TestCanUpdateFieldsWithDots) { +TEST_F(FirestoreTest, TestCanUpdateFieldsWithDots) { DocumentReference document = Collection("rooms").Document("eros"); Await(document.Set(MapFieldValue{{"a.b", FieldValue::String("old")}, {"c.d", FieldValue::String("old")}, @@ -550,7 +574,7 @@ TEST_F(FirestoreIntegrationTest, TestCanUpdateFieldsWithDots) { {"e.f", FieldValue::String("old")}})); } -TEST_F(FirestoreIntegrationTest, TestCanUpdateNestedFields) { +TEST_F(FirestoreTest, TestCanUpdateNestedFields) { DocumentReference document = Collection("rooms").Document("eros"); Await(document.Set(MapFieldValue{ {"a", FieldValue::Map({{"b", FieldValue::String("old")}})}, @@ -569,7 +593,7 @@ TEST_F(FirestoreIntegrationTest, TestCanUpdateNestedFields) { // Verify that multiple deletes in a single update call work. // https://github.com/firebase/quickstart-unity/issues/882 -TEST_F(FirestoreIntegrationTest, TestCanUpdateFieldsWithMultipleDeletes) { +TEST_F(FirestoreTest, TestCanUpdateFieldsWithMultipleDeletes) { DocumentReference document = Collection("rooms").Document(); Await(document.Set(MapFieldValue{{"key1", FieldValue::String("value1")}, {"key2", FieldValue::String("value2")}, @@ -586,7 +610,7 @@ TEST_F(FirestoreIntegrationTest, TestCanUpdateFieldsWithMultipleDeletes) { {"key4", FieldValue::String("value4")}})); } -TEST_F(FirestoreIntegrationTest, TestDeleteDocument) { +TEST_F(FirestoreTest, TestDeleteDocument) { DocumentReference document = Collection("rooms").Document("eros"); WriteDocument(document, MapFieldValue{{"value", FieldValue::String("bar")}}); DocumentSnapshot snapshot = ReadDocument(document); @@ -599,7 +623,7 @@ TEST_F(FirestoreIntegrationTest, TestDeleteDocument) { EXPECT_FALSE(snapshot.exists()); } -TEST_F(FirestoreIntegrationTest, TestCannotUpdateNonexistentDocument) { +TEST_F(FirestoreTest, TestCannotUpdateNonexistentDocument) { DocumentReference document = Collection("rooms").Document(); Future future = document.Update(MapFieldValue{{"owner", FieldValue::String("abc")}}); @@ -610,7 +634,7 @@ TEST_F(FirestoreIntegrationTest, TestCannotUpdateNonexistentDocument) { EXPECT_FALSE(snapshot.exists()); } -TEST_F(FirestoreIntegrationTest, TestCanRetrieveNonexistentDocument) { +TEST_F(FirestoreTest, TestCanRetrieveNonexistentDocument) { DocumentReference document = Collection("rooms").Document(); DocumentSnapshot snapshot = ReadDocument(document); EXPECT_FALSE(snapshot.exists()); @@ -623,7 +647,7 @@ TEST_F(FirestoreIntegrationTest, TestCanRetrieveNonexistentDocument) { registration.Remove(); } -TEST_F(FirestoreIntegrationTest, +TEST_F(FirestoreTest, TestAddingToACollectionYieldsTheCorrectDocumentReference) { DocumentReference document = Collection("rooms").Document(); Await(document.Set(MapFieldValue{{"foo", FieldValue::Double(1.0)}})); @@ -633,8 +657,7 @@ TEST_F(FirestoreIntegrationTest, ContainerEq(MapFieldValue{{"foo", FieldValue::Double(1.0)}})); } -TEST_F(FirestoreIntegrationTest, - TestSnapshotsInSyncListenerFiresAfterListenersInSync) { +TEST_F(FirestoreTest, TestSnapshotsInSyncListenerFiresAfterListenersInSync) { class TestData { public: void AddEvent(const std::string& event) { @@ -723,7 +746,7 @@ TEST_F(FirestoreIntegrationTest, sync_registration.Remove(); } -TEST_F(FirestoreIntegrationTest, TestQueriesAreValidatedOnClient) { +TEST_F(FirestoreTest, TestQueriesAreValidatedOnClient) { // NOTE: Failure cases are validated in ValidationTest. CollectionReference collection = Collection(); Query query = @@ -755,7 +778,7 @@ TEST_F(FirestoreIntegrationTest, TestQueriesAreValidatedOnClient) { // The test harness will generate Java JUnit test regardless whether this is // inside a #if or not. So we move #if inside instead of enclose the whole case. -TEST_F(FirestoreIntegrationTest, TestListenCanBeCalledMultipleTimes) { +TEST_F(FirestoreTest, TestListenCanBeCalledMultipleTimes) { class TestData { public: void SetDocumentSnapshot(const DocumentSnapshot& document_snapshot) { @@ -803,7 +826,7 @@ TEST_F(FirestoreIntegrationTest, TestListenCanBeCalledMultipleTimes) { ContainerEq(MapFieldValue{{"foo", FieldValue::String("bar")}})); } -TEST_F(FirestoreIntegrationTest, TestDocumentSnapshotEventsNonExistent) { +TEST_F(FirestoreTest, TestDocumentSnapshotEventsNonExistent) { DocumentReference document = Collection("rooms").Document(); TestEventListener listener("TestNonExistent"); ListenerRegistration registration = @@ -815,7 +838,7 @@ TEST_F(FirestoreIntegrationTest, TestDocumentSnapshotEventsNonExistent) { registration.Remove(); } -TEST_F(FirestoreIntegrationTest, TestDocumentSnapshotEventsForAdd) { +TEST_F(FirestoreTest, TestDocumentSnapshotEventsForAdd) { DocumentReference document = Collection("rooms").Document(); TestEventListener listener("TestForAdd"); ListenerRegistration registration = @@ -839,7 +862,7 @@ TEST_F(FirestoreIntegrationTest, TestDocumentSnapshotEventsForAdd) { registration.Remove(); } -TEST_F(FirestoreIntegrationTest, TestDocumentSnapshotEventsForChange) { +TEST_F(FirestoreTest, TestDocumentSnapshotEventsForChange) { CollectionReference collection = Collection(std::map{ {"doc", MapFieldValue{{"a", FieldValue::Double(1.0)}}}}); @@ -873,7 +896,7 @@ TEST_F(FirestoreIntegrationTest, TestDocumentSnapshotEventsForChange) { registration.Remove(); } -TEST_F(FirestoreIntegrationTest, TestDocumentSnapshotEventsForDelete) { +TEST_F(FirestoreTest, TestDocumentSnapshotEventsForDelete) { CollectionReference collection = Collection(std::map{ {"doc", MapFieldValue{{"a", FieldValue::Double(1.0)}}}}); @@ -897,7 +920,7 @@ TEST_F(FirestoreIntegrationTest, TestDocumentSnapshotEventsForDelete) { registration.Remove(); } -TEST_F(FirestoreIntegrationTest, TestDocumentSnapshotErrorReporting) { +TEST_F(FirestoreTest, TestDocumentSnapshotErrorReporting) { DocumentReference document = Collection("col").Document("__badpath__"); TestEventListener listener("TestBadPath"); ListenerRegistration registration = @@ -910,7 +933,7 @@ TEST_F(FirestoreIntegrationTest, TestDocumentSnapshotErrorReporting) { registration.Remove(); } -TEST_F(FirestoreIntegrationTest, TestQuerySnapshotEventsForAdd) { +TEST_F(FirestoreTest, TestQuerySnapshotEventsForAdd) { CollectionReference collection = Collection(); DocumentReference document = collection.Document(); TestEventListener listener("TestForCollectionAdd"); @@ -937,7 +960,7 @@ TEST_F(FirestoreIntegrationTest, TestQuerySnapshotEventsForAdd) { registration.Remove(); } -TEST_F(FirestoreIntegrationTest, TestQuerySnapshotEventsForChange) { +TEST_F(FirestoreTest, TestQuerySnapshotEventsForChange) { CollectionReference collection = Collection(std::map{ {"doc", MapFieldValue{{"a", FieldValue::Double(1.0)}}}}); @@ -971,7 +994,7 @@ TEST_F(FirestoreIntegrationTest, TestQuerySnapshotEventsForChange) { registration.Remove(); } -TEST_F(FirestoreIntegrationTest, TestQuerySnapshotEventsForDelete) { +TEST_F(FirestoreTest, TestQuerySnapshotEventsForDelete) { CollectionReference collection = Collection(std::map{ {"doc", MapFieldValue{{"a", FieldValue::Double(1.0)}}}}); @@ -995,7 +1018,7 @@ TEST_F(FirestoreIntegrationTest, TestQuerySnapshotEventsForDelete) { registration.Remove(); } -TEST_F(FirestoreIntegrationTest, TestQuerySnapshotErrorReporting) { +TEST_F(FirestoreTest, TestQuerySnapshotErrorReporting) { CollectionReference collection = Collection("a").Document("__badpath__").Collection("b"); TestEventListener listener("TestBadPath"); @@ -1009,8 +1032,7 @@ TEST_F(FirestoreIntegrationTest, TestQuerySnapshotErrorReporting) { registration.Remove(); } -TEST_F(FirestoreIntegrationTest, - TestMetadataOnlyChangesAreNotFiredWhenNoOptionsProvided) { +TEST_F(FirestoreTest, TestMetadataOnlyChangesAreNotFiredWhenNoOptionsProvided) { DocumentReference document = Collection().Document(); TestEventListener listener("TestForNoMetadataOnlyChanges"); ListenerRegistration registration = listener.AttachTo(&document); @@ -1027,7 +1049,7 @@ TEST_F(FirestoreIntegrationTest, registration.Remove(); } -TEST_F(FirestoreIntegrationTest, TestDocumentReferenceExposesFirestore) { +TEST_F(FirestoreTest, TestDocumentReferenceExposesFirestore) { Firestore* db = TestFirestore(); // EXPECT_EQ(db, db->Document("foo/bar").firestore()); // TODO(varconst): use the commented out check above. @@ -1041,19 +1063,19 @@ TEST_F(FirestoreIntegrationTest, TestDocumentReferenceExposesFirestore) { EXPECT_NE(nullptr, db->Document("foo/bar").firestore()); } -TEST_F(FirestoreIntegrationTest, TestCollectionReferenceExposesFirestore) { +TEST_F(FirestoreTest, TestCollectionReferenceExposesFirestore) { Firestore* db = TestFirestore(); // EXPECT_EQ(db, db->Collection("foo").firestore()); EXPECT_NE(nullptr, db->Collection("foo").firestore()); } -TEST_F(FirestoreIntegrationTest, TestQueryExposesFirestore) { +TEST_F(FirestoreTest, TestQueryExposesFirestore) { Firestore* db = TestFirestore(); // EXPECT_EQ(db, db->Collection("foo").Limit(5).firestore()); EXPECT_NE(nullptr, db->Collection("foo").Limit(5).firestore()); } -TEST_F(FirestoreIntegrationTest, TestDocumentReferenceEquality) { +TEST_F(FirestoreTest, TestDocumentReferenceEquality) { Firestore* db = TestFirestore(); DocumentReference document = db->Document("foo/bar"); EXPECT_EQ(document, db->Document("foo/bar")); @@ -1065,7 +1087,7 @@ TEST_F(FirestoreIntegrationTest, TestDocumentReferenceEquality) { EXPECT_NE(document, another_db->Document("foo/bar")); } -TEST_F(FirestoreIntegrationTest, TestQueryReferenceEquality) { +TEST_F(FirestoreTest, TestQueryReferenceEquality) { Firestore* db = TestFirestore(); Query query = db->Collection("foo").OrderBy("bar").WhereEqualTo( "baz", FieldValue::Integer(42)); @@ -1081,7 +1103,7 @@ TEST_F(FirestoreIntegrationTest, TestQueryReferenceEquality) { // So we skip the testing of two queries with different Firestore instance. } -TEST_F(FirestoreIntegrationTest, TestCanTraverseCollectionsAndDocuments) { +TEST_F(FirestoreTest, TestCanTraverseCollectionsAndDocuments) { Firestore* db = TestFirestore(); // doc path from root Firestore. @@ -1097,7 +1119,7 @@ TEST_F(FirestoreIntegrationTest, TestCanTraverseCollectionsAndDocuments) { EXPECT_EQ("a/b/c/d/e", db->Document("a/b").Collection("c/d/e").path()); } -TEST_F(FirestoreIntegrationTest, TestCanTraverseCollectionAndDocumentParents) { +TEST_F(FirestoreTest, TestCanTraverseCollectionAndDocumentParents) { Firestore* db = TestFirestore(); CollectionReference collection = db->Collection("a/b/c"); EXPECT_EQ("a/b/c", collection.path()); @@ -1112,17 +1134,17 @@ TEST_F(FirestoreIntegrationTest, TestCanTraverseCollectionAndDocumentParents) { EXPECT_FALSE(invalidDoc.is_valid()); } -TEST_F(FirestoreIntegrationTest, TestCollectionId) { +TEST_F(FirestoreTest, TestCollectionId) { EXPECT_EQ("foo", TestFirestore()->Collection("foo").id()); EXPECT_EQ("baz", TestFirestore()->Collection("foo/bar/baz").id()); } -TEST_F(FirestoreIntegrationTest, TestDocumentId) { +TEST_F(FirestoreTest, TestDocumentId) { EXPECT_EQ(TestFirestore()->Document("foo/bar").id(), "bar"); EXPECT_EQ(TestFirestore()->Document("foo/bar/baz/qux").id(), "qux"); } -TEST_F(FirestoreIntegrationTest, TestCanQueueWritesWhileOffline) { +TEST_F(FirestoreTest, TestCanQueueWritesWhileOffline) { // Arrange DocumentReference document = Collection("rooms").Document("eros"); @@ -1150,7 +1172,7 @@ TEST_F(FirestoreIntegrationTest, TestCanQueueWritesWhileOffline) { EXPECT_FALSE(snapshot.metadata().is_from_cache()); } -TEST_F(FirestoreIntegrationTest, TestCanGetDocumentsWhileOffline) { +TEST_F(FirestoreTest, TestCanGetDocumentsWhileOffline) { DocumentReference document = Collection("rooms").Document(); Await(TestFirestore()->DisableNetwork()); Future future = document.Get(); @@ -1200,7 +1222,7 @@ TEST_F(FirestoreIntegrationTest, TestCanGetDocumentsWhileOffline) { // really unit tests that have to be run in integration tests setup. The // existing Objective-C and Android tests cover these cases fairly well. -TEST_F(FirestoreIntegrationTest, TestCanDisableAndEnableNetworking) { +TEST_F(FirestoreTest, TestCanDisableAndEnableNetworking) { // There's not currently a way to check if networking is in fact disabled, // so for now just test that the method is well-behaved and doesn't throw. Firestore* db = TestFirestore(); @@ -1212,7 +1234,7 @@ TEST_F(FirestoreIntegrationTest, TestCanDisableAndEnableNetworking) { } // TODO(varconst): split this test. -TEST_F(FirestoreIntegrationTest, TestToString) { +TEST_F(FirestoreTest, TestToString) { Settings settings; settings.set_host("foo.bar"); settings.set_ssl_enabled(false); @@ -1242,12 +1264,12 @@ TEST_F(FirestoreIntegrationTest, TestToString) { // TODO(wuandy): Enable this for other platforms when they can handle // exceptions. #if defined(__ANDROID__) && FIRESTORE_HAVE_EXCEPTIONS -TEST_F(FirestoreIntegrationTest, ClientCallsAfterTerminateFails) { +TEST_F(FirestoreTest, ClientCallsAfterTerminateFails) { EXPECT_THAT(TestFirestore()->Terminate(), FutureSucceeds()); EXPECT_THROW(Await(TestFirestore()->DisableNetwork()), std::logic_error); } -TEST_F(FirestoreIntegrationTest, NewOperationThrowsAfterFirestoreTerminate) { +TEST_F(FirestoreTest, NewOperationThrowsAfterFirestoreTerminate) { auto instance = TestFirestore(); DocumentReference reference = TestFirestore()->Document("abc/123"); Await(reference.Set({{"Field", FieldValue::Integer(100)}})); @@ -1273,7 +1295,7 @@ TEST_F(FirestoreIntegrationTest, NewOperationThrowsAfterFirestoreTerminate) { std::logic_error); } -TEST_F(FirestoreIntegrationTest, TerminateCanBeCalledMultipleTimes) { +TEST_F(FirestoreTest, TerminateCanBeCalledMultipleTimes) { auto instance = TestFirestore(); DocumentReference reference = instance->Document("abc/123"); Await(reference.Set({{"Field", FieldValue::Integer(100)}})); @@ -1290,7 +1312,41 @@ TEST_F(FirestoreIntegrationTest, TerminateCanBeCalledMultipleTimes) { } #endif // defined(__ANDROID__) && FIRESTORE_HAVE_EXCEPTIONS -TEST_F(FirestoreIntegrationTest, MaintainsPersistenceAfterRestarting) { +TEST_F(FirestoreTest, CanTerminateFirestoreInstance) { + App* app = App::GetInstance(); + InitResult init_result1; + auto db1 = + std::unique_ptr(Firestore::GetInstance(app, &init_result1)); + ASSERT_EQ(kInitResultSuccess, init_result1); + + EXPECT_THAT(db1->Terminate(), FutureSucceeds()); + + InitResult init_result2; + auto db2 = + std::unique_ptr(Firestore::GetInstance(app, &init_result2)); + ASSERT_EQ(kInitResultSuccess, init_result2); + + EXPECT_NE(db1, db2); +} + +TEST_F(FirestoreTest, CanTerminateNamedFirestoreInstance) { + App* app = App::GetInstance(); + InitResult init_result1; + auto db1 = std::unique_ptr( + Firestore::GetInstance(app, "foo", &init_result1)); + ASSERT_EQ(kInitResultSuccess, init_result1); + + EXPECT_THAT(db1->Terminate(), FutureSucceeds()); + + InitResult init_result2; + auto db2 = std::unique_ptr( + Firestore::GetInstance(app, "foo", &init_result2)); + ASSERT_EQ(kInitResultSuccess, init_result2); + + EXPECT_NE(db1, db2); +} + +TEST_F(FirestoreTest, MaintainsPersistenceAfterRestarting) { Firestore* db = TestFirestore(); App* app = db->app(); DocumentReference doc = db->Collection("col1").Document("doc1"); @@ -1304,7 +1360,7 @@ TEST_F(FirestoreIntegrationTest, MaintainsPersistenceAfterRestarting) { EXPECT_TRUE(snap->exists()); } -TEST_F(FirestoreIntegrationTest, RestartFirestoreLeadsToNewInstance) { +TEST_F(FirestoreTest, RestartFirestoreLeadsToNewInstance) { // Get App and Settings objects to use in the test. Firestore* db_template = TestFirestore("restart_firestore_new_instance_test"); App* app = db_template->app(); @@ -1348,7 +1404,209 @@ TEST_F(FirestoreIntegrationTest, RestartFirestoreLeadsToNewInstance) { delete db1; } -TEST_F(FirestoreIntegrationTest, CanStopListeningAfterTerminate) { +TEST_F(FirestoreTest, CanCreateMultipleFirestoreInstances) { + // TODO(b/282947967): Remove the emulator env check after prod supports + // multiDB. + RUN_TEST_ONLY_AGAINST_FIRESTORE_EMULATOR; + + // Create two Firestore instances in the same app. + App* app = App::GetInstance(); + Firestore* db1 = TestFirestoreWithDatabaseId(app->name(), "db1"); + Firestore* db2 = TestFirestoreWithDatabaseId(app->name(), "db2"); + EXPECT_NE(db1, db2); + + // Create collections with same name in different databases. + DocumentReference doc1 = db1->Collection("abc").Document(); + DocumentReference doc2 = db2->Collection("abc").Document(); + + // Databases can store and retrieve documents without interfering with each + // other. + EXPECT_THAT(doc1.Set({{"foo", FieldValue::String("bar1")}}), + FutureSucceeds()); + EXPECT_THAT(doc2.Set({{"foo", FieldValue::String("bar2")}}), + FutureSucceeds()); + + const DocumentSnapshot* snapshot1 = Await(doc1.Get()); + EXPECT_TRUE(snapshot1->exists()); + EXPECT_THAT(snapshot1->GetData(), + ContainerEq(MapFieldValue{{"foo", FieldValue::String("bar1")}})); + const DocumentSnapshot* snapshot2 = Await(doc2.Get()); + EXPECT_TRUE(snapshot2->exists()); + EXPECT_THAT(snapshot2->GetData(), + ContainerEq(MapFieldValue{{"foo", FieldValue::String("bar2")}})); +} + +TEST_F(FirestoreTest, CanTerminateMultipleFirestoreInstances) { + // TODO(b/282947967): Remove the emulator env check after prod supports + // multiDB. + RUN_TEST_ONLY_AGAINST_FIRESTORE_EMULATOR; + + // Create two Firestore instances in the same app. + App* app = App::GetInstance(); + Firestore* db1 = TestFirestoreWithDatabaseId(app->name(), "db1"); + Firestore* db2 = TestFirestoreWithDatabaseId(app->name(), "db2"); + EXPECT_NE(db1, db2); + + // A database can be terminated without affecting other databases. + DeleteFirestore(db1); + DocumentReference doc = db2->Collection("abc").Document(); + EXPECT_THAT(doc.Set({{"foo", FieldValue::String("bar")}}), FutureSucceeds()); + const DocumentSnapshot* snapshot = Await(doc.Get()); + EXPECT_TRUE(snapshot->exists()); + EXPECT_THAT(snapshot->GetData(), + ContainerEq(MapFieldValue{{"foo", FieldValue::String("bar")}})); +} + +TEST_F(FirestoreTest, CanReadDocsAfterRestartFirestoreAndCreateNewInstance) { + // TODO(b/282947967): Remove the emulator env check and LocateEmulator call + // after prod supports multiDB. + RUN_TEST_ONLY_AGAINST_FIRESTORE_EMULATOR; + + App* app = App::GetInstance(); + auto db1 = std::unique_ptr(Firestore::GetInstance(app, "test-db")); + firestore::LocateEmulator(db1.get()); + + // Create a document that we can use for verification later. + DocumentReference doc1 = db1->Collection("abc").Document(); + const std::string doc_path = doc1.path(); + EXPECT_THAT(doc1.Set({{"foo", FieldValue::String("bar")}}), FutureSucceeds()); + + // Terminate `db1` so that it will be removed from the instance cache. + EXPECT_THAT(db1->Terminate(), FutureSucceeds()); + + // Verify that GetInstance() returns a new instance since the old instance has + // been terminated. + auto db2 = std::unique_ptr(Firestore::GetInstance(app, "test-db")); + firestore::LocateEmulator(db2.get()); + EXPECT_NE(db1, db2); + + // Verify that the new instance points to the same database by verifying that + // the document created with the old instance exists in the new instance. + DocumentReference doc2 = db2->Document(doc_path); + const DocumentSnapshot* snapshot2 = Await(doc2.Get()); + ASSERT_NE(snapshot2, nullptr); + EXPECT_TRUE(snapshot2->exists()); + EXPECT_THAT(snapshot2->GetData(), + ContainerEq(MapFieldValue{{"foo", FieldValue::String("bar")}})); +} + +TEST_F(FirestoreTest, CanKeepDocsSeparateWithMultiDBWhenOnline) { + // TODO(b/282947967): Remove the emulator env check after prod supports + // multiDB. + RUN_TEST_ONLY_AGAINST_FIRESTORE_EMULATOR; + + // Create two Firestore instances in the same app. + App* app = App::GetInstance(); + Firestore* db1 = TestFirestoreWithDatabaseId(app->name(), "db1"); + Firestore* db2 = TestFirestoreWithDatabaseId(app->name(), "db2"); + EXPECT_NE(db1, db2); + + // Create a document in the first Firestore instance. + DocumentReference doc1 = db1->Collection("abc").Document(); + const std::string doc_path = doc1.path(); + EXPECT_THAT(doc1.Set({{"foo", FieldValue::String("bar")}}), FutureSucceeds()); + const DocumentSnapshot* snapshot1 = Await(doc1.Get()); + EXPECT_TRUE(snapshot1->exists()); + EXPECT_THAT(snapshot1->GetData(), + ContainerEq(MapFieldValue{{"foo", FieldValue::String("bar")}})); + + // Verify that the previously saved document only exists in the first + // Firestore instance by verifying that the document does not exist in the + // second instance. + DocumentReference doc2 = db2->Document(doc_path); + const DocumentSnapshot* snapshot2 = Await(doc2.Get()); + EXPECT_FALSE(snapshot2->exists()); + EXPECT_THAT(snapshot2->GetData(), ContainerEq(MapFieldValue{})); +} + +TEST_F(FirestoreTest, CanKeepDocsSeparateWithMultiDBWhenOffline) { + // TODO(b/282947967): Remove the emulator env check after prod supports + // multiDB. + RUN_TEST_ONLY_AGAINST_FIRESTORE_EMULATOR; + + // Create two Firestore instances in the same app. + App* app = App::GetInstance(); + Firestore* db1 = TestFirestoreWithDatabaseId(app->name(), "db1"); + Firestore* db2 = TestFirestoreWithDatabaseId(app->name(), "db2"); + EXPECT_NE(db1, db2); + + DisableNetwork(); + + // Create a document in the first Firestore instance. + DocumentReference doc1 = db1->Collection("abc").Document(); + const std::string doc_path = doc1.path(); + EXPECT_THAT(doc1.Set({{"foo", FieldValue::String("bar")}}), FutureSucceeds()); + const DocumentSnapshot* snapshot1 = Await(doc1.Get(Source::kCache)); + EXPECT_TRUE(snapshot1->exists()); + EXPECT_THAT(snapshot1->GetData(), + ContainerEq(MapFieldValue{{"foo", FieldValue::String("bar")}})); + + // Verify that the previously saved document only exists in the first + // Firestore instance by verifying that the document does not exist in the + // second instance. + DocumentReference doc2 = db2->Document(doc_path); + const DocumentSnapshot* snapshot2 = Await(doc2.Get(Source::kCache)); + EXPECT_FALSE(snapshot2->exists()); + EXPECT_THAT(snapshot2->GetData(), ContainerEq(MapFieldValue{})); +} + +TEST_F(FirestoreTest, ComprehensiveTestOnMultiDbCreationAndTermination) { + // TODO(b/282947967): Remove the emulator env check and LocateEmulator call + // after prod supports multiDB. + RUN_TEST_ONLY_AGAINST_FIRESTORE_EMULATOR; + + // Create 2 firestore instances. + App* app = App::GetInstance(); + Firestore* db1 = Firestore::GetInstance(app, "db1"); + Firestore* db2 = Firestore::GetInstance(app, "db2"); + firestore::LocateEmulator(db1); + firestore::LocateEmulator(db2); + EXPECT_NE(db1, db2); + + // Each firestore can store and read documents independently. + DocumentReference doc1 = db1->Collection("abc").Document(); + EXPECT_THAT(doc1.Set({{"foo", FieldValue::String("bar1")}}), + FutureSucceeds()); + DocumentReference doc2 = db2->Collection("abc").Document(); + EXPECT_THAT(doc2.Set({{"foo", FieldValue::String("bar2")}}), + FutureSucceeds()); + const DocumentSnapshot* snapshot1 = Await(doc1.Get()); + EXPECT_TRUE(snapshot1->exists()); + EXPECT_THAT(snapshot1->GetData(), + ContainerEq(MapFieldValue{{"foo", FieldValue::String("bar1")}})); + + // Save the doc path in db1 for verification later. + const std::string doc_path = doc1.path(); + + // Terminate `db1` so that it will be removed from the instance cache. + EXPECT_THAT(db1->Terminate(), FutureSucceeds()); + delete db1; + + // Termination of one firestore instance will not affect other instances. + const DocumentSnapshot* snapshot2 = Await(doc2.Get()); + EXPECT_TRUE(snapshot2->exists()); + EXPECT_THAT(snapshot2->GetData(), + ContainerEq(MapFieldValue{{"foo", FieldValue::String("bar2")}})); + + // GetInstance() returns a new instance since the old instance has been + // terminated. + Firestore* db3 = Firestore::GetInstance(app, "db1"); + firestore::LocateEmulator(db3); + + // The new instance points to the same database by verifying that the document + // created with the old instance exists in the new instance. + DocumentReference doc3 = db3->Document(doc_path); + const DocumentSnapshot* snapshot3 = Await(doc3.Get()); + ASSERT_NE(snapshot3, nullptr); + EXPECT_TRUE(snapshot3->exists()); + EXPECT_THAT(snapshot3->GetData(), + ContainerEq(MapFieldValue{{"foo", FieldValue::String("bar1")}})); + + delete db2; + delete db3; +} + +TEST_F(FirestoreTest, CanStopListeningAfterTerminate) { auto instance = TestFirestore(); DocumentReference reference = instance->Document("abc/123"); EventAccumulator accumulator; @@ -1364,7 +1622,7 @@ TEST_F(FirestoreIntegrationTest, CanStopListeningAfterTerminate) { registration.Remove(); } -TEST_F(FirestoreIntegrationTest, WaitForPendingWritesResolves) { +TEST_F(FirestoreTest, WaitForPendingWritesResolves) { DocumentReference document = Collection("abc").Document("123"); Await(TestFirestore()->DisableNetwork()); @@ -1390,9 +1648,9 @@ TEST_F(FirestoreIntegrationTest, WaitForPendingWritesResolves) { // TODO(wuandy): This test requires to create underlying firestore instance with // a MockCredentialProvider first. -// TEST_F(FirestoreIntegrationTest, WaitForPendingWritesFailsWhenUserChanges) {} +// TEST_F(FirestoreTest, WaitForPendingWritesFailsWhenUserChanges) {} -TEST_F(FirestoreIntegrationTest, +TEST_F(FirestoreTest, WaitForPendingWritesResolvesWhenOfflineIfThereIsNoPending) { Await(TestFirestore()->DisableNetwork()); Future await_pending_writes = TestFirestore()->WaitForPendingWrites(); @@ -1403,7 +1661,7 @@ TEST_F(FirestoreIntegrationTest, EXPECT_EQ(await_pending_writes.status(), FutureStatus::kFutureStatusComplete); } -TEST_F(FirestoreIntegrationTest, CanClearPersistenceTestHarnessVerification) { +TEST_F(FirestoreTest, CanClearPersistenceTestHarnessVerification) { // Verify that TestFirestore(), DeleteFirestore(), and DeleteApp() behave how // we expect; otherwise, the tests for ClearPersistence() could yield false // positives. @@ -1426,7 +1684,7 @@ TEST_F(FirestoreIntegrationTest, CanClearPersistenceTestHarnessVerification) { ContainerEq(MapFieldValue{{"foo", FieldValue::Integer(42)}})); } -TEST_F(FirestoreIntegrationTest, CanClearPersistenceAfterRestarting) { +TEST_F(FirestoreTest, CanClearPersistenceAfterRestarting) { Firestore* db = TestFirestore(); App* app = db->app(); const std::string app_name = app->name(); @@ -1458,7 +1716,7 @@ TEST_F(FirestoreIntegrationTest, CanClearPersistenceAfterRestarting) { EXPECT_EQ(await_get.error(), Error::kErrorUnavailable); } -TEST_F(FirestoreIntegrationTest, CanClearPersistenceOnANewFirestoreInstance) { +TEST_F(FirestoreTest, CanClearPersistenceOnANewFirestoreInstance) { Firestore* db = TestFirestore(); App* app = db->app(); const std::string app_name = app->name(); @@ -1487,7 +1745,7 @@ TEST_F(FirestoreIntegrationTest, CanClearPersistenceOnANewFirestoreInstance) { EXPECT_EQ(await_get.error(), Error::kErrorUnavailable); } -TEST_F(FirestoreIntegrationTest, ClearPersistenceWhileRunningFails) { +TEST_F(FirestoreTest, ClearPersistenceWhileRunningFails) { // Call EnableNetwork() in order to ensure that Firestore is fully // initialized before clearing persistence. EnableNetwork() is chosen because // it is easy to call. @@ -1500,12 +1758,12 @@ TEST_F(FirestoreIntegrationTest, ClearPersistenceWhileRunningFails) { } // Note: this test only exists in C++. -TEST_F(FirestoreIntegrationTest, DomainObjectsReferToSameFirestoreInstance) { +TEST_F(FirestoreTest, DomainObjectsReferToSameFirestoreInstance) { EXPECT_EQ(TestFirestore(), TestFirestore()->Document("foo/bar").firestore()); EXPECT_EQ(TestFirestore(), TestFirestore()->Collection("foo").firestore()); } -TEST_F(FirestoreIntegrationTest, AuthWorks) { +TEST_F(FirestoreTest, AuthWorks) { SKIP_TEST_ON_QUICK_CHECK; // This app instance is managed by the text fixture. App* app = GetApp(); @@ -1549,7 +1807,7 @@ TEST_F(FirestoreIntegrationTest, AuthWorks) { // This test is to ensure b/172986326 doesn't regress. // Note: this test only exists in C++. -TEST_F(FirestoreIntegrationTest, FirestoreCanBeDeletedFromTransactionAsync) { +TEST_F(FirestoreTest, FirestoreCanBeDeletedFromTransactionAsync) { Firestore* db = TestFirestore(); DisownFirestore(db); @@ -1571,7 +1829,7 @@ TEST_F(FirestoreIntegrationTest, FirestoreCanBeDeletedFromTransactionAsync) { // This test is to ensure b/172986326 doesn't regress. // Note: this test only exists in C++. -TEST_F(FirestoreIntegrationTest, FirestoreCanBeDeletedFromTransaction) { +TEST_F(FirestoreTest, FirestoreCanBeDeletedFromTransaction) { Firestore* db = TestFirestore(); DisownFirestore(db); diff --git a/firestore/integration_test_internal/src/util/integration_test_util.cc b/firestore/integration_test_internal/src/util/integration_test_util.cc index 3746d0faf..d3a4129ed 100644 --- a/firestore/integration_test_internal/src/util/integration_test_util.cc +++ b/firestore/integration_test_internal/src/util/integration_test_util.cc @@ -34,13 +34,15 @@ namespace firebase { namespace firestore { struct TestFriend { - static FirestoreInternal* CreateTestFirestoreInternal(App* app) { + static FirestoreInternal* CreateTestFirestoreInternal( + App* app, const std::string& database_id) { #if !defined(__ANDROID__) return new FirestoreInternal( - app, std::make_unique(), - std::make_unique()); + app, database_id, + absl::make_unique(), + absl::make_unique()); #else - return new FirestoreInternal(app); + return new FirestoreInternal(app, database_id); #endif // !defined(__ANDROID__) } }; @@ -78,8 +80,9 @@ App* GetApp(const char* name, const std::string& override_project_id) { App* GetApp() { return GetApp(/*name=*/nullptr, /*project_id=*/""); } -FirestoreInternal* CreateTestFirestoreInternal(App* app) { - return TestFriend::CreateTestFirestoreInternal(app); +FirestoreInternal* CreateTestFirestoreInternal(App* app, + const std::string& database_id) { + return TestFriend::CreateTestFirestoreInternal(app, database_id); } } // namespace firestore diff --git a/firestore/integration_test_internal/src/validation_test.cc b/firestore/integration_test_internal/src/validation_test.cc index 4825b8c56..1d3ee2fcd 100644 --- a/firestore/integration_test_internal/src/validation_test.cc +++ b/firestore/integration_test_internal/src/validation_test.cc @@ -407,21 +407,157 @@ TEST_F(ValidationTest, DisableSslWithoutSettingHostFails) { } TEST_F(ValidationTest, FirestoreGetInstanceWithNullAppFails) { - EXPECT_ERROR( - Firestore::GetInstance(/*app=*/nullptr, /*init_result=*/nullptr), - "firebase::App instance cannot be null. Use " - "firebase::App::GetInstance() without arguments if you'd like to use " - "the default instance."); + EXPECT_ERROR(Firestore::GetInstance(/*app=*/(App*)nullptr, + /*init_result=*/(InitResult*)nullptr), + "firebase::App instance cannot be null. Use other " + "Firestore::GetInstance() if you'd like to use the default " + "app instance."); +} + +TEST_F(ValidationTest, FirestoreGetInstanceWithNullDatabaseNameFails) { + EXPECT_ERROR(Firestore::GetInstance(/*db_name=*/(char*)nullptr, + /*init_result=*/(InitResult*)nullptr), + "Provided database ID must not be null. Use other " + "Firestore::GetInstance() if you'd like to use the default " + "database ID."); +} + +TEST_F(ValidationTest, + FirestoreGetInstanceWithNonNullDatabaseIdButNullAppFails) { + EXPECT_ERROR(Firestore::GetInstance(/*app=*/(App*)nullptr, "foo", + /*init_result=*/(InitResult*)nullptr), + "firebase::App instance cannot be null. Use other " + "Firestore::GetInstance() if you'd like to use the default " + "app instance."); +} + +TEST_F(ValidationTest, + FirestoreGetInstanceWithNonNullAppButNullDatabaseNameFails) { + EXPECT_ERROR(Firestore::GetInstance(app(), /*db_name=*/(char*)nullptr, + /*init_result=*/(InitResult*)nullptr), + "Provided database ID must not be null. Use other " + "Firestore::GetInstance() if you'd like to use the default " + "database ID."); +} + +TEST_F(ValidationTest, + FirestoreGetInstanceWithNoArgumentsReturnsNonNullInstance) { + InitResult result; + Firestore* instance = Firestore::GetInstance(&result); + EXPECT_EQ(kInitResultSuccess, result); + EXPECT_NE(instance, nullptr); + delete instance; } TEST_F(ValidationTest, FirestoreGetInstanceWithNonNullAppReturnsNonNullInstance) { InitResult result; - EXPECT_NO_THROW(Firestore::GetInstance(app(), &result)); + Firestore* instance = Firestore::GetInstance(app(), &result); + EXPECT_EQ(kInitResultSuccess, result); + EXPECT_NE(instance, nullptr); + delete instance; +} + +TEST_F(ValidationTest, + FirestoreGetInstanceWithAppAndDatabaseNameReturnsNonNullInstance) { + InitResult result; + Firestore* instance = Firestore::GetInstance(app(), "foo", &result); EXPECT_EQ(kInitResultSuccess, result); - EXPECT_NE(Firestore::GetInstance(app()), nullptr); + EXPECT_NE(instance, nullptr); + delete instance; } +TEST_F(ValidationTest, + FirestoreGetInstanceWithDatabaseNameReturnsNonNullInstance) { + InitResult result; + Firestore* instance = Firestore::GetInstance("foo", &result); + EXPECT_EQ(kInitResultSuccess, result); + EXPECT_NE(instance, nullptr); + delete instance; +} + +TEST_F(ValidationTest, + FirestoreGetInstanceCalledMultipleTimeReturnSameInstance) { + { + Firestore* instance1 = Firestore::GetInstance(); + Firestore* instance2 = Firestore::GetInstance(); + EXPECT_EQ(instance1, instance2); + delete instance1; + } + { + Firestore* instance1 = Firestore::GetInstance(app()); + Firestore* instance2 = Firestore::GetInstance(app()); + EXPECT_EQ(instance1, instance2); + delete instance1; + } + { + Firestore* instance1 = Firestore::GetInstance("foo"); + Firestore* instance2 = Firestore::GetInstance("foo"); + EXPECT_EQ(instance1, instance2); + delete instance1; + } + { + Firestore* instance1 = Firestore::GetInstance(app(), "foo"); + Firestore* instance2 = Firestore::GetInstance(app(), "foo"); + EXPECT_EQ(instance1, instance2); + delete instance1; + } +} + +TEST_F(ValidationTest, + DifferentFirestoreGetInstanceMethodCanGetSameDefaultFirestoreInstance) { + Firestore* instance1 = Firestore::GetInstance(); + Firestore* instance2 = Firestore::GetInstance(app()); + Firestore* instance3 = Firestore::GetInstance("(default)"); + Firestore* instance4 = Firestore::GetInstance(app(), "(default)"); + + EXPECT_EQ(instance1, instance2); + EXPECT_EQ(instance1, instance3); + EXPECT_EQ(instance1, instance4); + delete instance1; +} + +TEST_F( + ValidationTest, + DifferentFirestoreGetInstanceWithSameDatabaseNameShouldGetSameFirestoreInstance) { + Firestore* instance1 = Firestore::GetInstance("foo"); + Firestore* instance2 = Firestore::GetInstance(app(), "foo"); + EXPECT_EQ(instance1, instance2); + delete instance1; +} + +TEST_F( + ValidationTest, + DifferentFirestoreGetInstanceWithDifferentDatabaseNameShouldGetDifferentFirestoreInstance) { + { + Firestore* instance1 = Firestore::GetInstance(); + Firestore* instance2 = Firestore::GetInstance("foo"); + EXPECT_NE(instance1, instance2); + delete instance1; + delete instance2; + } + { + Firestore* instance1 = Firestore::GetInstance("foo"); + Firestore* instance2 = Firestore::GetInstance("bar"); + EXPECT_NE(instance1, instance2); + delete instance1; + delete instance2; + } + { + Firestore* instance1 = Firestore::GetInstance(app(), "foo"); + Firestore* instance2 = Firestore::GetInstance(app(), "bar"); + EXPECT_NE(instance1, instance2); + delete instance1; + delete instance2; + } + { + Firestore* instance1 = Firestore::GetInstance("foo"); + Firestore* instance2 = Firestore::GetInstance(app(), "bar"); + EXPECT_NE(instance1, instance2); + delete instance1; + delete instance2; + } +} TEST_F(ValidationTest, CollectionPathsMustBeOddLength) { Firestore* db = TestFirestore(); DocumentReference base_document = db->Document("foo/bar"); diff --git a/firestore/src/android/firestore_android.cc b/firestore/src/android/firestore_android.cc index 4c0e2a990..e3317b6fe 100644 --- a/firestore/src/android/firestore_android.cc +++ b/firestore/src/android/firestore_android.cc @@ -114,7 +114,7 @@ Method kGetSettings( "()Lcom/google/firebase/firestore/FirebaseFirestoreSettings;"); StaticMethod kGetInstance( "getInstance", - "(Lcom/google/firebase/FirebaseApp;)" + "(Lcom/google/firebase/FirebaseApp;Ljava/lang/String;)" "Lcom/google/firebase/firestore/FirebaseFirestore;"); StaticMethod kSetLoggingEnabled("setLoggingEnabled", "(Z)V"); StaticMethod kSetClientLanguage("setClientLanguage", @@ -260,14 +260,17 @@ Local CreateLoadBundleTask(Env& env, const char kApiIdentifier[] = "Firestore"; -FirestoreInternal::FirestoreInternal(App* app) { +FirestoreInternal::FirestoreInternal(App* app, const std::string& database_id) { FIREBASE_ASSERT(app != nullptr); if (!Initialize(app)) return; app_ = app; + database_name_ = database_id; Env env = GetEnv(); Local platform_app(env.get(), app_->GetPlatformApp()); - Local java_firestore = env.Call(kGetInstance, platform_app); + Local java_database_id = env.NewStringUtf(database_id); + Local java_firestore = + env.Call(kGetInstance, platform_app, java_database_id); FIREBASE_ASSERT(java_firestore.get() != nullptr); obj_ = java_firestore; diff --git a/firestore/src/android/firestore_android.h b/firestore/src/android/firestore_android.h index d5079c753..be72945ce 100644 --- a/firestore/src/android/firestore_android.h +++ b/firestore/src/android/firestore_android.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "app/src/cleanup_notifier.h" @@ -75,7 +76,7 @@ class FirestoreInternal { }; // Note: call `set_firestore_public` immediately after construction. - explicit FirestoreInternal(App* app); + FirestoreInternal(App* app, const std::string& database_id); ~FirestoreInternal(); App* app() const { return app_; } @@ -135,6 +136,8 @@ class FirestoreInternal { ListenerRegistrationInternal* registration); void ClearListeners(); + const std::string& database_name() const { return database_name_; } + // Bundles Future LoadBundle(const std::string& bundle); Future LoadBundle( @@ -211,6 +214,8 @@ class FirestoreInternal { std::unique_ptr> promises_; CleanupNotifier cleanup_; + + std::string database_name_; }; // Holds a "weak reference" to a `FirestoreInternal` object. diff --git a/firestore/src/common/firestore.cc b/firestore/src/common/firestore.cc index a7695a7b1..b5484c684 100644 --- a/firestore/src/common/firestore.cc +++ b/firestore/src/common/firestore.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include "app/meta/move.h" #include "app/src/cleanup_notifier.h" @@ -63,25 +64,37 @@ const char* GetPlatform() { #endif } +// Use the combination of the App address and database ID to identify a +// firestore instance in cache. +using FirestoreMap = std::map, Firestore*>; + +FirestoreMap::key_type MakeKey(App* app, std::string database_id) { + return std::make_pair(app, std::move(database_id)); +} + Mutex* g_firestores_lock = new Mutex(); -std::map* g_firestores = nullptr; +FirestoreMap* g_firestores = nullptr; // Ensures that the cache is initialized. // Prerequisite: `g_firestores_lock` must be locked before calling this // function. -std::map* FirestoreCache() { +FirestoreMap* FirestoreCache() { if (!g_firestores) { - g_firestores = new std::map(); + g_firestores = new FirestoreMap(); } return g_firestores; } // Prerequisite: `g_firestores_lock` must be locked before calling this // function. -Firestore* FindFirestoreInCache(App* app, InitResult* init_result_out) { +Firestore* FindFirestoreInCache(App* app, + const std::string& database_id, + InitResult* init_result_out) { auto* cache = FirestoreCache(); - auto found = cache->find(app); + FirestoreMap::key_type key = MakeKey(app, database_id); + FirestoreMap::iterator found = g_firestores->find(key); + if (found != cache->end()) { if (init_result_out) *init_result_out = kInitResultSuccess; return found->second; @@ -101,36 +114,61 @@ InitResult CheckInitialized(const FirestoreInternal& firestore) { void ValidateApp(App* app) { if (!app) { SimpleThrowInvalidArgument( - "firebase::App instance cannot be null. Use " - "firebase::App::GetInstance() without arguments if you'd like to use " - "the default instance."); + "firebase::App instance cannot be null. Use other " + "Firestore::GetInstance() if you'd like to use the default " + "app instance."); } } -} // namespace - -Firestore* Firestore::GetInstance(App* app, InitResult* init_result_out) { - ValidateApp(app); +void ValidateDatabase(const char* database_id) { + if (!database_id) { + SimpleThrowInvalidArgument( + "Provided database ID must not be null. Use other " + "Firestore::GetInstance() if you'd like to use the default " + "database ID."); + } +} - MutexLock lock(*g_firestores_lock); +} // namespace - Firestore* from_cache = FindFirestoreInCache(app, init_result_out); - if (from_cache) { - return from_cache; +Firestore* Firestore::GetInstance(InitResult* init_result_out) { + App* app = App::GetInstance(); + if (!app) { + SimpleThrowInvalidArgument( + "Failed to get firebase::App instance. Please call " + "firebase::App::Create before using Firestore"); } + return Firestore::GetInstance(app, kDefaultDatabase, init_result_out); +} - return AddFirestoreToCache(new Firestore(app), init_result_out); +Firestore* Firestore::GetInstance(App* app, InitResult* init_result_out) { + return Firestore::GetInstance(app, kDefaultDatabase, init_result_out); } -Firestore* Firestore::GetInstance(InitResult* init_result_out) { +Firestore* Firestore::GetInstance(const char* db_name, + InitResult* init_result_out) { App* app = App::GetInstance(); if (!app) { SimpleThrowInvalidArgument( "Failed to get firebase::App instance. Please call " "firebase::App::Create before using Firestore"); } + return Firestore::GetInstance(app, db_name, init_result_out); +} - return Firestore::GetInstance(app, init_result_out); +Firestore* Firestore::GetInstance(App* app, + const char* db_name, + InitResult* init_result_out) { + ValidateApp(app); + ValidateDatabase(db_name); + + MutexLock lock(*g_firestores_lock); + Firestore* from_cache = FindFirestoreInCache(app, db_name, init_result_out); + if (from_cache) { + return from_cache; + } + + return AddFirestoreToCache(new Firestore(app, db_name), init_result_out); } Firestore* Firestore::CreateFirestore(App* app, @@ -142,7 +180,9 @@ Firestore* Firestore::CreateFirestore(App* app, MutexLock lock(*g_firestores_lock); - Firestore* from_cache = FindFirestoreInCache(app, init_result_out); + const std::string& database_id = internal->database_name(); + Firestore* from_cache = + FindFirestoreInCache(app, database_id, init_result_out); SIMPLE_HARD_ASSERT(from_cache == nullptr, "Firestore must not be created already"); @@ -160,12 +200,14 @@ Firestore* Firestore::AddFirestoreToCache(Firestore* firestore, return nullptr; } - FirestoreCache()->emplace(firestore->app(), firestore); + FirestoreMap::key_type key = + MakeKey(firestore->app(), firestore->internal_->database_name()); + FirestoreCache()->emplace(std::move(key), firestore); return firestore; } -Firestore::Firestore(::firebase::App* app) - : Firestore{new FirestoreInternal{app}} {} +Firestore::Firestore(::firebase::App* app, const std::string& database_id) + : Firestore{new FirestoreInternal{app, database_id}} {} Firestore::Firestore(FirestoreInternal* internal) // TODO(wuandy): use make_unique once it is supported for our build here. @@ -201,6 +243,8 @@ void Firestore::DeleteInternal() { if (!internal_) return; App* my_app = app(); + // Store the database id before deleting the firestore instance. + const std::string database_id = internal_->database_name(); // Only need to unregister if internal_ is initialized. if (internal_->initialized()) { @@ -224,8 +268,9 @@ void Firestore::DeleteInternal() { delete internal_; internal_ = nullptr; // If a Firestore is explicitly deleted, remove it from our cache. - FirestoreCache()->erase(my_app); - // If it's the last one, delete the map. + + FirestoreMap::key_type key = MakeKey(my_app, database_id); + FirestoreCache()->erase(key); if (g_firestores->empty()) { delete g_firestores; g_firestores = nullptr; @@ -337,7 +382,9 @@ Future Firestore::EnableNetwork() { Future Firestore::Terminate() { if (!internal_) return FailedFuture(); - FirestoreCache()->erase(app()); + FirestoreMap::key_type key = MakeKey(app(), internal_->database_name()); + + FirestoreCache()->erase(key); return internal_->Terminate(); } diff --git a/firestore/src/include/firebase/firestore.h b/firestore/src/include/firebase/firestore.h index 1749a4e3b..7cc027481 100644 --- a/firestore/src/include/firebase/firestore.h +++ b/firestore/src/include/firebase/firestore.h @@ -71,6 +71,9 @@ class TransactionManager; } // namespace csharp +/** The default name for "unset" database ID in resource names. */ +static const char* kDefaultDatabase = "(default)"; + /** * @brief Entry point for the Firebase Firestore C++ SDK. * @@ -88,7 +91,8 @@ class TransactionManager; class Firestore { public: /** - * @brief Returns an instance of Firestore corresponding to the given App. + * @brief Returns an instance of Firestore corresponding to the given App + * with default database ID. * * Firebase Firestore uses firebase::App to communicate with Firebase * Authentication to authenticate users to the Firestore server backend. @@ -103,13 +107,15 @@ class Firestore { * succeeded, or firebase::kInitResultFailedMissingDependency on Android if * Google Play services is not available on the current device. * - * @return An instance of Firestore corresponding to the given App. + * @return An instance of Firestore corresponding to the given App with + * default database ID. */ static Firestore* GetInstance(::firebase::App* app, InitResult* init_result_out = nullptr); /** - * @brief Returns an instance of Firestore corresponding to the default App. + * @brief Returns an instance of Firestore corresponding to the default App + * with default database ID. * * Firebase Firestore uses the default App to communicate with Firebase * Authentication to authenticate users to the Firestore server backend. @@ -121,10 +127,60 @@ class Firestore { * succeeded, or firebase::kInitResultFailedMissingDependency on Android if * Google Play services is not available on the current device. * - * @return An instance of Firestore corresponding to the default App. + * @return An instance of Firestore corresponding to the default App + * with default database ID. */ static Firestore* GetInstance(InitResult* init_result_out = nullptr); + /** + * @brief Returns an instance of Firestore corresponding to the given App with + * the given database ID. + * + * Firebase Firestore uses firebase::App to communicate with Firebase + * Authentication to authenticate users to the Firestore server backend. + * + * If you call GetInstance() multiple times with the same App, you will get + * the same instance of Firestore. + * + * @param[in] app Your instance of firebase::App. Firebase Firestore will use + * this to communicate with Firebase Authentication. + * @param[in] db_name Name of the database. Firebase Firestore will use + * this to communicate with Firebase Authentication. + * @param[out] init_result_out If provided, the initialization result will be + * written here. Will be set to firebase::kInitResultSuccess if initialization + * succeeded, or firebase::kInitResultFailedMissingDependency on Android if + * Google Play services is not available on the current device. + * + * @return An instance of Firestore corresponding to the given App with + * the given database ID. + */ + static Firestore* GetInstance(::firebase::App* app, + const char* db_name, + InitResult* init_result_out = nullptr); + + /** + * @brief Returns an instance of Firestore corresponding to the default App + * with the given database ID. + * + * Firebase Firestore uses firebase::App to communicate with Firebase + * Authentication to authenticate users to the Firestore server backend. + * + * If you call GetInstance() multiple times with the same App, you will get + * the same instance of Firestore. + * + * @param[in] db_name Name of the database. Firebase Firestore will use + * this to communicate with Firebase Authentication. + * @param[out] init_result_out If provided, the initialization result will be + * written here. Will be set to firebase::kInitResultSuccess if initialization + * succeeded, or firebase::kInitResultFailedMissingDependency on Android if + * Google Play services is not available on the current device. + * + * @return An instance of Firestore corresponding to the default App with + * the given database ID. + */ + static Firestore* GetInstance(const char* db_name, + InitResult* init_result_out = nullptr); + /** * @brief Destructor for the Firestore object. * @@ -438,7 +494,8 @@ class Firestore { friend class csharp::ApiHeaders; friend class csharp::TransactionManager; - explicit Firestore(::firebase::App* app); + explicit Firestore(::firebase::App* app, + const std::string& database_id = kDefaultDatabase); explicit Firestore(FirestoreInternal* internal); static Firestore* CreateFirestore(::firebase::App* app, diff --git a/firestore/src/main/firestore_main.cc b/firestore/src/main/firestore_main.cc index 1616e3453..8a512a49e 100644 --- a/firestore/src/main/firestore_main.cc +++ b/firestore/src/main/firestore_main.cc @@ -102,19 +102,23 @@ void ValidateDoubleSlash(const char* path) { } // namespace -FirestoreInternal::FirestoreInternal(App* app) - : FirestoreInternal{app, CreateCredentialsProvider(*app), +FirestoreInternal::FirestoreInternal(App* app, const std::string& database_id) + : FirestoreInternal{app, database_id, CreateCredentialsProvider(*app), CreateAppCheckCredentialsProvider(*app)} {} FirestoreInternal::FirestoreInternal( App* app, + const std::string& database_id, std::unique_ptr auth_credentials, std::unique_ptr app_check_credentials) : app_(NOT_NULL(app)), - firestore_core_(CreateFirestore( - app, std::move(auth_credentials), std::move(app_check_credentials))), + firestore_core_(CreateFirestore(app, + database_id, + std::move(auth_credentials), + std::move(app_check_credentials))), transaction_executor_(absl::ShareUniquePtr(Executor::CreateConcurrent( - "com.google.firebase.firestore.transaction", /*threads=*/5))) { + "com.google.firebase.firestore.transaction", /*threads=*/5))), + database_name_(database_id) { ApplyDefaultSettings(); #if FIREBASE_PLATFORM_ANDROID @@ -132,13 +136,14 @@ FirestoreInternal::~FirestoreInternal() { std::shared_ptr FirestoreInternal::CreateFirestore( App* app, + const std::string& database_id, std::unique_ptr auth_credentials, std::unique_ptr app_check_credentials) { const AppOptions& opt = app->options(); return std::make_shared( - DatabaseId{opt.project_id()}, app->name(), std::move(auth_credentials), - std::move(app_check_credentials), CreateWorkerQueue(), - CreateFirebaseMetadataProvider(*app), this); + DatabaseId{opt.project_id(), database_id}, app->name(), + std::move(auth_credentials), std::move(app_check_credentials), + CreateWorkerQueue(), CreateFirebaseMetadataProvider(*app), this); } CollectionReference FirestoreInternal::Collection( diff --git a/firestore/src/main/firestore_main.h b/firestore/src/main/firestore_main.h index 49b377954..7920eccc4 100644 --- a/firestore/src/main/firestore_main.h +++ b/firestore/src/main/firestore_main.h @@ -21,6 +21,7 @@ #include #include #include // NOLINT(build/c++11) +#include #include #include "Firestore/core/src/api/firestore.h" @@ -55,7 +56,7 @@ class Executor; class FirestoreInternal { public: // Note: call `set_firestore_public` immediately after construction. - explicit FirestoreInternal(App* app); + FirestoreInternal(App* app, const std::string& database_id); ~FirestoreInternal(); FirestoreInternal(const FirestoreInternal&) = delete; @@ -107,6 +108,8 @@ class FirestoreInternal { return firestore_core_->database_id(); } + const std::string& database_name() const { return database_name_; } + // Bundles Future LoadBundle(const std::string& bundle); Future LoadBundle( @@ -150,12 +153,14 @@ class FirestoreInternal { FirestoreInternal( App* app, + const std::string& database_id, std::unique_ptr auth_credentials, std::unique_ptr app_check_credentials); std::shared_ptr CreateFirestore( App* app, + const std::string& database_id, std::unique_ptr auth_credentials, std::unique_ptr app_check_credentials); @@ -182,6 +187,7 @@ class FirestoreInternal { std::unordered_set listeners_; std::shared_ptr transaction_executor_; + std::string database_name_; }; } // namespace firestore diff --git a/release_build_files/readme.md b/release_build_files/readme.md index 43bd213df..b35ddaf9b 100644 --- a/release_build_files/readme.md +++ b/release_build_files/readme.md @@ -633,6 +633,7 @@ code. home directory contains non-ANSI characters (Unicode above U+00FF). - Storage (Desktop): Fixed a crash on Windows when uploading files from a path containing non-ANSI characters (Unicode above U+00FF). + - Firestore: Added MultiDb support. ([#1321](https://github.com/firebase/firebase-cpp-sdk/pull/1321)). ### 11.0.1 - Changes diff --git a/testing/test_framework/src/firebase_test_framework.h b/testing/test_framework/src/firebase_test_framework.h index 8c2ce92d0..cff9ef5ae 100644 --- a/testing/test_framework/src/firebase_test_framework.h +++ b/testing/test_framework/src/firebase_test_framework.h @@ -58,6 +58,17 @@ namespace firebase_test_framework { return; \ } +#define RUN_TEST_ONLY_AGAINST_FIRESTORE_EMULATOR \ + { \ + if (std::getenv("USE_FIRESTORE_EMULATOR") == nullptr) { \ + app_framework::LogInfo( \ + "Skipping %s on non firestore emulator environment.", \ + test_info_->name()); \ + GTEST_SKIP(); \ + return; \ + } \ + } + #if TARGET_OS_IPHONE #define TEST_REQUIRES_USER_INTERACTION_ON_IOS TEST_REQUIRES_USER_INTERACTION #define TEST_REQUIRES_USER_INTERACTION_ON_ANDROID ((void)0)