Use fake_async over waiting for timers with delays (dart-lang/stream_transform#166)

On the web the timers may have some variance.
Replace all usages of `waitForTimer` utility to use `fakeAsync`.
Separate out the call to `Stream.listen` from `setUp` so it
happens in the fake async zone.
diff --git a/pkgs/stream_transform/pubspec.yaml b/pkgs/stream_transform/pubspec.yaml
index 915dc10..4d1ffbe 100644
--- a/pkgs/stream_transform/pubspec.yaml
+++ b/pkgs/stream_transform/pubspec.yaml
@@ -8,5 +8,6 @@
 
 dev_dependencies:
   async: ^2.5.0
+  fake_async: ^1.3.0
   lints: ^2.0.0
   test: ^1.16.0
diff --git a/pkgs/stream_transform/test/audit_test.dart b/pkgs/stream_transform/test/audit_test.dart
index 907d873..e2f8133 100644
--- a/pkgs/stream_transform/test/audit_test.dart
+++ b/pkgs/stream_transform/test/audit_test.dart
@@ -4,6 +4,7 @@
 
 import 'dart:async';
 
+import 'package:fake_async/fake_async.dart';
 import 'package:stream_transform/stream_transform.dart';
 import 'package:test/test.dart';
 
@@ -21,7 +22,7 @@
       late StreamSubscription<int> subscription;
 
       group('audit', () {
-        setUp(() async {
+        setUp(() {
           valuesCanceled = false;
           values = createController(streamType)
             ..onCancel = () {
@@ -31,73 +32,106 @@
           errors = [];
           isDone = false;
           transformed = values.stream.audit(const Duration(milliseconds: 6));
+        });
+
+        void listen() {
           subscription = transformed
               .listen(emittedValues.add, onError: errors.add, onDone: () {
             isDone = true;
           });
-        });
+        }
 
         test('cancels values', () async {
+          listen();
           await subscription.cancel();
           expect(valuesCanceled, true);
         });
 
-        test('swallows values that come faster than duration', () async {
-          values
-            ..add(1)
-            ..add(2);
-          await values.close();
-          await waitForTimer(5);
-          expect(emittedValues, [2]);
+        test('swallows values that come faster than duration', () {
+          fakeAsync((async) {
+            listen();
+            values
+              ..add(1)
+              ..add(2)
+              ..close();
+            async.elapse(const Duration(milliseconds: 6));
+            expect(emittedValues, [2]);
+          });
         });
 
-        test('outputs multiple values spaced further than duration', () async {
-          values.add(1);
-          await waitForTimer(5);
-          values.add(2);
-          await waitForTimer(5);
-          expect(emittedValues, [1, 2]);
+        test('outputs multiple values spaced further than duration', () {
+          fakeAsync((async) {
+            listen();
+            values.add(1);
+            async.elapse(const Duration(milliseconds: 6));
+            values.add(2);
+            async.elapse(const Duration(milliseconds: 6));
+            expect(emittedValues, [1, 2]);
+          });
         });
 
-        test('waits for pending value to close', () async {
-          values.add(1);
-          await values.close();
-          expect(isDone, false);
-          await waitForTimer(5);
-          expect(isDone, true);
+        test('waits for pending value to close', () {
+          fakeAsync((async) {
+            listen();
+            values
+              ..add(1)
+              ..close();
+            expect(isDone, false);
+            async.elapse(const Duration(milliseconds: 6));
+            expect(isDone, true);
+          });
         });
 
-        test('closes output if there are no pending values', () async {
-          values.add(1);
-          await waitForTimer(5);
-          values.add(2);
-          await values.close();
-          expect(isDone, false);
-          await waitForTimer(5);
-          expect(isDone, true);
+        test('closes output if there are no pending values', () {
+          fakeAsync((async) {
+            listen();
+            values.add(1);
+            async.elapse(const Duration(milliseconds: 6));
+            values
+              ..add(2)
+              ..close();
+            expect(isDone, false);
+            expect(emittedValues, [1]);
+            async.elapse(const Duration(milliseconds: 6));
+            expect(isDone, true);
+            expect(emittedValues, [1, 2]);
+          });
         });
 
         test('does not starve output if many values come closer than duration',
-            () async {
-          values.add(1);
-          await Future.delayed(const Duration(milliseconds: 4));
-          values.add(2);
-          await Future.delayed(const Duration(milliseconds: 4));
-          values.add(3);
-          await waitForTimer(6);
-          expect(emittedValues, [2, 3]);
+            () {
+          fakeAsync((async) {
+            listen();
+            values.add(1);
+            async.elapse(const Duration(milliseconds: 3));
+            values.add(2);
+            async.elapse(const Duration(milliseconds: 3));
+            values.add(3);
+            async.elapse(const Duration(milliseconds: 6));
+            expect(emittedValues, [2, 3]);
+          });
         });
 
         if (streamType == 'broadcast') {
-          test('multiple listeners all get values', () async {
-            var otherValues = [];
-            transformed.listen(otherValues.add);
-            values
-              ..add(1)
-              ..add(2);
-            await waitForTimer(5);
-            expect(emittedValues, [2]);
-            expect(otherValues, [2]);
+          test('multiple listeners all get the values', () {
+            fakeAsync((async) {
+              listen();
+              values.add(1);
+              async.elapse(const Duration(milliseconds: 3));
+              values.add(2);
+              var otherValues = [];
+              transformed.listen(otherValues.add);
+              values.add(3);
+              async.elapse(const Duration(milliseconds: 3));
+              values.add(4);
+              async.elapse(const Duration(milliseconds: 3));
+              values
+                ..add(5)
+                ..close();
+              async.elapse(const Duration(milliseconds: 6));
+              expect(emittedValues, [3, 5]);
+              expect(otherValues, [3, 5]);
+            });
           });
         }
       });
diff --git a/pkgs/stream_transform/test/debounce_test.dart b/pkgs/stream_transform/test/debounce_test.dart
index f9c97eb..6b9775f 100644
--- a/pkgs/stream_transform/test/debounce_test.dart
+++ b/pkgs/stream_transform/test/debounce_test.dart
@@ -4,6 +4,7 @@
 
 import 'dart:async';
 
+import 'package:fake_async/fake_async.dart';
 import 'package:stream_transform/stream_transform.dart';
 import 'package:test/test.dart';
 
@@ -31,63 +32,83 @@
           errors = [];
           isDone = false;
           transformed = values.stream.debounce(const Duration(milliseconds: 5));
+        });
+
+        void listen() {
           subscription = transformed
               .listen(emittedValues.add, onError: errors.add, onDone: () {
             isDone = true;
           });
-        });
+        }
 
         test('cancels values', () async {
+          listen();
           await subscription.cancel();
           expect(valuesCanceled, true);
         });
 
-        test('swallows values that come faster than duration', () async {
-          values
-            ..add(1)
-            ..add(2);
-          await values.close();
-          await waitForTimer(5);
-          expect(emittedValues, [2]);
+        test('swallows values that come faster than duration', () {
+          fakeAsync((async) {
+            listen();
+            values
+              ..add(1)
+              ..add(2)
+              ..close();
+            async.elapse(const Duration(milliseconds: 6));
+            expect(emittedValues, [2]);
+          });
         });
 
-        test('outputs multiple values spaced further than duration', () async {
-          values.add(1);
-          await waitForTimer(5);
-          values.add(2);
-          await waitForTimer(5);
-          expect(emittedValues, [1, 2]);
+        test('outputs multiple values spaced further than duration', () {
+          fakeAsync((async) {
+            listen();
+            values.add(1);
+            async.elapse(const Duration(milliseconds: 6));
+            values.add(2);
+            async.elapse(const Duration(milliseconds: 6));
+            expect(emittedValues, [1, 2]);
+          });
         });
 
-        test('waits for pending value to close', () async {
-          values.add(1);
-          await waitForTimer(5);
-          await values.close();
-          await Future(() {});
-          expect(isDone, true);
+        test('waits for pending value to close', () {
+          fakeAsync((async) {
+            listen();
+            values.add(1);
+            async.elapse(const Duration(milliseconds: 6));
+            values.close();
+            async.flushMicrotasks();
+            expect(isDone, true);
+          });
         });
 
-        test('closes output if there are no pending values', () async {
-          values.add(1);
-          await waitForTimer(5);
-          values.add(2);
-          await Future(() {});
-          await values.close();
-          expect(isDone, false);
-          await waitForTimer(5);
-          expect(isDone, true);
+        test('closes output if there are no pending values', () {
+          fakeAsync((async) {
+            listen();
+            values.add(1);
+            async.elapse(const Duration(milliseconds: 6));
+            values
+              ..add(2)
+              ..close();
+            async.flushMicrotasks();
+            expect(isDone, false);
+            async.elapse(const Duration(milliseconds: 6));
+            expect(isDone, true);
+          });
         });
 
         if (streamType == 'broadcast') {
-          test('multiple listeners all get values', () async {
-            var otherValues = [];
-            transformed.listen(otherValues.add);
-            values
-              ..add(1)
-              ..add(2);
-            await waitForTimer(5);
-            expect(emittedValues, [2]);
-            expect(otherValues, [2]);
+          test('multiple listeners all get values', () {
+            fakeAsync((async) {
+              listen();
+              var otherValues = [];
+              transformed.listen(otherValues.add);
+              values
+                ..add(1)
+                ..add(2);
+              async.elapse(const Duration(milliseconds: 6));
+              expect(emittedValues, [2]);
+              expect(otherValues, [2]);
+            });
           });
         }
       });
@@ -103,13 +124,17 @@
           emittedValues = [];
           isDone = false;
           transformed = values.stream.debounce(const Duration(milliseconds: 5),
-              leading: true, trailing: false)
-            ..listen(emittedValues.add, onDone: () {
-              isDone = true;
-            });
+              leading: true, trailing: false);
         });
 
+        void listen() {
+          transformed.listen(emittedValues.add, onDone: () {
+            isDone = true;
+          });
+        }
+
         test('swallows values that come faster than duration', () async {
+          listen();
           values
             ..add(1)
             ..add(2);
@@ -117,29 +142,36 @@
           expect(emittedValues, [1]);
         });
 
-        test('outputs multiple values spaced further than duration', () async {
-          values.add(1);
-          await waitForTimer(5);
-          values.add(2);
-          await waitForTimer(5);
-          expect(emittedValues, [1, 2]);
+        test('outputs multiple values spaced further than duration', () {
+          fakeAsync((async) {
+            listen();
+            values.add(1);
+            async.elapse(const Duration(milliseconds: 6));
+            values.add(2);
+            async.elapse(const Duration(milliseconds: 6));
+            expect(emittedValues, [1, 2]);
+          });
         });
 
         if (streamType == 'broadcast') {
-          test('multiple listeners all get values', () async {
-            var otherValues = [];
-            transformed.listen(otherValues.add);
-            values
-              ..add(1)
-              ..add(2);
-            await waitForTimer(5);
-            expect(emittedValues, [1]);
-            expect(otherValues, [1]);
+          test('multiple listeners all get values', () {
+            fakeAsync((async) {
+              listen();
+              var otherValues = [];
+              transformed.listen(otherValues.add);
+              values
+                ..add(1)
+                ..add(2);
+              async.elapse(const Duration(milliseconds: 6));
+              expect(emittedValues, [1]);
+              expect(otherValues, [1]);
+            });
           });
         }
 
         test('closes output immediately if not waiting for trailing value',
             () async {
+          listen();
           values.add(1);
           await values.close();
           expect(isDone, true);
@@ -155,38 +187,49 @@
           values = createController(streamType);
           emittedValues = [];
           transformed = values.stream.debounce(const Duration(milliseconds: 5),
-              leading: true, trailing: true)
-            ..listen(emittedValues.add);
+              leading: true, trailing: true);
+        });
+        void listen() {
+          transformed.listen(emittedValues.add);
+        }
+
+        test('swallows values that come faster than duration', () {
+          fakeAsync((async) {
+            listen();
+            values
+              ..add(1)
+              ..add(2)
+              ..add(3)
+              ..close();
+            async.elapse(const Duration(milliseconds: 6));
+            expect(emittedValues, [1, 3]);
+          });
         });
 
-        test('swallows values that come faster than duration', () async {
-          values
-            ..add(1)
-            ..add(2)
-            ..add(3);
-          await values.close();
-          await waitForTimer(5);
-          expect(emittedValues, [1, 3]);
-        });
-
-        test('outputs multiple values spaced further than duration', () async {
-          values.add(1);
-          await waitForTimer(5);
-          values.add(2);
-          await waitForTimer(5);
-          expect(emittedValues, [1, 2]);
+        test('outputs multiple values spaced further than duration', () {
+          fakeAsync((async) {
+            listen();
+            values.add(1);
+            async.elapse(const Duration(milliseconds: 6));
+            values.add(2);
+            async.elapse(const Duration(milliseconds: 6));
+            expect(emittedValues, [1, 2]);
+          });
         });
 
         if (streamType == 'broadcast') {
-          test('multiple listeners all get values', () async {
-            var otherValues = [];
-            transformed.listen(otherValues.add);
-            values
-              ..add(1)
-              ..add(2);
-            await waitForTimer(5);
-            expect(emittedValues, [1, 2]);
-            expect(otherValues, [1, 2]);
+          test('multiple listeners all get values', () {
+            fakeAsync((async) {
+              listen();
+              var otherValues = [];
+              transformed.listen(otherValues.add);
+              values
+                ..add(1)
+                ..add(2);
+              async.elapse(const Duration(milliseconds: 6));
+              expect(emittedValues, [1, 2]);
+              expect(otherValues, [1, 2]);
+            });
           });
         }
       });
@@ -201,49 +244,60 @@
           values = createController(streamType);
           emittedValues = [];
           errors = [];
-          transformed = values.stream
-              .debounceBuffer(const Duration(milliseconds: 5))
-            ..listen(emittedValues.add, onError: errors.add);
+          transformed =
+              values.stream.debounceBuffer(const Duration(milliseconds: 5));
         });
+        void listen() {
+          transformed.listen(emittedValues.add, onError: errors.add);
+        }
 
-        test('Emits all values as a list', () async {
-          values
-            ..add(1)
-            ..add(2);
-          await values.close();
-          await waitForTimer(5);
-          expect(emittedValues, [
-            [1, 2]
-          ]);
-        });
-
-        test('separate lists for multiple values spaced further than duration',
-            () async {
-          values.add(1);
-          await waitForTimer(5);
-          values.add(2);
-          await waitForTimer(5);
-          expect(emittedValues, [
-            [1],
-            [2]
-          ]);
-        });
-
-        if (streamType == 'broadcast') {
-          test('multiple listeners all get values', () async {
-            var otherValues = [];
-            transformed.listen(otherValues.add);
+        test('Emits all values as a list', () {
+          fakeAsync((async) {
+            listen();
             values
               ..add(1)
-              ..add(2);
-            await waitForTimer(5);
+              ..add(2)
+              ..close();
+            async.elapse(const Duration(milliseconds: 6));
             expect(emittedValues, [
               [1, 2]
             ]);
-            expect(otherValues, [
-              [1, 2]
+          });
+        });
+
+        test('separate lists for multiple values spaced further than duration',
+            () {
+          fakeAsync((async) {
+            listen();
+            values.add(1);
+            async.elapse(const Duration(milliseconds: 6));
+            values.add(2);
+            async.elapse(const Duration(milliseconds: 6));
+            expect(emittedValues, [
+              [1],
+              [2]
             ]);
           });
+        });
+
+        if (streamType == 'broadcast') {
+          test('multiple listeners all get values', () {
+            fakeAsync((async) {
+              listen();
+              var otherValues = [];
+              transformed.listen(otherValues.add);
+              values
+                ..add(1)
+                ..add(2);
+              async.elapse(const Duration(milliseconds: 6));
+              expect(emittedValues, [
+                [1, 2]
+              ]);
+              expect(otherValues, [
+                [1, 2]
+              ]);
+            });
+          });
         }
       });
     });
diff --git a/pkgs/stream_transform/test/throttle_test.dart b/pkgs/stream_transform/test/throttle_test.dart
index 22bf5b3..07f607a 100644
--- a/pkgs/stream_transform/test/throttle_test.dart
+++ b/pkgs/stream_transform/test/throttle_test.dart
@@ -4,6 +4,7 @@
 
 import 'dart:async';
 
+import 'package:fake_async/fake_async.dart';
 import 'package:stream_transform/stream_transform.dart';
 import 'package:test/test.dart';
 
@@ -29,49 +30,68 @@
           emittedValues = [];
           isDone = false;
           transformed = values.stream.throttle(const Duration(milliseconds: 5));
+        });
+
+        void listen() {
           subscription = transformed.listen(emittedValues.add, onDone: () {
             isDone = true;
           });
-        });
+        }
 
         test('cancels values', () async {
+          listen();
           await subscription.cancel();
           expect(valuesCanceled, true);
         });
 
-        test('swallows values that come faster than duration', () async {
-          values
-            ..add(1)
-            ..add(2);
-          await values.close();
-          await waitForTimer(5);
-          expect(emittedValues, [1]);
+        test('swallows values that come faster than duration', () {
+          fakeAsync((async) {
+            listen();
+            values
+              ..add(1)
+              ..add(2)
+              ..close();
+            async.elapse(const Duration(milliseconds: 6));
+            expect(emittedValues, [1]);
+          });
         });
 
-        test('outputs multiple values spaced further than duration', () async {
-          values.add(1);
-          await waitForTimer(5);
-          values.add(2);
-          await waitForTimer(5);
-          expect(emittedValues, [1, 2]);
+        test('outputs multiple values spaced further than duration', () {
+          fakeAsync((async) {
+            listen();
+            values.add(1);
+            async.elapse(const Duration(milliseconds: 6));
+            values.add(2);
+            async.elapse(const Duration(milliseconds: 6));
+            expect(emittedValues, [1, 2]);
+            async.elapse(const Duration(milliseconds: 6));
+          });
         });
 
-        test('closes output immediately', () async {
-          values.add(1);
-          await waitForTimer(5);
-          values.add(2);
-          await values.close();
-          expect(isDone, true);
+        test('closes output immediately', () {
+          fakeAsync((async) {
+            listen();
+            values.add(1);
+            async.elapse(const Duration(milliseconds: 6));
+            values
+              ..add(2)
+              ..close();
+            async.flushMicrotasks();
+            expect(isDone, true);
+          });
         });
 
         if (streamType == 'broadcast') {
-          test('multiple listeners all get values', () async {
-            var otherValues = <int>[];
-            transformed.listen(otherValues.add);
-            values.add(1);
-            await Future(() {});
-            expect(emittedValues, [1]);
-            expect(otherValues, [1]);
+          test('multiple listeners all get values', () {
+            fakeAsync((async) {
+              listen();
+              var otherValues = <int>[];
+              transformed.listen(otherValues.add);
+              values.add(1);
+              async.flushMicrotasks();
+              expect(emittedValues, [1]);
+              expect(otherValues, [1]);
+            });
           });
         }
       });
@@ -87,65 +107,84 @@
           isDone = false;
           transformed = values.stream
               .throttle(const Duration(milliseconds: 5), trailing: true);
+        });
+        void listen() {
           subscription = transformed.listen(emittedValues.add, onDone: () {
             isDone = true;
           });
+        }
+
+        test('emits both first and last in a period', () {
+          fakeAsync((async) {
+            listen();
+            values
+              ..add(1)
+              ..add(2)
+              ..close();
+            async.elapse(const Duration(milliseconds: 6));
+            expect(emittedValues, [1, 2]);
+          });
         });
 
-        test('emits both first and last in a period', () async {
-          values
-            ..add(1)
-            ..add(2);
-          await values.close();
-          await waitForTimer(5);
-          expect(emittedValues, [1, 2]);
-        });
-
-        test('swallows values that are not the latest in a period', () async {
-          values
-            ..add(1)
-            ..add(2)
-            ..add(3);
-          await values.close();
-          await waitForTimer(5);
-          expect(emittedValues, [1, 3]);
+        test('swallows values that are not the latest in a period', () {
+          fakeAsync((async) {
+            listen();
+            values
+              ..add(1)
+              ..add(2)
+              ..add(3)
+              ..close();
+            async.elapse(const Duration(milliseconds: 6));
+            expect(emittedValues, [1, 3]);
+          });
         });
 
         test('waits to output the last value even if the stream closes',
             () async {
-          values
-            ..add(1)
-            ..add(2);
-          await values.close();
-          await Future(() {});
-          expect(isDone, false);
-          expect(emittedValues, [1],
-              reason: 'Should not be emitted until after duration');
-          await waitForTimer(5);
-          expect(emittedValues, [1, 2]);
-          expect(isDone, true);
+          fakeAsync((async) {
+            listen();
+            values
+              ..add(1)
+              ..add(2)
+              ..close();
+            async.flushMicrotasks();
+            expect(isDone, false);
+            expect(emittedValues, [1],
+                reason: 'Should not be emitted until after duration');
+            async.elapse(const Duration(milliseconds: 6));
+            expect(emittedValues, [1, 2]);
+            expect(isDone, true);
+            async.elapse(const Duration(milliseconds: 6));
+          });
         });
 
-        test('closes immediately if there is no pending value', () async {
-          values.add(1);
-          await values.close();
-          await Future(() {});
-          expect(isDone, true);
+        test('closes immediately if there is no pending value', () {
+          fakeAsync((async) {
+            listen();
+            values
+              ..add(1)
+              ..close();
+            async.flushMicrotasks();
+            expect(isDone, true);
+          });
         });
 
         if (streamType == 'broadcast') {
-          test('multiple listeners all get values', () async {
-            var otherValues = <int>[];
-            transformed.listen(otherValues.add);
-            values
-              ..add(1)
-              ..add(2);
-            await Future(() {});
-            expect(emittedValues, [1]);
-            expect(otherValues, [1]);
-            await waitForTimer(5);
-            expect(emittedValues, [1, 2]);
-            expect(otherValues, [1, 2]);
+          test('multiple listeners all get values', () {
+            fakeAsync((async) {
+              listen();
+              var otherValues = <int>[];
+              transformed.listen(otherValues.add);
+              values
+                ..add(1)
+                ..add(2);
+              async.flushMicrotasks();
+              expect(emittedValues, [1]);
+              expect(otherValues, [1]);
+              async.elapse(const Duration(milliseconds: 6));
+              expect(emittedValues, [1, 2]);
+              expect(otherValues, [1, 2]);
+            });
           });
         }
       });
diff --git a/pkgs/stream_transform/test/utils.dart b/pkgs/stream_transform/test/utils.dart
index b6196d6..42d9613 100644
--- a/pkgs/stream_transform/test/utils.dart
+++ b/pkgs/stream_transform/test/utils.dart
@@ -4,12 +4,6 @@
 
 import 'dart:async';
 
-/// Cycle the event loop to ensure timers are started, then wait for a delay
-/// longer than [milliseconds] to allow for the timer to fire.
-Future<void> waitForTimer(int milliseconds) =>
-    Future(() {/* ensure Timer is started*/})
-        .then((_) => Future.delayed(Duration(milliseconds: milliseconds + 1)));
-
 StreamController<T> createController<T>(String streamType) {
   switch (streamType) {
     case 'single subscription':