Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(557)

Unified Diff: lib/src/backend/invoker.dart

Issue 1116443002: Support future matchers and expectAsync in tearDowns. (Closed) Base URL: git@github.com:dart-lang/unittest.git@master
Patch Set: Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: lib/src/backend/invoker.dart
diff --git a/lib/src/backend/invoker.dart b/lib/src/backend/invoker.dart
index 81a931e4eec7e2315f5df21b9b3daedc69294847..a0151c385ec722bcb38e6d045e5f747d9111a1ba 100644
--- a/lib/src/backend/invoker.dart
+++ b/lib/src/backend/invoker.dart
@@ -14,6 +14,7 @@ import 'closed_exception.dart';
import 'live_test.dart';
import 'live_test_controller.dart';
import 'metadata.dart';
+import 'outstanding_callback_counter.dart';
import 'state.dart';
import 'suite.dart';
import 'test.dart';
@@ -76,14 +77,13 @@ class Invoker {
/// The test metadata merged with the suite metadata.
final Metadata metadata;
- /// Note that this is meaningless once [_onCompleteCompleter] is complete.
- var _outstandingCallbacks = 0;
-
- /// The completer to complete once the test body finishes.
- ///
- /// This is distinct from [_controller.completer] because a tear-down may need
- /// to run before the test is truly finished.
- final _completer = new Completer();
+ /// The outstanding callback counter for the current zone.
+ OutstandingCallbackCounter get _outstandingCallbacks {
+ var counter = Zone.current[this];
+ if (counter != null) return counter;
+ throw new StateError("Can't add or remove outstanding callbacks outside "
+ "of a test body.");
+ }
/// The current invoker, or `null` if none is defined.
///
@@ -112,22 +112,13 @@ class Invoker {
/// Throws a [ClosedException] if this test has been closed.
void addOutstandingCallback() {
if (closed) throw new ClosedException();
- _outstandingCallbacks++;
+ _outstandingCallbacks.addOutstandingCallback();
}
/// Tells the invoker that a callback declared with [addOutstandingCallback]
/// is no longer running.
- void removeOutstandingCallback() {
- _outstandingCallbacks--;
-
- if (_outstandingCallbacks != 0) return;
- if (_completer.isCompleted) return;
-
- // The test must be passing if we get here, because if there were an error
- // the completer would already be completed.
- assert(liveTest.state.result == Result.success);
- _completer.complete();
- }
+ void removeOutstandingCallback() =>
+ _outstandingCallbacks.removeOutstandingCallback();
/// Notifies the invoker of an asynchronous error.
///
@@ -146,8 +137,7 @@ class Invoker {
}
_controller.addError(error, stackTrace);
-
- if (!_completer.isCompleted) _completer.complete();
+ _outstandingCallbacks.removeAllOutstandingCallbacks();
// If a test was marked as success but then had an error, that indicates
// that it was poorly-written and could be flaky.
@@ -163,11 +153,12 @@ class Invoker {
void _onRun() {
_controller.setState(const State(Status.running, Result.success));
+ var outstandingCallbacksForBody = new OutstandingCallbackCounter();
+
Chain.capture(() {
- runZoned(() {
- // TODO(nweiz): Make the timeout configurable.
- // TODO(nweiz): Reset this timer whenever the user's code interacts with
- // the library.
+ runZonedWithValues(() {
+ // TODO(nweiz): Reset this timer whenever the user's code interacts
+ // with the library.
var timeout = metadata.timeout.apply(new Duration(seconds: 30));
var timer = new Timer(timeout, () {
if (liveTest.isComplete) return;
@@ -176,20 +167,30 @@ class Invoker {
"Test timed out after ${niceDuration(timeout)}.", timeout));
});
- addOutstandingCallback();
-
- // Run the test asynchronously so that the "running" state change has a
- // chance to hit its event handler(s) before the test produces an error.
- // If an error is emitted before the first state change is handled, we
- // can end up with [onError] callbacks firing before the corresponding
- // [onStateChange], which violates the timing guarantees.
+ // Run the test asynchronously so that the "running" state change has
+ // a chance to hit its event handler(s) before the test produces an
+ // error. If an error is emitted before the first state change is
+ // handled, we can end up with [onError] callbacks firing before the
+ // corresponding [onStateChange], which violates the timing
+ // guarantees.
new Future(_test._body)
.then((_) => removeOutstandingCallback());
- _completer.future.then((_) {
+ _outstandingCallbacks.noOutstandingCallbacks.then((_) {
if (_test._tearDown == null) return null;
- return new Future.sync(_test._tearDown);
- }).catchError(Zone.current.handleUncaughtError).then((_) {
+
+ // Reset the outstanding callback counter to wait for callbacks from
+ // the test's `tearDown` to complete.
+ var outstandingCallbacksForTearDown = new OutstandingCallbackCounter();
+ runZonedWithValues(() {
+ new Future.sync(_test._tearDown)
+ .then((_) => removeOutstandingCallback());
+ }, onError: handleError, zoneValues: {
+ this: outstandingCallbacksForTearDown
+ });
+
+ return outstandingCallbacksForTearDown.noOutstandingCallbacks;
+ }).then((_) {
timer.cancel();
_controller.setState(
new State(Status.complete, liveTest.state.result));
@@ -198,10 +199,14 @@ class Invoker {
// non-microtask events.
Timer.run(_controller.completer.complete);
});
+ }, zoneValues: {
+ #test.invoker: this,
+ // Use the invoker as a key so that multiple invokers can have different
+ // outstanding callback counters at once.
+ this: outstandingCallbacksForBody
},
zoneSpecification: new ZoneSpecification(
print: (self, parent, zone, line) => _controller.print(line)),
- zoneValues: {#test.invoker: this},
onError: handleError);
});
}

Powered by Google App Engine
This is Rietveld 408576698