| Index: lib/src/backend/invoker.dart
|
| diff --git a/lib/src/backend/invoker.dart b/lib/src/backend/invoker.dart
|
| index dfcc795e04f0749513945a01c07378f52eccc486..5b6eec6264a933d19da7d885c91924d2ca8fb100 100644
|
| --- a/lib/src/backend/invoker.dart
|
| +++ b/lib/src/backend/invoker.dart
|
| @@ -92,6 +92,12 @@ class Invoker {
|
| "of a test body.");
|
| }
|
|
|
| + /// All the zones created by [waitForOutstandingCallbacks], in the order they
|
| + /// were created.
|
| + ///
|
| + /// This is used to throw timeout errors in the most recent zone.
|
| + final _outstandingCallbackZones = <Zone>[];
|
| +
|
| /// An opaque object used as a key in the zone value map to identify
|
| /// [_outstandingCallbacks].
|
| ///
|
| @@ -164,21 +170,30 @@ class Invoker {
|
| /// failed, removes all outstanding callbacks registered within [fn], and
|
| /// completes the returned future. It does not remove any outstanding
|
| /// callbacks registered outside of [fn].
|
| + ///
|
| + /// If the test times out, the *most recent* call to
|
| + /// [waitForOutstandingCallbacks] will treat that error as occurring within
|
| + /// [fn]—that is, it will complete immediately.
|
| Future waitForOutstandingCallbacks(fn()) {
|
| heartbeat();
|
|
|
| + var zone;
|
| var counter = new OutstandingCallbackCounter();
|
| runZoned(() {
|
| // TODO(nweiz): Use async/await here once issue 23497 has been fixed in
|
| // two stable versions.
|
| runZoned(() {
|
| + zone = Zone.current;
|
| + _outstandingCallbackZones.add(zone);
|
| new Future.sync(fn).then((_) => counter.removeOutstandingCallback());
|
| }, onError: _handleError);
|
| }, zoneValues: {
|
| _counterKey: counter
|
| });
|
|
|
| - return counter.noOutstandingCallbacks;
|
| + return counter.noOutstandingCallbacks.whenComplete(() {
|
| + _outstandingCallbackZones.remove(zone);
|
| + });
|
| }
|
|
|
| /// Runs [fn] in a zone where [closed] is always `false`.
|
| @@ -205,13 +220,14 @@ class Invoker {
|
| var timeout = liveTest.test.metadata.timeout
|
| .apply(new Duration(seconds: 30));
|
| if (timeout == null) return;
|
| - _timeoutTimer = _invokerZone.createTimer(timeout,
|
| - Zone.current.bindCallback(() {
|
| - if (liveTest.isComplete) return;
|
| - _handleError(
|
| - new TimeoutException(
|
| - "Test timed out after ${niceDuration(timeout)}.", timeout));
|
| - }));
|
| + _timeoutTimer = _invokerZone.createTimer(timeout, () {
|
| + _outstandingCallbackZones.last.run(() {
|
| + if (liveTest.isComplete) return;
|
| + _handleError(
|
| + new TimeoutException(
|
| + "Test timed out after ${niceDuration(timeout)}.", timeout));
|
| + });
|
| + });
|
| }
|
|
|
| /// Notifies the invoker of an asynchronous error.
|
| @@ -251,8 +267,7 @@ class Invoker {
|
| Chain.capture(() {
|
| runZonedWithValues(() {
|
| _invokerZone = Zone.current;
|
| -
|
| - heartbeat();
|
| + _outstandingCallbackZones.add(Zone.current);
|
|
|
| // Run the test asynchronously so that the "running" state change has
|
| // a chance to hit its event handler(s) before the test produces an
|
|
|