diff --git a/.github/workflows/all_plugins.yaml b/.github/workflows/all_plugins.yaml index ca840d494f10..aabd2f955046 100644 --- a/.github/workflows/all_plugins.yaml +++ b/.github/workflows/all_plugins.yaml @@ -91,7 +91,17 @@ jobs: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 with: fetch-depth: 0 - - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 + - id: setup-flutter-format + uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 + continue-on-error: true + with: + channel: 'stable' + cache: true + cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" + pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" + - name: Retry Flutter setup + if: steps.setup-flutter-format.outcome == 'failure' + uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' cache: true diff --git a/.github/workflows/e2e_tests_fdc.yaml b/.github/workflows/e2e_tests_fdc.yaml index 927b412e68d0..7546d63ad647 100644 --- a/.github/workflows/e2e_tests_fdc.yaml +++ b/.github/workflows/e2e_tests_fdc.yaml @@ -324,16 +324,13 @@ jobs: # Web devices are not supported for the `flutter test` command yet. As a # workaround we can use the `flutter drive` command. Tracking issue: # https://github.com/flutter/flutter/issues/66264 + # Chrome debug service can fail with AppConnectionException before tests + # connect. Retry only those infrastructure startup failures. run: | - chromedriver --port=4444 --trace-buffer-size=100000 & - flutter drive --target=./integration_test/e2e_test.dart --driver=./test_driver/integration_test.dart -d chrome --dart-define=CI=true | tee output.log - # We have to check the output for failed tests matching the string "[E]" - output=$(/dev/null || true + wait "$chromedriver_pid" 2>/dev/null || true + cleanup_web_processes + + output=$(/dev/null || true + wait "$chromedriver_pid" 2>/dev/null || true + cleanup_web_processes + + output=$(/dev/null || true + wait "$chromedriver_pid" 2>/dev/null || true + cleanup_web_processes + output=$(/dev/null || true + wait "$chromedriver_pid" 2>/dev/null || true + cleanup_web_processes + + output=$(>().having((e) => e.data(), 'data', 42), ), @@ -647,7 +647,7 @@ void runDocumentReferenceTests() { ); await expectLater( - fooConverter.get(), + fooConverter.get(const GetOptions(source: Source.server)), completion( isA>().having((e) => e.data(), 'data', 21), ), diff --git a/packages/cloud_firestore/cloud_firestore/example/integration_test/query_e2e.dart b/packages/cloud_firestore/cloud_firestore/example/integration_test/query_e2e.dart index d97ddc4e2fda..05d13abe3651 100644 --- a/packages/cloud_firestore/cloud_firestore/example/integration_test/query_e2e.dart +++ b/packages/cloud_firestore/cloud_firestore/example/integration_test/query_e2e.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'dart:math'; +import 'package:collection/collection.dart' show IterableExtension; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart'; @@ -330,47 +331,80 @@ void runQueryTests() { Stream>> stream = collection.snapshots(); - final snapshots = >>[]; - final receivedAll = Completer(); + final initialSnapshot = + Completer>>(); + final doc1Set = Completer>>(); + final doc1Deleted = Completer>>(); + final doc2Set = Completer>>(); + final doc2Updated = Completer>>(); StreamSubscription subscription = stream.listen((snapshot) { - snapshots.add(snapshot); - if (snapshots.length >= 5 && !receivedAll.isCompleted) { - receivedAll.complete(); + final doc1 = + snapshot.docs.where((doc) => doc.id == 'doc1').firstOrNull; + final doc2 = + snapshot.docs.where((doc) => doc.id == 'doc2').firstOrNull; + + if (!initialSnapshot.isCompleted && + snapshot.docs.length == 1 && + snapshot.docs.single.data()['foo'] == 'bar') { + initialSnapshot.complete(snapshot); + } else if (!doc1Set.isCompleted && + snapshot.docs.length == 2 && + doc1?.data()['bar'] == 'baz') { + doc1Set.complete(snapshot); + } else if (doc1Set.isCompleted && + !doc1Deleted.isCompleted && + doc1 == null) { + doc1Deleted.complete(snapshot); + } else if (doc1Deleted.isCompleted && + !doc2Set.isCompleted && + doc2?.data()['foo'] == 'bar') { + doc2Set.complete(snapshot); + } else if (doc2Set.isCompleted && + !doc2Updated.isCompleted && + doc2?.data()['foo'] == 'baz') { + doc2Updated.complete(snapshot); } }); - // Wait for initial snapshot before making changes - await Future.delayed(const Duration(milliseconds: 500)); - await collection.doc('doc1').set({'bar': 'baz'}); - await collection.doc('doc1').delete(); - await collection.doc('doc2').set({'foo': 'bar'}); - await collection.doc('doc2').update({'foo': 'baz'}); - - // Wait for all 5 snapshots with a timeout instead of hanging forever - await receivedAll.future.timeout(const Duration(seconds: 30)); + final initial = await initialSnapshot.future.timeout( + const Duration(seconds: 30), + ); + expect(initial.docs, hasLength(1)); - expect(snapshots[0].docs.length, equals(1)); - expect(snapshots[0].docs[0].data()['foo'], equals('bar')); + await collection.doc('doc1').set({'bar': 'baz'}); + final withDoc1 = await doc1Set.future.timeout( + const Duration(seconds: 30), + ); + expect(withDoc1.docs, hasLength(2)); + expect( + withDoc1.docs.firstWhere((doc) => doc.id == 'doc1').data()['bar'], + equals('baz'), + ); - expect(snapshots[1].docs.length, equals(2)); - final doc1 = snapshots[1].docs.firstWhere((doc) => doc.id == 'doc1'); - expect(doc1.data()['bar'], equals('baz')); + await collection.doc('doc1').delete(); + final withoutDoc1 = await doc1Deleted.future.timeout( + const Duration(seconds: 30), + ); + expect(withoutDoc1.docs.where((doc) => doc.id == 'doc1'), isEmpty); - expect(snapshots[2].docs.length, equals(1)); + await collection.doc('doc2').set({'foo': 'bar'}); + final withDoc2 = await doc2Set.future.timeout( + const Duration(seconds: 30), + ); expect( - snapshots[2].docs.where((doc) => doc.id == 'doc1').isEmpty, - isTrue, + withDoc2.docs.firstWhere((doc) => doc.id == 'doc2').data()['foo'], + equals('bar'), ); - expect(snapshots[3].docs.length, equals(2)); - final doc2set = snapshots[3].docs.firstWhere((doc) => doc.id == 'doc2'); - expect(doc2set.data()['foo'], equals('bar')); - - expect(snapshots[4].docs.length, equals(2)); - final doc2update = - snapshots[4].docs.firstWhere((doc) => doc.id == 'doc2'); - expect(doc2update.data()['foo'], equals('baz')); + await collection.doc('doc2').update({'foo': 'baz'}); + final updatedDoc2 = await doc2Updated.future.timeout( + const Duration(seconds: 30), + ); + expect( + updatedDoc2.docs.firstWhere((doc) => doc.id == 'doc2').data()['foo'], + equals('baz'), + ); await subscription.cancel(); }); diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/connector/mutations.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/connector/mutations.gql index 6263b482f9b9..53e9c8edf247 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/connector/mutations.gql +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/connector/mutations.gql @@ -62,6 +62,12 @@ mutation deleteMovie($id: UUID!) @auth(level: PUBLIC) { movie_delete(id: $id) } +mutation deleteAllMovieData @auth(level: PUBLIC) { + directedBy_deleteMany(all: true) + movie_deleteMany(all: true) + person_deleteMany(all: true) +} + mutation thing($title: Any! = "ABC") @auth(level: PUBLIC) { abc: thing_insert(data: { id: "a231d1ff-1825-447d-8b12-de092fb3a0f1" diff --git a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/listen_e2e.dart b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/listen_e2e.dart index 7e84491890ae..1bdd62287498 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/listen_e2e.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/listen_e2e.dart @@ -10,6 +10,8 @@ import 'package:flutter_test/flutter_test.dart'; import 'query_e2e.dart'; +const _listenTimeout = Duration(seconds: 30); + void runListenTests() { group( '$FirebaseDataConnect.instance listen', @@ -25,9 +27,8 @@ void runListenTests() { expect(initialValue.data.movies.length, 0, reason: 'Initial movie list should be empty'); - final Completer isReady = Completer(); - final Completer hasBeenListened = Completer(); - int count = 0; + final initialMovies = Completer>(); + final updatedMovies = Completer>(); final listener = MoviesConnector.instance .listMovies() @@ -36,47 +37,48 @@ void runListenTests() { .listen((value) { final movies = value.data.movies; - if (count == 0) { - expect(movies.length, 0, - reason: 'First emission should contain an empty list'); - isReady.complete(); - } else { - expect(movies.length, 1, - reason: 'Second emission should contain one movie'); - expect(movies[0].title, 'The Matrix', - reason: 'The movie should be The Matrix'); - hasBeenListened.complete(true); + if (!initialMovies.isCompleted && movies.isEmpty) { + initialMovies.complete(movies); + } else if (!updatedMovies.isCompleted && + movies.length == 1 && + movies.single.title == 'The Matrix') { + updatedMovies.complete(movies); } - count++; }); - // Wait for the listener to be ready - await isReady.future; - - // Create the movie - await MoviesConnector.instance - .createMovie( - genre: 'Action', - title: 'The Matrix', - releaseYear: 1999, - ) - .rating(4.5) - .ref() - .execute(); + try { + // Wait for the listener to be ready + final initial = await initialMovies.future.timeout(_listenTimeout); + expect(initial, isEmpty, + reason: 'First emission should contain an empty list'); - await MoviesConnector.instance - .listMovies() - .ref() - .execute(fetchPolicy: QueryFetchPolicy.serverOnly); + // Create the movie + await MoviesConnector.instance + .createMovie( + genre: 'Action', + title: 'The Matrix', + releaseYear: 1999, + ) + .rating(4.5) + .ref() + .execute(); - // Wait for the listener to receive the movie update - final bool hasListenerReceived = await hasBeenListened.future; + await MoviesConnector.instance + .listMovies() + .ref() + .execute(fetchPolicy: QueryFetchPolicy.serverOnly); - // Cancel the listener and wait for it to finish - await listener.cancel(); + // Wait for the listener to receive the movie update + final movies = await updatedMovies.future.timeout(_listenTimeout); - expect(hasListenerReceived, isTrue, - reason: 'The stream should have emitted new data'); + expect(movies, hasLength(1), + reason: 'Second emission should contain one movie'); + expect(movies.single.title, 'The Matrix', + reason: 'The movie should be The Matrix'); + } finally { + // Cancel the listener and wait for it to finish + await listener.cancel(); + } }); }, ); diff --git a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart index 4ef24e41b467..3843e29e7c80 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/query_e2e.dart @@ -7,11 +7,23 @@ import 'package:firebase_data_connect_example/generated/movies.dart'; import 'package:flutter_test/flutter_test.dart'; Future deleteAllMovies() async { - final value = await MoviesConnector.instance.listMovies().ref().execute(); - final result = value.data; - for (var movie in result.movies) { - await MoviesConnector.instance.deleteMovie(id: movie.id).ref().execute(); + for (var attempt = 0; attempt < 3; attempt++) { + await MoviesConnector.instance.deleteAllMovieData().ref().execute(); + + final movies = await listMoviesFromServer(); + if (movies.isEmpty) return; } + + final movies = await listMoviesFromServer(); + expect(movies, isEmpty); +} + +Future> listMoviesFromServer() async { + final value = await MoviesConnector.instance + .listMovies() + .ref() + .execute(fetchPolicy: QueryFetchPolicy.serverOnly); + return value.data.movies; } void runQueryTests() { @@ -23,11 +35,8 @@ void runQueryTests() { }); testWidgets('can query', (WidgetTester tester) async { - final value = - await MoviesConnector.instance.listMovies().ref().execute(); - - final result = value.data; - expect(result.movies.length, 0); + final movies = await listMoviesFromServer(); + expect(movies, isEmpty); }); testWidgets('can add a movie', (WidgetTester tester) async { @@ -61,10 +70,8 @@ void runQueryTests() { .people[0] .id; - final value = - await MoviesConnector.instance.listMovies().ref().execute(); - final result = value.data; - expect(result.movies.length, 0); + final movies = await listMoviesFromServer(); + expect(movies, isEmpty); ref = MoviesConnector.instance .createMovie( @@ -123,10 +130,8 @@ void runQueryTests() { await ref.execute(); - final value2 = - await MoviesConnector.instance.listMovies().ref().execute(); - final result2 = value2.data; - expect(result2.movies.length, 0); + final movies = await listMoviesFromServer(); + expect(movies, isEmpty); }); }, ); diff --git a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/websocket_e2e.dart b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/websocket_e2e.dart index a105b4783184..53bf21f06842 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/websocket_e2e.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/websocket_e2e.dart @@ -144,9 +144,8 @@ void runWebSocketTests() { await _waitForStreamEvent(isReady.future, 'listMovies subscription'); // Now perform a query, which should go over WebSocket if connected - final result = - await MoviesConnector.instance.listMovies().ref().execute(); - expect(result.data.movies.length, 0); + final movies = await listMoviesFromServer(); + expect(movies, isEmpty); // Perform a mutation await MoviesConnector.instance @@ -188,31 +187,35 @@ void runWebSocketTests() { count++; }); - await _waitForStreamEvent(isReady.future, 'listMovies subscription'); + try { + await _waitForStreamEvent(isReady.future, 'listMovies subscription'); - // Cancel the subscription - await sub.cancel(); + // Cancel the subscription + await sub.cancel(); - // Create a movie - await MoviesConnector.instance - .createMovie( - genre: 'Action', - title: 'Avatar', - releaseYear: 2009, - ) - .rating(4.7) - .ref() - .execute(); + // Create a movie + await MoviesConnector.instance + .createMovie( + genre: 'Action', + title: 'Avatar', + releaseYear: 2009, + ) + .rating(4.7) + .ref() + .execute(); - // Wait a bit to ensure no event is received - bool received = true; - try { - await receivedUpdate.future.timeout(const Duration(seconds: 2)); - } on TimeoutException { - received = false; + // Wait a bit to ensure no event is received + bool received = true; + try { + await receivedUpdate.future.timeout(const Duration(seconds: 2)); + } on TimeoutException { + received = false; + } + expect(received, isFalse, + reason: 'Should not receive events after cancel'); + } finally { + await sub.cancel(); } - expect(received, isFalse, - reason: 'Should not receive events after cancel'); }); testWidgets( @@ -232,18 +235,22 @@ void runWebSocketTests() { count++; }); - await _waitForStreamEvent(isReady.future, 'listMovies subscription'); + try { + await _waitForStreamEvent(isReady.future, 'listMovies subscription'); - final dataConnect = MoviesConnector.instance.dataConnect; - final transport = (dataConnect as dynamic).transport; - final ws = (transport as dynamic).websocket; + final dataConnect = MoviesConnector.instance.dataConnect; + final transport = (dataConnect as dynamic).transport; + final ws = (transport as dynamic).websocket; - expect(ws.isConnected, isTrue); + expect(ws.isConnected, isTrue); - // Cancel the subscription - await sub.cancel(); + // Cancel the subscription + await sub.cancel(); - expect(ws.isConnected, isFalse); + expect(ws.isConnected, isFalse); + } finally { + await sub.cancel(); + } }); }, ); diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/delete_all_movie_data.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/delete_all_movie_data.dart new file mode 100644 index 000000000000..c71d5c27a826 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/delete_all_movie_data.dart @@ -0,0 +1,71 @@ +part of 'movies.dart'; + +class DeleteAllMovieDataVariablesBuilder { + final FirebaseDataConnect _dataConnect; + DeleteAllMovieDataVariablesBuilder( + this._dataConnect, + ); + Deserializer dataDeserializer = + (dynamic json) => DeleteAllMovieDataData.fromJson(jsonDecode(json)); + + Future> execute() { + return ref().execute(); + } + + MutationRef ref() { + return _dataConnect.mutation( + "deleteAllMovieData", dataDeserializer, emptySerializer, null); + } +} + +@immutable +class DeleteAllMovieDataData { + final int directedByDeleteMany; + final int movieDeleteMany; + final int personDeleteMany; + DeleteAllMovieDataData.fromJson(dynamic json) + : directedByDeleteMany = nativeFromJson( + json['directedBy_deleteMany'], + ), + movieDeleteMany = nativeFromJson( + json['movie_deleteMany'], + ), + personDeleteMany = nativeFromJson( + json['person_deleteMany'], + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final DeleteAllMovieDataData otherTyped = other as DeleteAllMovieDataData; + return directedByDeleteMany == otherTyped.directedByDeleteMany && + movieDeleteMany == otherTyped.movieDeleteMany && + personDeleteMany == otherTyped.personDeleteMany; + } + + @override + int get hashCode => Object.hashAll([ + directedByDeleteMany.hashCode, + movieDeleteMany.hashCode, + personDeleteMany.hashCode, + ]); + + Map toJson() { + Map json = {}; + json['directedBy_deleteMany'] = nativeToJson(directedByDeleteMany); + json['movie_deleteMany'] = nativeToJson(movieDeleteMany); + json['person_deleteMany'] = nativeToJson(personDeleteMany); + return json; + } + + const DeleteAllMovieDataData({ + required this.directedByDeleteMany, + required this.movieDeleteMany, + required this.personDeleteMany, + }); +} diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart index 7ad7e907c59a..a6ca68e5e0f3 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart @@ -18,6 +18,8 @@ part 'create_movie.dart'; part 'delete_movie.dart'; +part 'delete_all_movie_data.dart'; + part 'thing.dart'; part 'seed_data.dart'; @@ -95,6 +97,12 @@ class MoviesConnector { ); } + DeleteAllMovieDataVariablesBuilder deleteAllMovieData() { + return DeleteAllMovieDataVariablesBuilder( + dataConnect, + ); + } + ThingVariablesBuilder thing() { return ThingVariablesBuilder( dataConnect, diff --git a/tests/integration_test/firebase_auth/firebase_auth_e2e_test.dart b/tests/integration_test/firebase_auth/firebase_auth_e2e_test.dart index 7693e8ce69d4..cca5cee5990c 100644 --- a/tests/integration_test/firebase_auth/firebase_auth_e2e_test.dart +++ b/tests/integration_test/firebase_auth/firebase_auth_e2e_test.dart @@ -32,9 +32,10 @@ void main() { }); setUp(() async { + await ensureSignedOut(); + // Reset users on emulator. await emulatorClearAllUsers(); - await ensureSignedOut(); try { await FirebaseAuth.instance.createUserWithEmailAndPassword( diff --git a/tests/integration_test/firebase_auth/test_utils.dart b/tests/integration_test/firebase_auth/test_utils.dart index 2a2b1db23aa4..bf7d0773e15f 100644 --- a/tests/integration_test/firebase_auth/test_utils.dart +++ b/tests/integration_test/firebase_auth/test_utils.dart @@ -217,30 +217,56 @@ String emulatorCreateCustomToken( } Future ensureSignedIn(String testEmail) async { - if (FirebaseAuth.instance.currentUser == null) { + final currentUser = FirebaseAuth.instance.currentUser; + if (currentUser?.email == testEmail) { try { + await currentUser!.getIdToken(); + return; + } on FirebaseAuthException catch (e) { + if (e.code != 'user-token-expired' && e.code != 'user-not-found') { + rethrow; + } + } + } + + try { + await FirebaseAuth.instance.signInWithEmailAndPassword( + email: testEmail, + password: testPassword, + ); + } on FirebaseAuthException catch (e) { + // The Windows error codes are not consistent + if (e.code == 'user-not-found') { await FirebaseAuth.instance.createUserWithEmailAndPassword( email: testEmail, password: testPassword, ); - } on FirebaseAuthException catch (e) { - // The Windows error codes are not consistent - if (e.code == 'email-already-in-use') { - await FirebaseAuth.instance.signInWithEmailAndPassword( - email: testEmail, - password: testPassword, - ); - } - } catch (e) { - // ignore: avoid_print - print('ensureSignedIn Error $e'); + } else if (e.code != 'email-already-in-use') { + rethrow; } + } catch (e) { + // ignore: avoid_print + print('ensureSignedIn Error $e'); } } Future ensureSignedOut() async { if (FirebaseAuth.instance.currentUser != null) { - await FirebaseAuth.instance.signOut(); + try { + await FirebaseAuth.instance.signOut(); + } on FirebaseAuthException catch (e) { + if (e.code != 'user-token-expired') { + rethrow; + } + + // Web can keep a deleted emulator user in memory. Replacing it with a + // temporary user gives signOut a valid session to clear local state. + await FirebaseAuth.instance.createUserWithEmailAndPassword( + email: generateRandomEmail(prefix: 'signout-'), + password: testPassword, + ); + await FirebaseAuth.instance.signOut(); + } } } diff --git a/tests/integration_test/firebase_database/data_snapshot_e2e.dart b/tests/integration_test/firebase_database/data_snapshot_e2e.dart index 29ff36f4cd7d..8828ba723198 100644 --- a/tests/integration_test/firebase_database/data_snapshot_e2e.dart +++ b/tests/integration_test/firebase_database/data_snapshot_e2e.dart @@ -9,11 +9,15 @@ void setupDataSnapshotTests() { group('DataSnapshot', () { late DatabaseReference ref; DatabaseReference getRef(String path) { - return FirebaseDatabase.instance.ref('tests').child(path); + return ref.child(path); } + var testCount = 0; + setUp(() async { - ref = FirebaseDatabase.instance.ref('tests'); + ref = FirebaseDatabase.instance.ref( + 'tests/data-snapshot-${DateTime.now().microsecondsSinceEpoch}-${testCount++}', + ); // Wipe the database before each test await ref.remove(); diff --git a/tests/integration_test/firebase_database/database_reference_e2e.dart b/tests/integration_test/firebase_database/database_reference_e2e.dart index 0c59b4af6ccb..4de90287cb8b 100644 --- a/tests/integration_test/firebase_database/database_reference_e2e.dart +++ b/tests/integration_test/firebase_database/database_reference_e2e.dart @@ -11,6 +11,12 @@ import 'package:flutter_test/flutter_test.dart'; import 'firebase_database_e2e_test.dart'; +DatabaseReference _uniqueRef(String name) { + return database.ref( + 'tests/$name-${DateTime.now().microsecondsSinceEpoch}', + ); +} + void setupDatabaseReferenceTests() { group('DatabaseReference', () { group('set()', () { @@ -127,7 +133,7 @@ void setupDatabaseReferenceTests() { }); test('does not emit local transaction events when disabled', () async { - final ref = database.ref('tests/transaction-apply-locally-false'); + final ref = _uniqueRef('transaction-apply-locally-false'); await ref.set({'count': 0}); final initialEvent = Completer(); @@ -208,8 +214,7 @@ void setupDatabaseReferenceTests() { }); test('Server.increment', () async { - final FirebaseDatabase database = FirebaseDatabase.instance; - final DatabaseReference ref = database.ref('tests/server-increment'); + final DatabaseReference ref = _uniqueRef('server-increment'); await ref.set(ServerValue.increment(1.5)); final snap = await ref.get(); diff --git a/tests/integration_test/firebase_database/query_e2e.dart b/tests/integration_test/firebase_database/query_e2e.dart index 8b2ec17a3da5..d849ee96a973 100644 --- a/tests/integration_test/firebase_database/query_e2e.dart +++ b/tests/integration_test/firebase_database/query_e2e.dart @@ -12,9 +12,12 @@ import 'package:flutter_test/flutter_test.dart'; void setupQueryTests() { group('Query', () { late DatabaseReference ref; + var testCount = 0; setUp(() async { - ref = FirebaseDatabase.instance.ref('tests'); + ref = FirebaseDatabase.instance.ref( + 'tests/query-${DateTime.now().microsecondsSinceEpoch}-${testCount++}', + ); // Wipe the database before each test await ref.remove();