Skip to content

Commit 82a63c8

Browse files
fix(firestore, web): stop cleaning up snapshot listeners in debug (#13119)
1 parent 18642a2 commit 82a63c8

File tree

3 files changed

+199
-3
lines changed

3 files changed

+199
-3
lines changed

packages/cloud_firestore/cloud_firestore/example/integration_test/e2e_test.dart

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import 'snapshot_metadata_e2e.dart';
2222
import 'timestamp_e2e.dart';
2323
import 'transaction_e2e.dart';
2424
import 'write_batch_e2e.dart';
25+
import 'web_snapshot_listeners.dart';
2526

2627
bool kUseFirestoreEmulator = true;
2728

@@ -52,6 +53,7 @@ void main() {
5253
runTransactionTests();
5354
runWriteBatchTests();
5455
runLoadBundleTests();
56+
runWebSnapshotListenersTests();
5557
if (defaultTargetPlatform != TargetPlatform.windows) {
5658
runSecondDatabaseTests();
5759
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
// Copyright 2020, the Chromium project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:async';
6+
7+
import 'package:cloud_firestore/cloud_firestore.dart';
8+
import 'package:flutter/foundation.dart';
9+
import 'package:flutter_test/flutter_test.dart';
10+
11+
// Run only on web for demonstrating snapshot listener clean up in debug mode does not clean up the listeners incorrectly.
12+
// See: https://github.com/firebase/flutterfire/issues/13019
13+
void runWebSnapshotListenersTests() {
14+
group('Web snapshot listeners', () {
15+
late FirebaseFirestore firestore;
16+
late CollectionReference<Map<String, dynamic>> collection;
17+
late DocumentReference<Map<String, dynamic>> document;
18+
late DocumentReference<Map<String, dynamic>> document2;
19+
setUpAll(() async {
20+
firestore = FirebaseFirestore.instance;
21+
collection = firestore
22+
.collection('flutter-tests/web-snapshot-listeners/query-tests');
23+
document = collection.doc('doc1');
24+
document2 = collection.doc('doc1');
25+
26+
await Future.wait([
27+
document.set({'foo': 1}),
28+
collection.add({'foo': 2}),
29+
collection.add({'foo': 3}),
30+
]);
31+
});
32+
33+
testWidgets(
34+
'document snapshot listeners in debug',
35+
(_) async {
36+
Completer<bool> completer = Completer<bool>();
37+
Completer<bool> completer2 = Completer<bool>();
38+
Completer<bool> completer3 = Completer<bool>();
39+
document.snapshots().listen((snapshot) {
40+
if (completer.isCompleted) {
41+
return;
42+
}
43+
completer.complete(true);
44+
});
45+
46+
document.snapshots().listen((snapshot) {
47+
if (completer2.isCompleted) {
48+
return;
49+
}
50+
completer2.complete(true);
51+
});
52+
53+
document.snapshots().listen((snapshot) {
54+
if (completer3.isCompleted) {
55+
return;
56+
}
57+
completer3.complete(true);
58+
});
59+
60+
final one = await completer.future;
61+
final two = await completer2.future;
62+
final three = await completer3.future;
63+
64+
expect(one, true);
65+
expect(two, true);
66+
expect(three, true);
67+
},
68+
skip: !kIsWeb,
69+
);
70+
71+
testWidgets(
72+
'document snapshot listeners with different doc refs in debug',
73+
(_) async {
74+
Completer<bool> completer = Completer<bool>();
75+
Completer<bool> completer2 = Completer<bool>();
76+
Completer<bool> completer3 = Completer<bool>();
77+
Completer<bool> completer4 = Completer<bool>();
78+
document.snapshots().listen((snapshot) {
79+
if (completer.isCompleted) {
80+
return;
81+
}
82+
completer.complete(true);
83+
});
84+
85+
document.snapshots().listen((snapshot) {
86+
if (completer2.isCompleted) {
87+
return;
88+
}
89+
completer2.complete(true);
90+
});
91+
92+
document2.snapshots().listen((snapshot) {
93+
if (completer3.isCompleted) {
94+
return;
95+
}
96+
completer3.complete(true);
97+
});
98+
99+
document2.snapshots().listen((snapshot) {
100+
if (completer4.isCompleted) {
101+
return;
102+
}
103+
completer4.complete(true);
104+
});
105+
106+
final one = await completer.future;
107+
final two = await completer2.future;
108+
final three = await completer3.future;
109+
final four = await completer4.future;
110+
111+
expect(one, true);
112+
expect(two, true);
113+
expect(three, true);
114+
expect(four, true);
115+
},
116+
skip: !kIsWeb,
117+
);
118+
119+
testWidgets(
120+
'query snapshot listeners in debug',
121+
(_) async {
122+
Completer<bool> completer = Completer<bool>();
123+
Completer<bool> completer2 = Completer<bool>();
124+
Completer<bool> completer3 = Completer<bool>();
125+
collection.snapshots().listen((snapshot) {
126+
if (completer.isCompleted) {
127+
return;
128+
}
129+
completer.complete(true);
130+
});
131+
132+
collection.snapshots().listen((snapshot) {
133+
if (completer2.isCompleted) {
134+
return;
135+
}
136+
completer2.complete(true);
137+
});
138+
139+
collection.snapshots().listen((snapshot) {
140+
if (completer3.isCompleted) {
141+
return;
142+
}
143+
completer3.complete(true);
144+
});
145+
final one = await completer.future;
146+
final two = await completer2.future;
147+
final three = await completer3.future;
148+
149+
expect(one, true);
150+
expect(two, true);
151+
expect(three, true);
152+
},
153+
skip: !kIsWeb,
154+
);
155+
156+
testWidgets(
157+
'snapshot in sync listeners in debug',
158+
(_) async {
159+
Completer<bool> completer = Completer<bool>();
160+
Completer<bool> completer2 = Completer<bool>();
161+
Completer<bool> completer3 = Completer<bool>();
162+
firestore.snapshotsInSync().listen((snapshot) {
163+
if (completer.isCompleted) {
164+
return;
165+
}
166+
completer.complete(true);
167+
});
168+
169+
firestore.snapshotsInSync().listen((snapshot) {
170+
if (completer2.isCompleted) {
171+
return;
172+
}
173+
completer2.complete(true);
174+
});
175+
176+
firestore.snapshotsInSync().listen((snapshot) {
177+
if (completer3.isCompleted) {
178+
return;
179+
}
180+
completer3.complete(true);
181+
});
182+
183+
final one = await completer.future;
184+
final two = await completer2.future;
185+
final three = await completer3.future;
186+
187+
expect(one, true);
188+
expect(two, true);
189+
expect(three, true);
190+
},
191+
skip: !kIsWeb,
192+
);
193+
});
194+
}

packages/cloud_firestore/cloud_firestore_web/lib/src/interop/firestore.dart

+3-3
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ class Firestore extends JsObjectWrapper<firestore_interop.FirestoreJsImpl> {
101101
}
102102

103103
// purely for debug mode and tracking listeners to clean up on "hot restart"
104-
final Map<String, int> _snapshotInSyncListeners = {};
104+
static final Map<String, int> _snapshotInSyncListeners = {};
105105
String _snapshotInSyncWindowsKey() {
106106
if (kDebugMode) {
107107
final key = 'flutterfire-${app.name}_snapshotInSync';
@@ -411,7 +411,7 @@ class DocumentReference
411411
}
412412

413413
// purely for debug mode and tracking listeners to clean up on "hot restart"
414-
final Map<String, int> _docListeners = {};
414+
static final Map<String, int> _docListeners = {};
415415
String _documentSnapshotWindowsKey() {
416416
if (kDebugMode) {
417417
final key = 'flutterfire-${firestore.app.name}_${path}_documentSnapshot';
@@ -537,7 +537,7 @@ class Query<T extends firestore_interop.QueryJsImpl>
537537
jsObject, firestore_interop.limitToLast(limit.toJS)));
538538

539539
// purely for debug mode and tracking listeners to clean up on "hot restart"
540-
final Map<String, int> _snapshotListeners = {};
540+
static final Map<String, int> _snapshotListeners = {};
541541
String _querySnapshotWindowsKey(hashCode) {
542542
if (kDebugMode) {
543543
final key = 'flutterfire-${firestore.app.name}_${hashCode}_querySnapshot';

0 commit comments

Comments
 (0)