Skip to content

Commit ef7dff1

Browse files
authored
Firestore: Improve logging of timeouts in integration tests (#1030)
1 parent 69b870d commit ef7dff1

File tree

5 files changed

+88
-15
lines changed

5 files changed

+88
-15
lines changed

firestore/integration_test_internal/src/firestore_integration_test.cc

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include <cstdlib>
2020
#include <fstream>
21+
#include <iomanip>
2122
#include <memory>
2223
#include <sstream>
2324

@@ -125,6 +126,16 @@ int WaitFor(const FutureBase& future) {
125126
return cycles;
126127
}
127128

129+
std::ostream& operator<<(std::ostream& out, const Stopwatch& stopwatch) {
130+
// Use a temporary ostream instead of writing directly to the given `out`
131+
// stream because std::setprecision has effects that extend beyond the
132+
// lifetime of this function and we shouldn't be changing the state of the
133+
// given stream.
134+
std::ostringstream oss;
135+
oss << std::setprecision(3) << stopwatch.elapsed_time().count() << " seconds";
136+
return out << oss.str();
137+
}
138+
128139
FirestoreIntegrationTest::FirestoreIntegrationTest() {
129140
// Allocate the default Firestore eagerly.
130141
TestFirestore();
@@ -227,8 +238,10 @@ DocumentReference FirestoreIntegrationTest::DocumentWithData(
227238
void FirestoreIntegrationTest::WriteDocument(DocumentReference reference,
228239
const MapFieldValue& data) const {
229240
Future<void> future = reference.Set(data);
241+
Stopwatch stopwatch;
230242
Await(future);
231-
FailIfUnsuccessful("WriteDocument", future);
243+
stopwatch.stop();
244+
FailIfUnsuccessful("WriteDocument", future, stopwatch);
232245
}
233246

234247
void FirestoreIntegrationTest::WriteDocuments(
@@ -242,8 +255,10 @@ void FirestoreIntegrationTest::WriteDocuments(
242255
DocumentSnapshot FirestoreIntegrationTest::ReadDocument(
243256
const DocumentReference& reference) const {
244257
Future<DocumentSnapshot> future = reference.Get();
258+
Stopwatch stopwatch;
245259
const DocumentSnapshot* result = Await(future);
246-
if (FailIfUnsuccessful("ReadDocument", future)) {
260+
stopwatch.stop();
261+
if (FailIfUnsuccessful("ReadDocument", future, stopwatch)) {
247262
return {};
248263
} else {
249264
return *result;
@@ -253,8 +268,10 @@ DocumentSnapshot FirestoreIntegrationTest::ReadDocument(
253268
QuerySnapshot FirestoreIntegrationTest::ReadDocuments(
254269
const Query& reference) const {
255270
Future<QuerySnapshot> future = reference.Get();
271+
Stopwatch stopwatch;
256272
const QuerySnapshot* result = Await(future);
257-
if (FailIfUnsuccessful("ReadDocuments", future)) {
273+
stopwatch.stop();
274+
if (FailIfUnsuccessful("ReadDocuments", future, stopwatch)) {
258275
return {};
259276
} else {
260277
return *result;
@@ -264,8 +281,10 @@ QuerySnapshot FirestoreIntegrationTest::ReadDocuments(
264281
void FirestoreIntegrationTest::DeleteDocument(
265282
DocumentReference reference) const {
266283
Future<void> future = reference.Delete();
284+
Stopwatch stopwatch;
267285
Await(future);
268-
FailIfUnsuccessful("DeleteDocument", future);
286+
stopwatch.stop();
287+
FailIfUnsuccessful("DeleteDocument", future, stopwatch);
269288
}
270289

271290
std::vector<std::string> FirestoreIntegrationTest::QuerySnapshotToIds(
@@ -309,14 +328,15 @@ void FirestoreIntegrationTest::Await(const Future<void>& future) {
309328

310329
/* static */
311330
bool FirestoreIntegrationTest::FailIfUnsuccessful(const char* operation,
312-
const FutureBase& future) {
331+
const FutureBase& future,
332+
const Stopwatch& stopwatch) {
313333
if (future.status() != FutureStatus::kFutureStatusComplete) {
314-
ADD_FAILURE() << operation << " timed out: " << DescribeFailedFuture(future)
315-
<< std::endl;
334+
ADD_FAILURE() << operation << " timed out after " << stopwatch << ": "
335+
<< DescribeFailedFuture(future) << std::endl;
316336
return true;
317337
} else if (future.error() != Error::kErrorOk) {
318-
ADD_FAILURE() << operation << " failed: " << DescribeFailedFuture(future)
319-
<< std::endl;
338+
ADD_FAILURE() << operation << " failed after " << stopwatch << ": "
339+
<< DescribeFailedFuture(future) << std::endl;
320340
return true;
321341
} else {
322342
return false;

firestore/integration_test_internal/src/firestore_integration_test.h

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
#ifndef FIREBASE_FIRESTORE_INTEGRATION_TEST_INTERNAL_SRC_FIRESTORE_INTEGRATION_TEST_H_
1818
#define FIREBASE_FIRESTORE_INTEGRATION_TEST_INTERNAL_SRC_FIRESTORE_INTEGRATION_TEST_H_
1919

20+
#include <chrono>
2021
#include <cstdio>
2122
#include <iostream>
2223
#include <map>
2324
#include <memory>
25+
#include <mutex>
2426
#include <string>
2527
#include <unordered_map>
2628
#include <vector>
@@ -174,6 +176,37 @@ class TestEventListener {
174176
bool print_debug_info_ = false;
175177
};
176178

179+
// A stopwatch that can calculate the runtime of some operation.
180+
//
181+
// The motivating use case for this class is to include the elapsed time of
182+
// an operation that timed out in the timeout error message.
183+
class Stopwatch {
184+
public:
185+
Stopwatch() : start_time_(std::chrono::steady_clock::now()) {}
186+
187+
std::chrono::duration<double> elapsed_time() const {
188+
std::lock_guard<std::mutex> lock(mutex_);
189+
const auto t =
190+
stop_time_valid_ ? stop_time_ : std::chrono::steady_clock::now();
191+
return t - start_time_;
192+
}
193+
194+
void stop() {
195+
std::lock_guard<std::mutex> lock(mutex_);
196+
assert(!stop_time_valid_);
197+
stop_time_ = std::chrono::steady_clock::now();
198+
stop_time_valid_ = true;
199+
}
200+
201+
private:
202+
mutable std::mutex mutex_;
203+
decltype(std::chrono::steady_clock::now()) start_time_;
204+
decltype(std::chrono::steady_clock::now()) stop_time_;
205+
bool stop_time_valid_ = false;
206+
};
207+
208+
std::ostream& operator<<(std::ostream&, const Stopwatch&);
209+
177210
// A RAII wrapper that enables Firestore debug logging and then disables it
178211
// upon destruction.
179212
//
@@ -318,8 +351,9 @@ class FirestoreIntegrationTest : public testing::Test {
318351
// A helper function to block until the future completes.
319352
template <typename T>
320353
static const T* Await(const Future<T>& future) {
354+
Stopwatch stopwatch;
321355
int cycles = WaitFor(future);
322-
EXPECT_GT(cycles, 0) << "Waiting future timed out.";
356+
EXPECT_GT(cycles, 0) << "Waiting future timed out after " << stopwatch;
323357
if (future.status() == FutureStatus::kFutureStatusComplete) {
324358
if (future.result() == nullptr) {
325359
std::cout << "WARNING: " << DescribeFailedFuture(future) << std::endl;
@@ -336,6 +370,7 @@ class FirestoreIntegrationTest : public testing::Test {
336370
template <typename T>
337371
static void Await(const TestEventListener<T>& listener, int n = 1) {
338372
// Instead of getting a clock, we count the cycles instead.
373+
Stopwatch stopwatch;
339374
int cycles = kTimeOutMillis / kCheckIntervalMillis;
340375
while (listener.event_count() < n && cycles > 0) {
341376
if (ProcessEvents(kCheckIntervalMillis)) {
@@ -345,13 +380,15 @@ class FirestoreIntegrationTest : public testing::Test {
345380
}
346381
--cycles;
347382
}
348-
EXPECT_GT(cycles, 0) << "Waiting listener timed out.";
383+
EXPECT_GT(cycles, 0) << "Waiting listener timed out after " << stopwatch;
349384
}
350385

351386
// Fails the current test if the given future did not complete or contained an
352-
// error. Returns true if the future has failed.
387+
// error. Returns true if the future has failed. The given Stopwatch will be
388+
// used to include the elapsed time in any failure message.
353389
static bool FailIfUnsuccessful(const char* operation,
354-
const FutureBase& future);
390+
const FutureBase& future,
391+
const Stopwatch& stopwatch);
355392

356393
static std::string DescribeFailedFuture(const FutureBase& future);
357394

firestore/integration_test_internal/src/firestore_test.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1494,8 +1494,10 @@ TEST_F(FirestoreIntegrationTest, AuthWorks) {
14941494

14951495
// Signing in should trigger an AuthStateListener event.
14961496
auto signin = auth->SignInAnonymously();
1497+
Stopwatch stopwatch;
14971498
Await(signin);
1498-
FailIfUnsuccessful("SignInAnonymously", signin);
1499+
stopwatch.stop();
1500+
FailIfUnsuccessful("SignInAnonymously", signin, stopwatch);
14991501

15001502
// Writing again will trigger another pull of the token.
15011503
WriteDocument(doc, MapFieldValue{{"foo", FieldValue::Integer(43)}});

firestore/integration_test_internal/src/numeric_transforms_test.cc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ const char* TypeName(Type type) {
6565
return "kIncrementInteger";
6666
case Type::kIncrementDouble:
6767
return "kIncrementDouble";
68+
default:
69+
return "Unknown Type";
6870
}
6971
}
7072

@@ -81,6 +83,7 @@ void PrintTo(const FieldValue& f, std::ostream* os) {
8183
class NumericTransformsTest : public FirestoreIntegrationTest {
8284
public:
8385
NumericTransformsTest() {
86+
SCOPED_TRACE("NumericTransformsTest constructor");
8487
doc_ref_ = Document();
8588
listener_ =
8689
accumulator_.listener()->AttachTo(&doc_ref_, MetadataChanges::kInclude);
@@ -96,18 +99,20 @@ class NumericTransformsTest : public FirestoreIntegrationTest {
9699
/** Writes values and waits for the corresponding snapshot. */
97100
void WriteInitialData(const MapFieldValue& doc) {
98101
WriteDocument(doc_ref_, doc);
99-
102+
SCOPED_TRACE("WriteInitialData");
100103
accumulator_.AwaitRemoteEvent();
101104
}
102105

103106
void ExpectLocalAndRemoteValue(int value) {
107+
SCOPED_TRACE("ExpectLocalAndRemoteValue");
104108
DocumentSnapshot snap = accumulator_.AwaitLocalEvent();
105109
ASSERT_EQ(snap.Get("sum"), FieldValue::Integer(value));
106110
snap = accumulator_.AwaitRemoteEvent();
107111
ASSERT_EQ(snap.Get("sum"), FieldValue::Integer(value));
108112
}
109113

110114
void ExpectLocalAndRemoteValue(double value) {
115+
SCOPED_TRACE("ExpectLocalAndRemoteValue");
111116
DocumentSnapshot snap = accumulator_.AwaitLocalEvent();
112117
ASSERT_EQ(snap.Get("sum"), FieldValue::Double(value));
113118
snap = accumulator_.AwaitRemoteEvent();

firestore/integration_test_internal/src/util/event_accumulator.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#ifndef FIREBASE_FIRESTORE_INTEGRATION_TEST_INTERNAL_SRC_UTIL_EVENT_ACCUMULATOR_H_
1818
#define FIREBASE_FIRESTORE_INTEGRATION_TEST_INTERNAL_SRC_UTIL_EVENT_ACCUMULATOR_H_
1919

20+
#include <vector>
21+
2022
#include "firestore_integration_test.h"
2123

2224
namespace firebase {
@@ -31,6 +33,8 @@ class EventAccumulator {
3133
TestEventListener<T>* listener() { return &listener_; }
3234

3335
std::vector<T> Await(int num_events) {
36+
SCOPED_TRACE("EventAccumulator::Await() num_events=" +
37+
std::to_string(num_events));
3438
int old_num_events = num_events_consumed_;
3539
int desired_events = num_events_consumed_ + num_events;
3640
FirestoreIntegrationTest::Await(listener_, desired_events);
@@ -54,6 +58,7 @@ class EventAccumulator {
5458

5559
/** Awaits 1 event. */
5660
T Await() {
61+
SCOPED_TRACE("EventAccumulator::Await()");
5762
auto events = Await(1);
5863
if (events.empty()) {
5964
return {};
@@ -64,6 +69,7 @@ class EventAccumulator {
6469

6570
/** Waits for a snapshot with pending writes. */
6671
T AwaitLocalEvent() {
72+
SCOPED_TRACE("EventAccumulator::AwaitLocalEvent()");
6773
T event;
6874
do {
6975
event = Await();
@@ -73,6 +79,7 @@ class EventAccumulator {
7379

7480
/** Waits for a snapshot that has no pending writes. */
7581
T AwaitRemoteEvent() {
82+
SCOPED_TRACE("EventAccumulator::AwaitRemoteEvent()");
7683
T event;
7784
do {
7885
event = Await();
@@ -85,6 +92,7 @@ class EventAccumulator {
8592
* NOTE: Helper exists only in C++ test. Not in native SDK test yet.
8693
*/
8794
T AwaitCacheEvent() {
95+
SCOPED_TRACE("EventAccumulator::AwaitCacheEvent()");
8896
T event;
8997
do {
9098
event = Await();
@@ -97,6 +105,7 @@ class EventAccumulator {
97105
* NOTE: Helper exists only in C++ test. Not in native SDK test yet.
98106
*/
99107
T AwaitServerEvent() {
108+
SCOPED_TRACE("EventAccumulator::AwaitServerEvent()");
100109
T event;
101110
do {
102111
event = Await();

0 commit comments

Comments
 (0)