Skip to content

Commit f8240f7

Browse files
Googlera-maurice
authored andcommitted
Check that calls to Terminate() and ClearPersistence() actually succeed.
I had incorrectly assumed that `Await()` would fail the test if the `Future` it was specified failed. This incorrect assumption hid the fact that `ClearPersistence()` failed on Android, resulting in unexplained downstream failures, resulting in a rollback: cl/326267389. With this change, the failure of the `Future` objects returned from `ClearPersistence()` and `Terminate()` will be noted, highlighting the source of the test failures. PiperOrigin-RevId: 330777066
1 parent 33d90f4 commit f8240f7

File tree

5 files changed

+134
-22
lines changed

5 files changed

+134
-22
lines changed

firestore/src/tests/firestore_integration_test.cc

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ void LocateEmulator(Firestore* db) {
4141
}
4242
}
4343

44+
} // namespace
45+
4446
std::string ToFirestoreErrorCodeName(int error_code) {
4547
switch (error_code) {
4648
case kErrorOk:
@@ -82,7 +84,19 @@ std::string ToFirestoreErrorCodeName(int error_code) {
8284
}
8385
}
8486

85-
} // anonymous namespace
87+
int WaitFor(const FutureBase& future) {
88+
// Instead of getting a clock, we count the cycles instead.
89+
int cycles = kTimeOutMillis / kCheckIntervalMillis;
90+
while (future.status() == FutureStatus::kFutureStatusPending && cycles > 0) {
91+
if (ProcessEvents(kCheckIntervalMillis)) {
92+
std::cout << "WARNING: app receives an event requesting exit."
93+
<< std::endl;
94+
break;
95+
}
96+
--cycles;
97+
}
98+
return cycles;
99+
}
86100

87101
FirestoreIntegrationTest::FirestoreIntegrationTest() {
88102
// Allocate the default Firestore eagerly.

firestore/src/tests/firestore_integration_test.h

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ App* GetApp();
2929
App* GetApp(const char* name);
3030
bool ProcessEvents(int msec);
3131

32+
// Converts a Firestore error code to a human-friendly name. The `error_code`
33+
// argument is expected to be an element from the firebase::firestore::Error
34+
// enum, but this function will gracefully handle the case where it is not.
35+
std::string ToFirestoreErrorCodeName(int error_code);
36+
37+
// Waits for a Future to complete. If a timeout is reached then this method
38+
// returns as if successful; therefore, the caller should verify the status of
39+
// the given Future after this function returns. Returns the number of cycles
40+
// that were left before a timeout would have occurred.
41+
int WaitFor(const FutureBase& future);
42+
3243
template <typename T>
3344
class EventAccumulator;
3445

@@ -222,17 +233,7 @@ class FirestoreIntegrationTest : public testing::Test {
222233
// A helper function to block until the future completes.
223234
template <typename T>
224235
static const T* Await(const Future<T>& future) {
225-
// Instead of getting a clock, we count the cycles instead.
226-
int cycles = kTimeOutMillis / kCheckIntervalMillis;
227-
while (future.status() == FutureStatus::kFutureStatusPending &&
228-
cycles > 0) {
229-
if (ProcessEvents(kCheckIntervalMillis)) {
230-
std::cout << "WARNING: app receives an event requesting exit."
231-
<< std::endl;
232-
return nullptr;
233-
}
234-
--cycles;
235-
}
236+
int cycles = WaitFor(future);
236237
EXPECT_GT(cycles, 0) << "Waiting future timed out.";
237238
if (future.status() == FutureStatus::kFutureStatusComplete) {
238239
if (future.result() == nullptr) {

firestore/src/tests/firestore_test.cc

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "firestore/src/include/firebase/firestore.h"
99
#include "firestore/src/tests/firestore_integration_test.h"
1010
#include "firestore/src/tests/util/event_accumulator.h"
11+
#include "firestore/src/tests/util/future_test_util.h"
1112
#if defined(__ANDROID__)
1213
#include "firestore/src/android/util_android.h"
1314
#endif // defined(__ANDROID__)
@@ -1179,7 +1180,7 @@ TEST_F(FirestoreIntegrationTest, TestToString) {
11791180
// exceptions.
11801181
#if defined(__ANDROID__)
11811182
TEST_F(FirestoreIntegrationTest, ClientCallsAfterTerminateFails) {
1182-
Await(firestore()->Terminate());
1183+
EXPECT_THAT(firestore()->Terminate(), FutureSucceeds());
11831184
EXPECT_THROW(Await(firestore()->DisableNetwork()), FirestoreException);
11841185
}
11851186

@@ -1188,7 +1189,7 @@ TEST_F(FirestoreIntegrationTest, NewOperationThrowsAfterFirestoreTerminate) {
11881189
DocumentReference reference = firestore()->Document("abc/123");
11891190
Await(reference.Set({{"Field", FieldValue::Integer(100)}}));
11901191

1191-
Await(instance->Terminate());
1192+
EXPECT_THAT(instance->Terminate(), FutureSucceeds());
11921193

11931194
EXPECT_THROW(Await(reference.Get()), FirestoreException);
11941195
EXPECT_THROW(Await(reference.Update({{"Field", FieldValue::Integer(1)}})),
@@ -1214,12 +1215,12 @@ TEST_F(FirestoreIntegrationTest, TerminateCanBeCalledMultipleTimes) {
12141215
DocumentReference reference = instance->Document("abc/123");
12151216
Await(reference.Set({{"Field", FieldValue::Integer(100)}}));
12161217

1217-
Await(instance->Terminate());
1218+
EXPECT_THAT(instance->Terminate(), FutureSucceeds());
12181219

12191220
EXPECT_THROW(Await(reference.Get()), FirestoreException);
12201221

12211222
// Calling a second time should go through and change nothing.
1222-
Await(instance->Terminate());
1223+
EXPECT_THAT(instance->Terminate(), FutureSucceeds());
12231224

12241225
EXPECT_THROW(Await(reference.Update({{"Field", FieldValue::Integer(1)}})),
12251226
FirestoreException);
@@ -1244,7 +1245,7 @@ TEST_F(FirestoreIntegrationTest, RestartFirestoreLeadsToNewInstance) {
12441245

12451246
// Shutdown `db` and create a new instance, make sure they are different
12461247
// instances.
1247-
Await(db->Terminate());
1248+
EXPECT_THAT(db->Terminate(), FutureSucceeds());
12481249
auto db_2 = CreateFirestore(app->name());
12491250
EXPECT_NE(db_2, db);
12501251

@@ -1263,7 +1264,7 @@ TEST_F(FirestoreIntegrationTest, CanStopListeningAfterTerminate) {
12631264
accumulator.listener()->AttachTo(&reference);
12641265

12651266
accumulator.Await();
1266-
Await(instance->Terminate());
1267+
EXPECT_THAT(instance->Terminate(), FutureSucceeds());
12671268

12681269
// This should proceed without error.
12691270
registration.Remove();
@@ -1321,8 +1322,8 @@ TEST_F(FirestoreIntegrationTest, CanClearPersistenceAfterRestarting) {
13211322

13221323
// ClearPersistence() requires Firestore to be terminated. Delete the app and
13231324
// the Firestore instance to emulate the way an end user would do this.
1324-
Await(db->Terminate());
1325-
Await(db->ClearPersistence());
1325+
EXPECT_THAT(db->Terminate(), FutureSucceeds());
1326+
EXPECT_THAT(db->ClearPersistence(), FutureSucceeds());
13261327
Release(db);
13271328

13281329
// We restart the app with the same name and options to check that the
@@ -1347,7 +1348,7 @@ TEST_F(FirestoreIntegrationTest, CanClearPersistenceOnANewFirestoreInstance) {
13471348
std::string path = document.path();
13481349
WriteDocument(document, MapFieldValue{{"foo", FieldValue::Integer(42)}});
13491350

1350-
Await(db->Terminate());
1351+
EXPECT_THAT(db->Terminate(), FutureSucceeds());
13511352
delete db;
13521353
delete app;
13531354

@@ -1356,7 +1357,7 @@ TEST_F(FirestoreIntegrationTest, CanClearPersistenceOnANewFirestoreInstance) {
13561357
// restart. Calling firestore() here would create a new instance of firestore,
13571358
// which defeats the purpose of this test.
13581359
Firestore* db_2 = CreateFirestore(app_name);
1359-
Await(db_2->ClearPersistence());
1360+
EXPECT_THAT(db_2->ClearPersistence(), FutureSucceeds());
13601361
DocumentReference document_2 = db_2->Document(path);
13611362
Future<DocumentSnapshot> await_get = document_2.Get(Source::kCache);
13621363
Await(await_get);
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#include "firestore/src/tests/util/future_test_util.h"
2+
3+
#include <ostream>
4+
5+
#include "firestore/src/tests/firestore_integration_test.h"
6+
#include "firebase/firestore/firestore_errors.h"
7+
8+
namespace firebase {
9+
10+
namespace {
11+
12+
void PrintTo(std::ostream* os, FutureStatus future_status, int error,
13+
const char* error_message = nullptr) {
14+
*os << "Future<void>{status=" << ToEnumeratorName(future_status)
15+
<< " error=" << firestore::ToFirestoreErrorCodeName(error);
16+
if (error_message != nullptr) {
17+
*os << " error_message=" << error_message;
18+
}
19+
*os << "}";
20+
}
21+
22+
class FutureSucceedsImpl
23+
: public testing::MatcherInterface<const Future<void>&> {
24+
public:
25+
void DescribeTo(std::ostream* os) const override {
26+
PrintTo(os, FutureStatus::kFutureStatusComplete,
27+
firestore::Error::kErrorOk);
28+
}
29+
30+
bool MatchAndExplain(const Future<void>& future,
31+
testing::MatchResultListener*) const override {
32+
firestore::WaitFor(future);
33+
return future.status() == FutureStatus::kFutureStatusComplete &&
34+
future.error() == firestore::Error::kErrorOk;
35+
}
36+
};
37+
38+
} // namespace
39+
40+
std::string ToEnumeratorName(FutureStatus status) {
41+
switch (status) {
42+
case kFutureStatusComplete:
43+
return "kFutureStatusComplete";
44+
case kFutureStatusPending:
45+
return "kFutureStatusPending";
46+
case kFutureStatusInvalid:
47+
return "kFutureStatusInvalid";
48+
default:
49+
return "[invalid FutureStatus]";
50+
}
51+
}
52+
53+
void PrintTo(const Future<void>& future, std::ostream* os) {
54+
PrintTo(os, future.status(), future.error(), future.error_message());
55+
}
56+
57+
testing::Matcher<const Future<void>&> FutureSucceeds() {
58+
return testing::Matcher<const Future<void>&>(new FutureSucceedsImpl());
59+
}
60+
61+
} // namespace firebase
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#ifndef FIREBASE_FIRESTORE_CLIENT_CPP_SRC_TESTS_UTIL_FUTURE_TEST_UTIL_H_
2+
#define FIREBASE_FIRESTORE_CLIENT_CPP_SRC_TESTS_UTIL_FUTURE_TEST_UTIL_H_
3+
4+
#include <iosfwd>
5+
#include <string>
6+
7+
#include "app/src/include/firebase/future.h"
8+
#include "third_party/googletest/googletest/include/gtest/gtest-matchers.h"
9+
10+
namespace firebase {
11+
12+
// Prints a human-friendly representation of a Future to an ostream.
13+
// This function is found dynamically by googletest to print a Future object
14+
// in a test failure message.
15+
void PrintTo(const Future<void>& future, std::ostream* os);
16+
17+
// Creates and returns a "matcher" for a Future's success. The matcher will wait
18+
// for the Future to complete with a timeout. If the timeout is reached or the
19+
// Future completes unsuccessfully then the matcher will fail; otherwise, it
20+
// will succeed.
21+
//
22+
// Here is an example of how this function could be used:
23+
// EXPECT_THAT(firestore()->Terminate(), FutureSucceeds());
24+
testing::Matcher<const Future<void>&> FutureSucceeds();
25+
26+
// Converts a `FutureStatus` value to its enumerator name, and returns it. For
27+
// example, if `kFutureStatusComplete` is specified then "kFutureStatusComplete"
28+
// will be returned. If the argument is invalid (i.e. not equal to any element
29+
// from `FutureStatus`) then this function will gracefully return a "name" to
30+
// indicate this.
31+
std::string ToEnumeratorName(FutureStatus status);
32+
33+
} // namespace firebase
34+
35+
#endif // FIREBASE_FIRESTORE_CLIENT_CPP_SRC_TESTS_UTIL_FUTURE_TEST_UTIL_H_

0 commit comments

Comments
 (0)