Skip to content

Commit ba332be

Browse files
authored
Port FSTAsyncQueryListener to C++ (#2620)
* Test that shows a leak. * Add EventListener to match Android. * Use AsyncEventListener in FSTQueryListenerTests * Remove FSTAsyncQueryListener * Use EventListener throughout
1 parent fd5ea77 commit ba332be

20 files changed

+441
-287
lines changed

Firestore/Example/Tests/Core/FSTEventManagerTests.mm

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,25 +35,26 @@
3535
#include "Firestore/core/src/firebase/firestore/util/statusor.h"
3636
#include "Firestore/core/test/firebase/firestore/testutil/xcgmock.h"
3737

38+
using firebase::firestore::core::EventListener;
3839
using firebase::firestore::core::ListenOptions;
3940
using firebase::firestore::core::ViewSnapshot;
40-
using firebase::firestore::core::ViewSnapshotHandler;
4141
using firebase::firestore::model::DocumentKeySet;
4242
using firebase::firestore::model::DocumentSet;
4343
using firebase::firestore::model::OnlineState;
4444
using firebase::firestore::util::StatusOr;
45+
using firebase::firestore::util::StatusOrCallback;
4546
using testing::ElementsAre;
4647

4748
NS_ASSUME_NONNULL_BEGIN
4849

4950
namespace {
5051

51-
ViewSnapshotHandler NoopViewSnapshotHandler() {
52-
return [](const StatusOr<ViewSnapshot> &) {};
52+
ViewSnapshot::Listener NoopViewSnapshotHandler() {
53+
return EventListener<ViewSnapshot>::Create([](const StatusOr<ViewSnapshot> &) {});
5354
}
5455

5556
std::shared_ptr<QueryListener> NoopQueryListener(FSTQuery *query) {
56-
return QueryListener::Create(query, NoopViewSnapshotHandler());
57+
return QueryListener::Create(query, ListenOptions::DefaultOptions(), NoopViewSnapshotHandler());
5758
}
5859

5960
} // namespace
@@ -112,17 +113,14 @@ - (void)testNotifiesListenersInTheRightOrder {
112113
FSTQuery *query2 = FSTTestQuery("bar/baz");
113114
NSMutableArray *eventOrder = [NSMutableArray array];
114115

115-
auto listener1 = QueryListener::Create(query1, [eventOrder](const StatusOr<ViewSnapshot> &) {
116-
[eventOrder addObject:@"listener1"];
117-
});
116+
auto listener1 = QueryListener::Create(
117+
query1, [eventOrder](StatusOr<ViewSnapshot>) { [eventOrder addObject:@"listener1"]; });
118118

119-
auto listener2 = QueryListener::Create(query2, [eventOrder](const StatusOr<ViewSnapshot> &) {
120-
[eventOrder addObject:@"listener2"];
121-
});
119+
auto listener2 = QueryListener::Create(
120+
query2, [eventOrder](StatusOr<ViewSnapshot>) { [eventOrder addObject:@"listener2"]; });
122121

123-
auto listener3 = QueryListener::Create(query1, [eventOrder](const StatusOr<ViewSnapshot> &) {
124-
[eventOrder addObject:@"listener3"];
125-
});
122+
auto listener3 = QueryListener::Create(
123+
query1, [eventOrder](StatusOr<ViewSnapshot>) { [eventOrder addObject:@"listener3"]; });
126124

127125
FSTSyncEngine *syncEngineMock = OCMClassMock([FSTSyncEngine class]);
128126
FSTEventManager *eventManager = [FSTEventManager eventManagerWithSyncEngine:syncEngineMock];

Firestore/Example/Tests/Core/FSTQueryListenerTests.mm

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@
2424
#import "Firestore/Source/Core/FSTQuery.h"
2525
#import "Firestore/Source/Core/FSTView.h"
2626
#import "Firestore/Source/Model/FSTDocument.h"
27-
#import "Firestore/Source/Util/FSTAsyncQueryListener.h"
2827

2928
#import "Firestore/Example/Tests/Util/FSTHelpers.h"
3029

3130
#include "Firestore/core/include/firebase/firestore/firestore_errors.h"
31+
#include "Firestore/core/src/firebase/firestore/core/event_listener.h"
3232
#include "Firestore/core/src/firebase/firestore/core/view_snapshot.h"
3333
#include "Firestore/core/src/firebase/firestore/model/document_set.h"
3434
#include "Firestore/core/src/firebase/firestore/model/types.h"
@@ -40,10 +40,12 @@
4040
#include "Firestore/core/test/firebase/firestore/testutil/xcgmock.h"
4141

4242
using firebase::firestore::FirestoreErrorCode;
43+
using firebase::firestore::core::AsyncEventListener;
44+
using firebase::firestore::core::EventListener;
4345
using firebase::firestore::core::DocumentViewChange;
46+
using firebase::firestore::core::EventListener;
4447
using firebase::firestore::core::ListenOptions;
4548
using firebase::firestore::core::ViewSnapshot;
46-
using firebase::firestore::core::ViewSnapshotHandler;
4749
using firebase::firestore::model::DocumentKeySet;
4850
using firebase::firestore::model::DocumentSet;
4951
using firebase::firestore::model::OnlineState;
@@ -72,10 +74,11 @@ ViewSnapshot ExcludingMetadataChanges(const ViewSnapshot &snapshot) {
7274
};
7375
}
7476

75-
ViewSnapshotHandler Accumulating(std::vector<ViewSnapshot> *values) {
76-
return [values](const StatusOr<ViewSnapshot> &maybe_snapshot) {
77-
values->push_back(maybe_snapshot.ValueOrDie());
78-
};
77+
ViewSnapshot::Listener Accumulating(std::vector<ViewSnapshot> *values) {
78+
return EventListener<ViewSnapshot>::Create(
79+
[values](const StatusOr<ViewSnapshot> &maybe_snapshot) {
80+
values->push_back(maybe_snapshot.ValueOrDie());
81+
});
7982
}
8083

8184
} // namespace
@@ -166,26 +169,26 @@ - (void)testRaisesEventForEmptyCollectionAfterSync {
166169
}
167170

168171
- (void)testMutingAsyncListenerPreventsAllSubsequentEvents {
169-
__block std::vector<ViewSnapshot> accum;
172+
std::vector<ViewSnapshot> accum;
170173

171174
FSTQuery *query = FSTTestQuery("rooms/Eros");
172175
FSTDocument *doc1 = FSTTestDoc("rooms/Eros", 3, @{@"name" : @"Eros"}, FSTDocumentStateSynced);
173176
FSTDocument *doc2 = FSTTestDoc("rooms/Eros", 4, @{@"name" : @"Eros2"}, FSTDocumentStateSynced);
174177

175-
__block FSTAsyncQueryListener *listener = [[FSTAsyncQueryListener alloc]
176-
initWithExecutor:_executor.get()
177-
snapshotHandler:^(const StatusOr<ViewSnapshot> &maybe_snapshot) {
178-
accum.push_back(maybe_snapshot.ValueOrDie());
179-
[listener mute];
180-
}];
178+
std::shared_ptr<AsyncEventListener<ViewSnapshot>> listener =
179+
AsyncEventListener<ViewSnapshot>::Create(
180+
_executor.get(), EventListener<ViewSnapshot>::Create(
181+
[&accum, &listener](const StatusOr<ViewSnapshot> &maybe_snapshot) {
182+
accum.push_back(maybe_snapshot.ValueOrDie());
183+
listener->Mute();
184+
}));
181185

182186
FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}];
183187
ViewSnapshot viewSnapshot1 = FSTTestApplyChanges(view, @[ doc1 ], absl::nullopt).value();
184188
ViewSnapshot viewSnapshot2 = FSTTestApplyChanges(view, @[ doc2 ], absl::nullopt).value();
185189

186-
ViewSnapshotHandler handler = listener.asyncSnapshotHandler;
187-
handler(viewSnapshot1);
188-
handler(viewSnapshot2);
190+
listener->OnEvent(viewSnapshot1);
191+
listener->OnEvent(viewSnapshot2);
189192

190193
// Drain queue
191194
XCTestExpectation *expectation = [self expectationWithDescription:@"Queue drained"];

Firestore/Example/Tests/Integration/API/FIRFirestoreSourceTests.mm

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,30 @@ - (void)testGetCollectionWhileOnlineWithDefaultSource {
6969
]));
7070
}
7171

72+
- (void)testGetDocumentError {
73+
FIRDocumentReference *doc = [self.db documentWithPath:@"foo/__invalid__"];
74+
75+
XCTestExpectation *completed = [self expectationWithDescription:@"get completed"];
76+
[doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
77+
XCTAssertNotNil(error);
78+
[completed fulfill];
79+
}];
80+
81+
[self awaitExpectations];
82+
}
83+
84+
- (void)testGetCollectionError {
85+
FIRCollectionReference *col = [self.db collectionWithPath:@"__invalid__"];
86+
87+
XCTestExpectation *completed = [self expectationWithDescription:@"get completed"];
88+
[col getDocumentsWithCompletion:^(FIRQuerySnapshot *snapshot, NSError *error) {
89+
XCTAssertNotNil(error);
90+
[completed fulfill];
91+
}];
92+
93+
[self awaitExpectations];
94+
}
95+
7296
- (void)testGetDocumentWhileOfflineWithDefaultSource {
7397
FIRDocumentReference *doc = [self documentRef];
7498

Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ - (TargetId)addUserListenerWithQuery:(FSTQuery *)query {
351351
// TODO(dimond): Allow customizing listen options in spec tests
352352
// TODO(dimond): Change spec tests to verify isFromCache on snapshots
353353
ListenOptions options = ListenOptions::FromIncludeMetadataChanges(true);
354-
auto listener = std::make_shared<QueryListener>(
354+
auto listener = QueryListener::Create(
355355
query, options, [self, query](const StatusOr<ViewSnapshot> &maybe_snapshot) {
356356
FSTQueryEvent *event = [[FSTQueryEvent alloc] init];
357357
event.query = query;

Firestore/Source/API/FIRDocumentReference.mm

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
#include "Firestore/core/src/firebase/firestore/api/document_reference.h"
3636
#include "Firestore/core/src/firebase/firestore/api/document_snapshot.h"
37+
#include "Firestore/core/src/firebase/firestore/core/event_listener.h"
3738
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
3839
#include "Firestore/core/src/firebase/firestore/model/document_set.h"
3940
#include "Firestore/core/src/firebase/firestore/model/precondition.h"
@@ -48,6 +49,7 @@
4849
using firebase::firestore::api::DocumentReference;
4950
using firebase::firestore::api::DocumentSnapshot;
5051
using firebase::firestore::api::Firestore;
52+
using firebase::firestore::core::EventListener;
5153
using firebase::firestore::core::ListenOptions;
5254
using firebase::firestore::core::ParsedSetData;
5355
using firebase::firestore::core::ParsedUpdateData;
@@ -213,21 +215,30 @@ - (void)getDocumentWithSource:(FIRFirestoreSource)source
213215
listener:(FIRDocumentSnapshotBlock)
214216
listener {
215217
ListenerRegistration result = _documentReference.AddSnapshotListener(
216-
[self wrapDocumentSnapshotBlock:listener], std::move(internalOptions));
218+
std::move(internalOptions), [self wrapDocumentSnapshotBlock:listener]);
217219
return [[FSTListenerRegistration alloc] initWithRegistration:std::move(result)];
218220
}
219221

220-
- (StatusOrCallback<DocumentSnapshot>)wrapDocumentSnapshotBlock:(FIRDocumentSnapshotBlock)block {
221-
FIRFirestore *firestore = self.firestore;
222-
return [block, firestore](StatusOr<DocumentSnapshot> maybe_snapshot) {
223-
if (maybe_snapshot.ok()) {
224-
FIRDocumentSnapshot *result =
225-
[[FIRDocumentSnapshot alloc] initWithSnapshot:std::move(maybe_snapshot).ValueOrDie()];
226-
block(result, nil);
227-
} else {
228-
block(nil, util::MakeNSError(maybe_snapshot.status()));
222+
- (DocumentSnapshot::Listener)wrapDocumentSnapshotBlock:(FIRDocumentSnapshotBlock)block {
223+
class Converter : public EventListener<DocumentSnapshot> {
224+
public:
225+
explicit Converter(FIRDocumentSnapshotBlock block) : block_(block) {
229226
}
227+
228+
void OnEvent(StatusOr<DocumentSnapshot> maybe_snapshot) override {
229+
if (maybe_snapshot.ok()) {
230+
FIRDocumentSnapshot *result =
231+
[[FIRDocumentSnapshot alloc] initWithSnapshot:std::move(maybe_snapshot).ValueOrDie()];
232+
block_(result, nil);
233+
} else {
234+
block_(nil, util::MakeNSError(maybe_snapshot.status()));
235+
}
236+
}
237+
238+
private:
239+
FIRDocumentSnapshotBlock block_;
230240
};
241+
return absl::make_unique<Converter>(block);
231242
}
232243

233244
@end

Firestore/Source/API/FIRQuery.mm

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
#import "Firestore/Source/Core/FSTQuery.h"
3939
#import "Firestore/Source/Model/FSTDocument.h"
4040
#import "Firestore/Source/Model/FSTFieldValue.h"
41-
#import "Firestore/Source/Util/FSTAsyncQueryListener.h"
4241
#import "Firestore/Source/Util/FSTUsageValidation.h"
4342

4443
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
@@ -50,8 +49,9 @@
5049
#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
5150

5251
namespace util = firebase::firestore::util;
52+
using firebase::firestore::core::AsyncEventListener;
53+
using firebase::firestore::core::EventListener;
5354
using firebase::firestore::core::ViewSnapshot;
54-
using firebase::firestore::core::ViewSnapshotHandler;
5555
using firebase::firestore::model::DocumentKey;
5656
using firebase::firestore::model::FieldPath;
5757
using firebase::firestore::model::ResourcePath;
@@ -175,34 +175,34 @@ ListenOptions listenOptions(
175175
Firestore *firestore = self.firestore.wrapped;
176176
FSTQuery *query = self.query;
177177

178-
ViewSnapshotHandler snapshotHandler = [listener, firestore,
179-
query](const StatusOr<ViewSnapshot> &maybe_snapshot) {
180-
if (!maybe_snapshot.status().ok()) {
181-
listener(nil, MakeNSError(maybe_snapshot.status()));
182-
return;
183-
}
184-
ViewSnapshot snapshot = maybe_snapshot.ValueOrDie();
185-
SnapshotMetadata metadata(snapshot.has_pending_writes(), snapshot.from_cache());
186-
187-
listener([[FIRQuerySnapshot alloc] initWithFirestore:firestore
188-
originalQuery:query
189-
snapshot:std::move(snapshot)
190-
metadata:std::move(metadata)],
191-
nil);
192-
};
178+
// Convert from ViewSnapshots to QuerySnapshots.
179+
auto view_listener = EventListener<ViewSnapshot>::Create(
180+
[listener, firestore, query](StatusOr<ViewSnapshot> maybe_snapshot) {
181+
if (!maybe_snapshot.status().ok()) {
182+
listener(nil, MakeNSError(maybe_snapshot.status()));
183+
return;
184+
}
185+
186+
ViewSnapshot snapshot = std::move(maybe_snapshot).ValueOrDie();
187+
SnapshotMetadata metadata(snapshot.has_pending_writes(), snapshot.from_cache());
188+
189+
listener([[FIRQuerySnapshot alloc] initWithFirestore:firestore
190+
originalQuery:query
191+
snapshot:std::move(snapshot)
192+
metadata:std::move(metadata)],
193+
nil);
194+
});
193195

194-
FSTAsyncQueryListener *asyncListener =
195-
[[FSTAsyncQueryListener alloc] initWithExecutor:self.firestore.client.userExecutor
196-
snapshotHandler:std::move(snapshotHandler)];
196+
// Call the view_listener on the user Executor.
197+
auto async_listener = AsyncEventListener<ViewSnapshot>::Create(firestore->client().userExecutor,
198+
std::move(view_listener));
197199

198-
std::shared_ptr<QueryListener> internalListener =
199-
[firestore->client() listenToQuery:query
200-
options:internalOptions
201-
viewSnapshotHandler:[asyncListener asyncSnapshotHandler]];
200+
std::shared_ptr<QueryListener> query_listener =
201+
[firestore->client() listenToQuery:query options:internalOptions listener:async_listener];
202202

203203
return [[FSTListenerRegistration alloc]
204-
initWithRegistration:ListenerRegistration(firestore->client(), asyncListener,
205-
std::move(internalListener))];
204+
initWithRegistration:ListenerRegistration(firestore->client(), std::move(async_listener),
205+
std::move(query_listener))];
206206
}
207207

208208
- (FIRQuery *)queryWhereField:(NSString *)field isEqualTo:(id)value {

Firestore/Source/Core/FSTEventManager.mm

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
namespace objc = firebase::firestore::util::objc;
3737
using firebase::firestore::core::DocumentViewChange;
3838
using firebase::firestore::core::ViewSnapshot;
39-
using firebase::firestore::core::ViewSnapshotHandler;
4039
using firebase::firestore::model::OnlineState;
4140
using firebase::firestore::model::TargetId;
4241
using firebase::firestore::util::MakeStatus;

Firestore/Source/Core/FSTFirestoreClient.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@
4747

4848
NS_ASSUME_NONNULL_BEGIN
4949

50+
using firebase::firestore::api::DocumentSnapshot;
5051
using firebase::firestore::core::ListenOptions;
5152
using firebase::firestore::core::QueryListener;
53+
using firebase::firestore::core::ViewSnapshot;
5254

5355
/**
5456
* FirestoreClient is a top-level class that constructs and owns all of the pieces of the client
@@ -84,8 +86,7 @@ using firebase::firestore::core::QueryListener;
8486
/** Starts listening to a query. */
8587
- (std::shared_ptr<QueryListener>)listenToQuery:(FSTQuery *)query
8688
options:(ListenOptions)options
87-
viewSnapshotHandler:(firebase::firestore::core::ViewSnapshotHandler &&)
88-
viewSnapshotHandler;
89+
listener:(ViewSnapshot::SharedListener &&)listener;
8990

9091
/** Stops listening to a query previously listened to. */
9192
- (void)removeListener:(const std::shared_ptr<QueryListener> &)listener;
@@ -95,8 +96,7 @@ using firebase::firestore::core::QueryListener;
9596
* doesn't exist, an error will be sent to the completion.
9697
*/
9798
- (void)getDocumentFromLocalCache:(const firebase::firestore::api::DocumentReference &)doc
98-
completion:(firebase::firestore::util::StatusOrCallback<
99-
firebase::firestore::api::DocumentSnapshot> &&)completion;
99+
completion:(DocumentSnapshot::Listener &&)completion;
100100

101101
/**
102102
* Retrieves a (possibly empty) set of documents from the cache via the

Firestore/Source/Core/FSTFirestoreClient.mm

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@
6464
using firebase::firestore::auth::User;
6565
using firebase::firestore::core::DatabaseInfo;
6666
using firebase::firestore::core::ViewSnapshot;
67-
using firebase::firestore::core::ViewSnapshotHandler;
6867
using firebase::firestore::local::LruParams;
6968
using firebase::firestore::model::DatabaseId;
7069
using firebase::firestore::model::DocumentKeySet;
@@ -314,22 +313,22 @@ - (void)shutdownWithCompletion:(nullable FSTVoidErrorBlock)completion {
314313

315314
- (std::shared_ptr<QueryListener>)listenToQuery:(FSTQuery *)query
316315
options:(ListenOptions)options
317-
viewSnapshotHandler:(ViewSnapshotHandler &&)viewSnapshotHandler {
318-
auto listener =
319-
std::make_shared<QueryListener>(query, std::move(options), std::move(viewSnapshotHandler));
316+
listener:(ViewSnapshot::SharedListener &&)listener {
317+
auto query_listener = QueryListener::Create(query, std::move(options), std::move(listener));
320318

321-
_workerQueue->Enqueue([self, listener] { [self.eventManager addListener:listener]; });
319+
_workerQueue->Enqueue([self, query_listener] { [self.eventManager addListener:query_listener]; });
322320

323-
return listener;
321+
return query_listener;
324322
}
325323

326324
- (void)removeListener:(const std::shared_ptr<QueryListener> &)listener {
327325
_workerQueue->Enqueue([self, listener] { [self.eventManager removeListener:listener]; });
328326
}
329327

330328
- (void)getDocumentFromLocalCache:(const DocumentReference &)doc
331-
completion:(StatusOrCallback<DocumentSnapshot> &&)completion {
332-
_workerQueue->Enqueue([self, doc, completion] {
329+
completion:(DocumentSnapshot::Listener &&)completion {
330+
auto shared_completion = absl::ShareUniquePtr(std::move(completion));
331+
_workerQueue->Enqueue([self, doc, shared_completion] {
333332
FSTMaybeDocument *maybeDoc = [self.localStore readDocument:doc.key()];
334333
StatusOr<DocumentSnapshot> maybe_snapshot;
335334

@@ -349,8 +348,8 @@ - (void)getDocumentFromLocalCache:(const DocumentReference &)doc
349348
"FIRFirestoreSourceCache to attempt to retrieve the document "};
350349
}
351350

352-
if (completion) {
353-
self->_userExecutor->Execute([=] { completion(std::move(maybe_snapshot)); });
351+
if (shared_completion) {
352+
self->_userExecutor->Execute([=] { shared_completion->OnEvent(std::move(maybe_snapshot)); });
354353
}
355354
});
356355
}

0 commit comments

Comments
 (0)