Merge pull request #713 from dart-lang/add-tear-down-all
Make addTearDown() play nice with setUpAll()
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e38fd1f..4a87965 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
## 0.12.27
+* When `addTearDown()` is called within a call to `setUpAll()`, it runs its
+ callback after *all* tests instead of running it after the `setUpAll()`
+ callback.
+
* When running in an interactive terminal, the test runner now prints status
lines as wide as the terminal and no wider.
diff --git a/lib/src/backend/declarer.dart b/lib/src/backend/declarer.dart
index 4e8851d..29490eb 100644
--- a/lib/src/backend/declarer.dart
+++ b/lib/src/backend/declarer.dart
@@ -155,11 +155,15 @@
}
}
- await Invoker.current.waitForOutstandingCallbacks(() async {
- await _runSetUps();
- await body();
- });
- }, trace: _collectTraces ? new Trace.current(2) : null));
+ await runZoned(
+ () => Invoker.current.waitForOutstandingCallbacks(() async {
+ await _runSetUps();
+ await body();
+ }),
+ // Make the declarer visible to running tests so that they'll throw
+ // useful errors when calling `test()` and `group()` within a test.
+ zoneValues: {#test.declarer: this});
+ }, trace: _collectTraces ? new Trace.current(2) : null, guarded: false));
}
/// Creates a group of tests.
@@ -224,7 +228,14 @@
_tearDownAlls.add(callback);
}
+ /// Like [tearDownAll], but called from within a running [setUpAll] test to
+ /// dynamically add a [tearDownAll].
+ void addTearDownAll(callback()) => _tearDownAlls.add(callback);
+
/// Finalizes and returns the group being declared.
+ ///
+ /// **Note**: The tests in this group must be run in a [Invoker.guard]
+ /// context; otherwise, test errors won't be captured.
Group build() {
_checkNotBuilt("build");
@@ -258,18 +269,30 @@
if (_setUpAlls.isEmpty) return null;
return new LocalTest(_prefix("(setUpAll)"), _metadata, () {
- return Future.forEach(_setUpAlls, (setUp) => setUp());
- }, trace: _setUpAllTrace);
+ return runZoned(() => Future.forEach(_setUpAlls, (setUp) => setUp()),
+ // Make the declarer visible to running scaffolds so they can add to
+ // the declarer's `tearDownAll()` list.
+ zoneValues: {#test.declarer: this});
+ }, trace: _setUpAllTrace, guarded: false, isScaffoldAll: true);
}
/// Returns a [Test] that runs the callbacks in [_tearDownAll].
Test get _tearDownAll {
- if (_tearDownAlls.isEmpty) return null;
+ // We have to create a tearDownAll if there's a setUpAll, since it might
+ // dynamically add tear-down code using [addTearDownAll].
+ if (_setUpAlls.isEmpty && _tearDownAlls.isEmpty) return null;
return new LocalTest(_prefix("(tearDownAll)"), _metadata, () {
- return Invoker.current.unclosable(() {
- return Future.forEach(_tearDownAlls.reversed, errorsDontStopTest);
- });
- }, trace: _tearDownAllTrace);
+ return runZoned(() {
+ return Invoker.current.unclosable(() async {
+ while (_tearDownAlls.isNotEmpty) {
+ await errorsDontStopTest(_tearDownAlls.removeLast());
+ }
+ });
+ },
+ // Make the declarer visible to running scaffolds so they can add to
+ // the declarer's `tearDownAll()` list.
+ zoneValues: {#test.declarer: this});
+ }, trace: _tearDownAllTrace, guarded: false, isScaffoldAll: true);
}
}
diff --git a/lib/src/backend/invoker.dart b/lib/src/backend/invoker.dart
index 12295c5..2335d08 100644
--- a/lib/src/backend/invoker.dart
+++ b/lib/src/backend/invoker.dart
@@ -10,6 +10,7 @@
import '../runner/load_suite.dart';
import '../utils.dart';
import 'closed_exception.dart';
+import 'declarer.dart';
import 'group.dart';
import 'live_test.dart';
import 'live_test_controller.dart';
@@ -28,21 +29,38 @@
final Metadata metadata;
final Trace trace;
- /// The test body.
- final AsyncFunction _body;
+ /// Whether this is a test defined using `setUpAll()` or `tearDownAll()`.
+ final bool isScaffoldAll;
- LocalTest(this.name, this.metadata, body(), {this.trace}) : _body = body;
+ /// The test body.
+ final Function() _body;
+
+ /// Whether the test is run in its own error zone.
+ final bool _guarded;
+
+ /// Creates a new [LocalTest].
+ ///
+ /// If [guarded] is `true`, the test is run in its own error zone, and any
+ /// errors that escape that zone cause the test to fail. If it's `false`, it's
+ /// the caller's responsiblity to invoke [LiveTest.run] in the context of a
+ /// call to [Invoker.guard].
+ LocalTest(this.name, this.metadata, this._body,
+ {this.trace, bool guarded: true, this.isScaffoldAll: false})
+ : _guarded = guarded;
+
+ LocalTest._(this.name, this.metadata, this._body, this.trace, this._guarded,
+ this.isScaffoldAll);
/// Loads a single runnable instance of this test.
LiveTest load(Suite suite, {Iterable<Group> groups}) {
- var invoker = new Invoker._(suite, this, groups: groups);
+ var invoker = new Invoker._(suite, this, groups: groups, guarded: _guarded);
return invoker.liveTest;
}
Test forPlatform(TestPlatform platform, {OperatingSystem os}) {
if (!metadata.testOn.evaluate(platform, os: os)) return null;
- return new LocalTest(name, metadata.forPlatform(platform, os: os), _body,
- trace: trace);
+ return new LocalTest._(name, metadata.forPlatform(platform, os: os), _body,
+ trace, _guarded, isScaffoldAll);
}
}
@@ -58,6 +76,9 @@
LiveTest get liveTest => _controller.liveTest;
LiveTestController _controller;
+ /// Whether to run this test in its own error zone.
+ final bool _guarded;
+
/// Whether the test can be closed in the current zone.
bool get _closable => Zone.current[_closableKey];
@@ -118,6 +139,22 @@
return Zone.current[#test.invoker];
}
+ /// Runs [callback] in a zone where unhandled errors from [LiveTest]s are
+ /// caught and dispatched to the appropriate [Invoker].
+ static T guard<T>(T callback()) =>
+ runZoned(callback, zoneSpecification: new ZoneSpecification(
+ // Use [handleUncaughtError] rather than [onError] so we can
+ // capture [zone] and with it the outstanding callback counter for
+ // the zone in which [error] was thrown.
+ handleUncaughtError: (self, _, zone, error, stackTrace) {
+ var invoker = zone[#test.invoker];
+ if (invoker != null) {
+ self.parent.run(() => invoker._handleError(zone, error, stackTrace));
+ } else {
+ self.parent.handleUncaughtError(error, stackTrace);
+ }
+ }));
+
/// The zone that the top level of [_test.body] is running in.
///
/// Tracking this ensures that [_timeoutTimer] isn't created in a
@@ -135,7 +172,9 @@
/// Messages to print if and when this test fails.
final _printsOnFailure = <String>[];
- Invoker._(Suite suite, LocalTest test, {Iterable<Group> groups}) {
+ Invoker._(Suite suite, LocalTest test,
+ {Iterable<Group> groups, bool guarded: true})
+ : _guarded = guarded {
_controller = new LiveTestController(
suite, test, _onRun, _onCloseCompleter.complete,
groups: groups);
@@ -147,7 +186,12 @@
/// run in the reverse of the order they're declared.
void addTearDown(callback()) {
if (closed) throw new ClosedException();
- _tearDowns.add(callback);
+
+ if (_test.isScaffoldAll) {
+ Declarer.current.addTearDownAll(callback);
+ } else {
+ _tearDowns.add(callback);
+ }
}
/// Tells the invoker that there's a callback running that it should wait for
@@ -282,7 +326,15 @@
void _handleError(Zone zone, error, [StackTrace stackTrace]) {
// Ignore errors propagated from previous test runs
if (_runCount != zone[#runCount]) return;
- if (stackTrace == null) stackTrace = new Chain.current();
+
+ // Get the chain information from the zone in which the error was thrown.
+ zone.run(() {
+ if (stackTrace == null) {
+ stackTrace = new Chain.current();
+ } else {
+ stackTrace = new Chain.forTrace(stackTrace);
+ }
+ });
// Store these here because they'll change when we set the state below.
var shouldBeDone = liveTest.state.shouldBeDone;
@@ -334,60 +386,68 @@
_runCount++;
Chain.capture(() {
- runZoned(() async {
- _invokerZone = Zone.current;
- _outstandingCallbackZones.add(Zone.current);
+ _guardIfGuarded(() {
+ runZoned(() async {
+ _invokerZone = Zone.current;
+ _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
- // error. If an error is emitted before the first state change is
- // handled, we can end up with [onError] callbacks firing before the
- // corresponding [onStateChkange], which violates the timing
- // guarantees.
- //
- // Using [new Future] also avoids starving the DOM or other
- // microtask-level events.
- new Future(() async {
- await _test._body();
- await unclosable(_runTearDowns);
- removeOutstandingCallback();
- });
+ // 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 [onStateChkange], which violates the timing
+ // guarantees.
+ //
+ // Using [new Future] also avoids starving the DOM or other
+ // microtask-level events.
+ new Future(() async {
+ await _test._body();
+ await unclosable(_runTearDowns);
+ removeOutstandingCallback();
+ });
- await _outstandingCallbacks.noOutstandingCallbacks;
- if (_timeoutTimer != null) _timeoutTimer.cancel();
+ await _outstandingCallbacks.noOutstandingCallbacks;
+ if (_timeoutTimer != null) _timeoutTimer.cancel();
- if (liveTest.state.result != Result.success &&
- _runCount < liveTest.test.metadata.retry + 1) {
+ if (liveTest.state.result != Result.success &&
+ _runCount < liveTest.test.metadata.retry + 1) {
+ _controller
+ .message(new Message.print("Retry: ${liveTest.test.name}"));
+ _onRun();
+ return;
+ }
+
_controller
- .message(new Message.print("Retry: ${liveTest.test.name}"));
- _onRun();
- return;
- }
+ .setState(new State(Status.complete, liveTest.state.result));
- _controller.setState(new State(Status.complete, liveTest.state.result));
-
- _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.
- _counterKey: outstandingCallbacksForBody,
- _closableKey: true,
- #runCount: _runCount
- },
- zoneSpecification: new ZoneSpecification(
- print: (self, parent, zone, line) =>
- _controller.message(new Message.print(line)),
- // Use [handleUncaughtError] rather than [onError] so we can
- // capture [zone] and with it the outstanding callback counter for
- // the zone in which [error] was thrown.
- handleUncaughtError: (self, _, zone, error, stackTrace) => self
- .parent
- .run(() => _handleError(zone, error, stackTrace))));
- }, when: liveTest.test.metadata.chainStackTraces);
+ _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.
+ _counterKey: outstandingCallbacksForBody,
+ _closableKey: true,
+ #runCount: _runCount,
+ },
+ zoneSpecification: new ZoneSpecification(
+ print: (_, __, ___, line) => _print(line)));
+ });
+ }, when: liveTest.test.metadata.chainStackTraces, errorZone: false);
}
+ /// Runs [callback], in a [Invoker.guard] context if [_guarded] is `true`.
+ void _guardIfGuarded(void callback()) {
+ if (_guarded) {
+ Invoker.guard(callback);
+ } else {
+ callback();
+ }
+ }
+
+ /// Prints [text] as a message to [_controller].
+ void _print(String text) => _controller.message(new Message.print(text));
+
/// Run [_tearDowns] in reverse order.
Future _runTearDowns() async {
while (_tearDowns.isNotEmpty) {
diff --git a/lib/src/runner/remote_listener.dart b/lib/src/runner/remote_listener.dart
index 883101f..bf08c44 100644
--- a/lib/src/runner/remote_listener.dart
+++ b/lib/src/runner/remote_listener.dart
@@ -9,6 +9,7 @@
import '../backend/declarer.dart';
import '../backend/group.dart';
+import '../backend/invoker.dart';
import '../backend/live_test.dart';
import '../backend/metadata.dart';
import '../backend/operating_system.dart';
@@ -97,7 +98,14 @@
: OperatingSystem.find(message['os']),
path: message['path']);
- new RemoteListener._(suite, printZone)._listen(channel);
+ runZoned(() {
+ Invoker.guard(
+ () => new RemoteListener._(suite, printZone)._listen(channel));
+ },
+ // Make the declarer visible to running tests so that they'll throw
+ // useful errors when calling `test()` and `group()` within a test,
+ // and so they can add to the declarer's `tearDownAll()` list.
+ zoneValues: {#test.declarer: declarer});
}, onError: (error, stackTrace) {
_sendError(channel, error, stackTrace, verboseChain);
}, zoneSpecification: new ZoneSpecification(print: (_, __, ___, line) {
diff --git a/lib/test.dart b/lib/test.dart
index 2550bef..8d159ad 100644
--- a/lib/test.dart
+++ b/lib/test.dart
@@ -67,7 +67,8 @@
ExpandedReporter.watch(engine,
color: true, printPath: false, printPlatform: false);
- var success = await engine.run();
+ var success = await runZoned(() => Invoker.guard(engine.run),
+ zoneValues: {#test.declarer: _globalDeclarer});
// TODO(nweiz): Set the exit code on the VM when issue 6943 is fixed.
if (success) return null;
print('');
@@ -252,6 +253,9 @@
///
/// The [callback] is run before any callbacks registered with [tearDown]. Like
/// [tearDown], the most recently registered callback is run first.
+///
+/// If this is called from within a [setUpAll] or [tearDownAll] callback, it
+/// instead runs the function after *all* tests in the current test suite.
void addTearDown(callback()) {
if (Invoker.current == null) {
throw new StateError("addTearDown() may only be called within a test.");
diff --git a/pubspec.yaml b/pubspec.yaml
index 5c20d16..a730642 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: test
-version: 0.12.27-dev
+version: 0.12.27
author: Dart Team <misc@dartlang.org>
description: A library for writing dart unit tests.
homepage: https://github.com/dart-lang/test
@@ -29,7 +29,7 @@
source_map_stack_trace: '^1.1.4'
source_maps: '^0.10.2'
source_span: '^1.4.0'
- stack_trace: '^1.6.0'
+ stack_trace: '^1.9.0'
stream_channel: '^1.6.0'
string_scanner: '>=0.1.1 <2.0.0'
term_glyph: '^1.0.0'
diff --git a/test/frontend/add_tear_down_test.dart b/test/frontend/add_tear_down_test.dart
index 650f0fd..3a63ff8 100644
--- a/test/frontend/add_tear_down_test.dart
+++ b/test/frontend/add_tear_down_test.dart
@@ -7,319 +7,25 @@
import 'package:async/async.dart';
import 'package:test/test.dart';
+import 'package:test/src/backend/declarer.dart';
+
import '../utils.dart';
void main() {
- test("runs after the test body", () {
- return expectTestsPass(() {
- var test1Run = false;
- var tearDownRun = false;
- test("test 1", () {
- addTearDown(() {
- expect(test1Run, isTrue);
- expect(tearDownRun, isFalse);
- tearDownRun = true;
- });
-
- expect(tearDownRun, isFalse);
- test1Run = true;
- });
-
- test("test 2", () {
- expect(tearDownRun, isTrue);
- });
- });
- });
-
- test("multiples run in reverse order", () {
- return expectTestsPass(() {
- var tearDown1Run = false;
- var tearDown2Run = false;
- var tearDown3Run = false;
-
- test("test 1", () {
- addTearDown(() {
- expect(tearDown1Run, isFalse);
- expect(tearDown2Run, isTrue);
- expect(tearDown3Run, isTrue);
- tearDown1Run = true;
- });
-
- addTearDown(() {
- expect(tearDown1Run, isFalse);
- expect(tearDown2Run, isFalse);
- expect(tearDown3Run, isTrue);
- tearDown2Run = true;
- });
-
- addTearDown(() {
- expect(tearDown1Run, isFalse);
- expect(tearDown2Run, isFalse);
- expect(tearDown3Run, isFalse);
- tearDown3Run = true;
- });
-
- expect(tearDown1Run, isFalse);
- expect(tearDown2Run, isFalse);
- expect(tearDown3Run, isFalse);
- });
-
- test("test 2", () {
- expect(tearDown1Run, isTrue);
- expect(tearDown2Run, isTrue);
- expect(tearDown3Run, isTrue);
- });
- });
- });
-
- test("can be called in addTearDown", () {
- return expectTestsPass(() {
- var tearDown2Run = false;
- var tearDown3Run = false;
-
- test("test 1", () {
- addTearDown(() {
- expect(tearDown2Run, isTrue);
- expect(tearDown3Run, isFalse);
- tearDown3Run = true;
- });
-
- addTearDown(() {
- addTearDown(() {
- expect(tearDown2Run, isFalse);
- expect(tearDown3Run, isFalse);
- tearDown2Run = true;
- });
- });
- });
-
- test("test 2", () {
- expect(tearDown2Run, isTrue);
- expect(tearDown3Run, isTrue);
- });
- });
- });
-
- test("can be called in tearDown", () {
- return expectTestsPass(() {
- var tearDown2Run = false;
- var tearDown3Run = false;
-
- tearDown(() {
- expect(tearDown2Run, isTrue);
- expect(tearDown3Run, isFalse);
- tearDown3Run = true;
- });
-
- tearDown(() {
- tearDown2Run = false;
- tearDown3Run = false;
-
- addTearDown(() {
- expect(tearDown2Run, isFalse);
- expect(tearDown3Run, isFalse);
- tearDown2Run = true;
- });
- });
-
- test("test 1", () {});
-
- test("test 2", () {
- expect(tearDown2Run, isTrue);
- expect(tearDown3Run, isTrue);
- });
- });
- });
-
- test("runs before a normal tearDown", () {
- return expectTestsPass(() {
- var groupTearDownRun = false;
- var testTearDownRun = false;
- group("group", () {
- tearDown(() {
- expect(testTearDownRun, isTrue);
- expect(groupTearDownRun, isFalse);
- groupTearDownRun = true;
- });
-
- test("test 1", () {
- addTearDown(() {
- expect(groupTearDownRun, isFalse);
- expect(testTearDownRun, isFalse);
- testTearDownRun = true;
- });
-
- expect(groupTearDownRun, isFalse);
- expect(testTearDownRun, isFalse);
- });
- });
-
- test("test 2", () {
- expect(groupTearDownRun, isTrue);
- expect(testTearDownRun, isTrue);
- });
- });
- });
-
- test("runs in the same error zone as the test", () {
- return expectTestsPass(() {
- test("test", () {
- var future = new Future.error("oh no");
- expect(future, throwsA("oh no"));
-
- addTearDown(() {
- // If the tear-down is in a different error zone than the test, the
- // error will try to cross the zone boundary and get top-leveled.
- expect(future, throwsA("oh no"));
- });
- });
- });
- });
-
- group("asynchronously", () {
- test("blocks additional test tearDowns on in-band async", () {
+ group("in a test", () {
+ test("runs after the test body", () {
return expectTestsPass(() {
- var tearDown1Run = false;
- var tearDown2Run = false;
- var tearDown3Run = false;
- test("test", () {
- addTearDown(() async {
- expect(tearDown1Run, isFalse);
- expect(tearDown2Run, isTrue);
- expect(tearDown3Run, isTrue);
- await pumpEventQueue();
- tearDown1Run = true;
- });
-
- addTearDown(() async {
- expect(tearDown1Run, isFalse);
- expect(tearDown2Run, isFalse);
- expect(tearDown3Run, isTrue);
- await pumpEventQueue();
- tearDown2Run = true;
- });
-
- addTearDown(() async {
- expect(tearDown1Run, isFalse);
- expect(tearDown2Run, isFalse);
- expect(tearDown3Run, isFalse);
- await pumpEventQueue();
- tearDown3Run = true;
- });
-
- expect(tearDown1Run, isFalse);
- expect(tearDown2Run, isFalse);
- expect(tearDown3Run, isFalse);
- });
- });
- });
-
- test("doesn't block additional test tearDowns on out-of-band async", () {
- return expectTestsPass(() {
- var tearDown1Run = false;
- var tearDown2Run = false;
- var tearDown3Run = false;
- test("test", () {
- addTearDown(() {
- expect(tearDown1Run, isFalse);
- expect(tearDown2Run, isFalse);
- expect(tearDown3Run, isFalse);
-
- expect(new Future(() {
- tearDown1Run = true;
- }), completes);
- });
-
- addTearDown(() {
- expect(tearDown1Run, isFalse);
- expect(tearDown2Run, isFalse);
- expect(tearDown3Run, isFalse);
-
- expect(new Future(() {
- tearDown2Run = true;
- }), completes);
- });
-
- addTearDown(() {
- expect(tearDown1Run, isFalse);
- expect(tearDown2Run, isFalse);
- expect(tearDown3Run, isFalse);
-
- expect(new Future(() {
- tearDown3Run = true;
- }), completes);
- });
-
- expect(tearDown1Run, isFalse);
- expect(tearDown2Run, isFalse);
- expect(tearDown3Run, isFalse);
- });
- });
- });
-
- test("blocks additional group tearDowns on in-band async", () {
- return expectTestsPass(() {
- var groupTearDownRun = false;
- var testTearDownRun = false;
- tearDown(() async {
- expect(groupTearDownRun, isFalse);
- expect(testTearDownRun, isTrue);
- await pumpEventQueue();
- groupTearDownRun = true;
- });
-
- test("test", () {
- addTearDown(() async {
- expect(groupTearDownRun, isFalse);
- expect(testTearDownRun, isFalse);
- await pumpEventQueue();
- testTearDownRun = true;
- });
-
- expect(groupTearDownRun, isFalse);
- expect(testTearDownRun, isFalse);
- });
- });
- });
-
- test("doesn't block additional group tearDowns on out-of-band async", () {
- return expectTestsPass(() {
- var groupTearDownRun = false;
- var testTearDownRun = false;
- tearDown(() {
- expect(groupTearDownRun, isFalse);
- expect(testTearDownRun, isFalse);
-
- expect(new Future(() {
- groupTearDownRun = true;
- }), completes);
- });
-
- test("test", () {
- addTearDown(() {
- expect(groupTearDownRun, isFalse);
- expect(testTearDownRun, isFalse);
-
- expect(new Future(() {
- testTearDownRun = true;
- }), completes);
- });
-
- expect(groupTearDownRun, isFalse);
- expect(testTearDownRun, isFalse);
- });
- });
- });
-
- test("blocks further tests on in-band async", () {
- return expectTestsPass(() {
+ var test1Run = false;
var tearDownRun = false;
test("test 1", () {
- addTearDown(() async {
+ addTearDown(() {
+ expect(test1Run, isTrue);
expect(tearDownRun, isFalse);
- await pumpEventQueue();
tearDownRun = true;
});
+
+ expect(tearDownRun, isFalse);
+ test1Run = true;
});
test("test 2", () {
@@ -328,74 +34,768 @@
});
});
- test("blocks further tests on out-of-band async", () {
+ test("multiples run in reverse order", () {
return expectTestsPass(() {
- var tearDownRun = false;
+ var tearDown1Run = false;
+ var tearDown2Run = false;
+ var tearDown3Run = false;
+
test("test 1", () {
- addTearDown(() async {
- expect(tearDownRun, isFalse);
- expect(
- pumpEventQueue().then((_) {
- tearDownRun = true;
- }),
- completes);
+ addTearDown(() {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isTrue);
+ expect(tearDown3Run, isTrue);
+ tearDown1Run = true;
+ });
+
+ addTearDown(() {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isTrue);
+ tearDown2Run = true;
+ });
+
+ addTearDown(() {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+ tearDown3Run = true;
+ });
+
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+ });
+
+ test("test 2", () {
+ expect(tearDown1Run, isTrue);
+ expect(tearDown2Run, isTrue);
+ expect(tearDown3Run, isTrue);
+ });
+ });
+ });
+
+ test("can be called in addTearDown", () {
+ return expectTestsPass(() {
+ var tearDown2Run = false;
+ var tearDown3Run = false;
+
+ test("test 1", () {
+ addTearDown(() {
+ expect(tearDown2Run, isTrue);
+ expect(tearDown3Run, isFalse);
+ tearDown3Run = true;
+ });
+
+ addTearDown(() {
+ addTearDown(() {
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+ tearDown2Run = true;
+ });
});
});
- test("after", () {
- expect(tearDownRun, isTrue);
+ test("test 2", () {
+ expect(tearDown2Run, isTrue);
+ expect(tearDown3Run, isTrue);
});
});
});
+
+ test("can be called in tearDown", () {
+ return expectTestsPass(() {
+ var tearDown2Run = false;
+ var tearDown3Run = false;
+
+ tearDown(() {
+ expect(tearDown2Run, isTrue);
+ expect(tearDown3Run, isFalse);
+ tearDown3Run = true;
+ });
+
+ tearDown(() {
+ tearDown2Run = false;
+ tearDown3Run = false;
+
+ addTearDown(() {
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+ tearDown2Run = true;
+ });
+ });
+
+ test("test 1", () {});
+
+ test("test 2", () {
+ expect(tearDown2Run, isTrue);
+ expect(tearDown3Run, isTrue);
+ });
+ });
+ });
+
+ test("runs before a normal tearDown", () {
+ return expectTestsPass(() {
+ var groupTearDownRun = false;
+ var testTearDownRun = false;
+ group("group", () {
+ tearDown(() {
+ expect(testTearDownRun, isTrue);
+ expect(groupTearDownRun, isFalse);
+ groupTearDownRun = true;
+ });
+
+ test("test 1", () {
+ addTearDown(() {
+ expect(groupTearDownRun, isFalse);
+ expect(testTearDownRun, isFalse);
+ testTearDownRun = true;
+ });
+
+ expect(groupTearDownRun, isFalse);
+ expect(testTearDownRun, isFalse);
+ });
+ });
+
+ test("test 2", () {
+ expect(groupTearDownRun, isTrue);
+ expect(testTearDownRun, isTrue);
+ });
+ });
+ });
+
+ test("runs in the same error zone as the test", () {
+ return expectTestsPass(() {
+ test("test", () {
+ var future = new Future.error("oh no");
+ expect(future, throwsA("oh no"));
+
+ addTearDown(() {
+ // If the tear-down is in a different error zone than the test, the
+ // error will try to cross the zone boundary and get top-leveled.
+ expect(future, throwsA("oh no"));
+ });
+ });
+ });
+ });
+
+ group("asynchronously", () {
+ test("blocks additional test tearDowns on in-band async", () {
+ return expectTestsPass(() {
+ var tearDown1Run = false;
+ var tearDown2Run = false;
+ var tearDown3Run = false;
+ test("test", () {
+ addTearDown(() async {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isTrue);
+ expect(tearDown3Run, isTrue);
+ await pumpEventQueue();
+ tearDown1Run = true;
+ });
+
+ addTearDown(() async {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isTrue);
+ await pumpEventQueue();
+ tearDown2Run = true;
+ });
+
+ addTearDown(() async {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+ await pumpEventQueue();
+ tearDown3Run = true;
+ });
+
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+ });
+ });
+ });
+
+ test("doesn't block additional test tearDowns on out-of-band async", () {
+ return expectTestsPass(() {
+ var tearDown1Run = false;
+ var tearDown2Run = false;
+ var tearDown3Run = false;
+ test("test", () {
+ addTearDown(() {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+
+ expect(new Future(() {
+ tearDown1Run = true;
+ }), completes);
+ });
+
+ addTearDown(() {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+
+ expect(new Future(() {
+ tearDown2Run = true;
+ }), completes);
+ });
+
+ addTearDown(() {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+
+ expect(new Future(() {
+ tearDown3Run = true;
+ }), completes);
+ });
+
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+ });
+ });
+ });
+
+ test("blocks additional group tearDowns on in-band async", () {
+ return expectTestsPass(() {
+ var groupTearDownRun = false;
+ var testTearDownRun = false;
+ tearDown(() async {
+ expect(groupTearDownRun, isFalse);
+ expect(testTearDownRun, isTrue);
+ await pumpEventQueue();
+ groupTearDownRun = true;
+ });
+
+ test("test", () {
+ addTearDown(() async {
+ expect(groupTearDownRun, isFalse);
+ expect(testTearDownRun, isFalse);
+ await pumpEventQueue();
+ testTearDownRun = true;
+ });
+
+ expect(groupTearDownRun, isFalse);
+ expect(testTearDownRun, isFalse);
+ });
+ });
+ });
+
+ test("doesn't block additional group tearDowns on out-of-band async", () {
+ return expectTestsPass(() {
+ var groupTearDownRun = false;
+ var testTearDownRun = false;
+ tearDown(() {
+ expect(groupTearDownRun, isFalse);
+ expect(testTearDownRun, isFalse);
+
+ expect(new Future(() {
+ groupTearDownRun = true;
+ }), completes);
+ });
+
+ test("test", () {
+ addTearDown(() {
+ expect(groupTearDownRun, isFalse);
+ expect(testTearDownRun, isFalse);
+
+ expect(new Future(() {
+ testTearDownRun = true;
+ }), completes);
+ });
+
+ expect(groupTearDownRun, isFalse);
+ expect(testTearDownRun, isFalse);
+ });
+ });
+ });
+
+ test("blocks further tests on in-band async", () {
+ return expectTestsPass(() {
+ var tearDownRun = false;
+ test("test 1", () {
+ addTearDown(() async {
+ expect(tearDownRun, isFalse);
+ await pumpEventQueue();
+ tearDownRun = true;
+ });
+ });
+
+ test("test 2", () {
+ expect(tearDownRun, isTrue);
+ });
+ });
+ });
+
+ test("blocks further tests on out-of-band async", () {
+ return expectTestsPass(() {
+ var tearDownRun = false;
+ test("test 1", () {
+ addTearDown(() async {
+ expect(tearDownRun, isFalse);
+ expect(
+ pumpEventQueue().then((_) {
+ tearDownRun = true;
+ }),
+ completes);
+ });
+ });
+
+ test("after", () {
+ expect(tearDownRun, isTrue);
+ });
+ });
+ });
+ });
+
+ group("with an error", () {
+ test("reports the error", () async {
+ var engine = declareEngine(() {
+ test("test", () {
+ addTearDown(() => throw new TestFailure("fail"));
+ });
+ });
+
+ var queue = new StreamQueue(engine.onTestStarted);
+ var liveTestFuture = queue.next;
+
+ expect(await engine.run(), isFalse);
+
+ var liveTest = await liveTestFuture;
+ expect(liveTest.test.name, equals("test"));
+ expectTestFailed(liveTest, "fail");
+ });
+
+ test("runs further test tearDowns", () async {
+ // Declare this in the outer test so if it doesn't run, the outer test
+ // will fail.
+ var shouldRun = expectAsync0(() {});
+
+ var engine = declareEngine(() {
+ test("test", () {
+ addTearDown(() => throw "error");
+ addTearDown(shouldRun);
+ });
+ });
+
+ expect(await engine.run(), isFalse);
+ });
+
+ test("runs further group tearDowns", () async {
+ // Declare this in the outer test so if it doesn't run, the outer test
+ // will fail.
+ var shouldRun = expectAsync0(() {});
+
+ var engine = declareEngine(() {
+ tearDown(shouldRun);
+
+ test("test", () {
+ addTearDown(() => throw "error");
+ });
+ });
+
+ expect(await engine.run(), isFalse);
+ });
+ });
});
- group("with an error", () {
- test("reports the error", () async {
- var engine = declareEngine(() {
- test("test", () {
- addTearDown(() => throw new TestFailure("fail"));
+ group("in setUpAll()", () {
+ test("runs after all tests", () async {
+ var test1Run = false;
+ var test2Run = false;
+ var tearDownRun = false;
+ await expectTestsPass(() {
+ setUpAll(() {
+ addTearDown(() {
+ expect(test1Run, isTrue);
+ expect(test2Run, isTrue);
+ expect(tearDownRun, isFalse);
+ tearDownRun = true;
+ });
+ });
+
+ test("test 1", () {
+ test1Run = true;
+ expect(tearDownRun, isFalse);
+ });
+
+ test("test 2", () {
+ test2Run = true;
+ expect(tearDownRun, isFalse);
});
});
- var queue = new StreamQueue(engine.onTestStarted);
- var liveTestFuture = queue.next;
-
- expect(await engine.run(), isFalse);
-
- var liveTest = await liveTestFuture;
- expect(liveTest.test.name, equals("test"));
- expectTestFailed(liveTest, "fail");
+ expect(test1Run, isTrue);
+ expect(test2Run, isTrue);
+ expect(tearDownRun, isTrue);
});
- test("runs further test tearDowns", () async {
- // Declare this in the outer test so if it doesn't run, the outer test
- // will fail.
- var shouldRun = expectAsync0(() {});
+ test("multiples run in reverse order", () async {
+ var tearDown1Run = false;
+ var tearDown2Run = false;
+ var tearDown3Run = false;
+ await expectTestsPass(() {
+ setUpAll(() {
+ addTearDown(() {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isTrue);
+ expect(tearDown3Run, isTrue);
+ tearDown1Run = true;
+ });
- var engine = declareEngine(() {
+ addTearDown(() {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isTrue);
+ tearDown2Run = true;
+ });
+
+ addTearDown(() {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+ tearDown3Run = true;
+ });
+
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+ });
+
test("test", () {
- addTearDown(() => throw "error");
- addTearDown(shouldRun);
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
});
});
- expect(await engine.run(), isFalse);
+ expect(tearDown1Run, isTrue);
+ expect(tearDown2Run, isTrue);
+ expect(tearDown3Run, isTrue);
});
- test("runs further group tearDowns", () async {
- // Declare this in the outer test so if it doesn't run, the outer test
- // will fail.
- var shouldRun = expectAsync0(() {});
+ test("can be called in addTearDown", () async {
+ var tearDown2Run = false;
+ var tearDown3Run = false;
+ await expectTestsPass(() {
+ setUpAll(() {
+ addTearDown(() {
+ expect(tearDown2Run, isTrue);
+ expect(tearDown3Run, isFalse);
+ tearDown3Run = true;
+ });
- var engine = declareEngine(() {
- tearDown(shouldRun);
+ addTearDown(() {
+ addTearDown(() {
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+ tearDown2Run = true;
+ });
+ });
+ });
test("test", () {
- addTearDown(() => throw "error");
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
});
});
- expect(await engine.run(), isFalse);
+ expect(tearDown2Run, isTrue);
+ expect(tearDown3Run, isTrue);
+ });
+
+ test("can be called in tearDownAll", () async {
+ var tearDown2Run = false;
+ var tearDown3Run = false;
+ await expectTestsPass(() {
+ tearDownAll(() {
+ expect(tearDown2Run, isTrue);
+ expect(tearDown3Run, isFalse);
+ tearDown3Run = true;
+ });
+
+ tearDownAll(() {
+ tearDown2Run = false;
+ tearDown3Run = false;
+
+ addTearDown(() {
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+ tearDown2Run = true;
+ });
+ });
+
+ test("test", () {});
+ });
+
+ expect(tearDown2Run, isTrue);
+ expect(tearDown3Run, isTrue);
+ });
+
+ test("runs before a normal tearDownAll", () async {
+ var groupTearDownRun = false;
+ var testTearDownRun = false;
+ await expectTestsPass(() {
+ tearDownAll(() {
+ expect(testTearDownRun, isTrue);
+ expect(groupTearDownRun, isFalse);
+ groupTearDownRun = true;
+ });
+
+ setUpAll(() {
+ addTearDown(() {
+ expect(groupTearDownRun, isFalse);
+ expect(testTearDownRun, isFalse);
+ testTearDownRun = true;
+ });
+ });
+
+ test("test", () {
+ expect(groupTearDownRun, isFalse);
+ expect(testTearDownRun, isFalse);
+ });
+ });
+
+ expect(groupTearDownRun, isTrue);
+ expect(testTearDownRun, isTrue);
+ });
+
+ test("runs in the same error zone as the setUpAll", () async {
+ return expectTestsPass(() {
+ setUpAll(() {
+ var future = new Future.error("oh no");
+ expect(future, throwsA("oh no"));
+
+ addTearDown(() {
+ // If the tear-down is in a different error zone than the setUpAll,
+ // the error will try to cross the zone boundary and get
+ // top-leveled.
+ expect(future, throwsA("oh no"));
+ });
+ });
+
+ test("test", () {});
+ });
+ });
+
+ group("asynchronously", () {
+ test("blocks additional tearDowns on in-band async", () async {
+ var tearDown1Run = false;
+ var tearDown2Run = false;
+ var tearDown3Run = false;
+ await expectTestsPass(() {
+ setUpAll(() {
+ addTearDown(() async {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isTrue);
+ expect(tearDown3Run, isTrue);
+ await pumpEventQueue();
+ tearDown1Run = true;
+ });
+
+ addTearDown(() async {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isTrue);
+ await pumpEventQueue();
+ tearDown2Run = true;
+ });
+
+ addTearDown(() async {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+ await pumpEventQueue();
+ tearDown3Run = true;
+ });
+ });
+
+ test("test", () {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+ });
+ });
+
+ expect(tearDown1Run, isTrue);
+ expect(tearDown2Run, isTrue);
+ expect(tearDown3Run, isTrue);
+ });
+
+ test("doesn't block additional tearDowns on out-of-band async", () async {
+ var tearDown1Run = false;
+ var tearDown2Run = false;
+ var tearDown3Run = false;
+ await expectTestsPass(() {
+ setUpAll(() {
+ addTearDown(() {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+
+ expect(new Future(() {
+ tearDown1Run = true;
+ }), completes);
+ });
+
+ addTearDown(() {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+
+ expect(new Future(() {
+ tearDown2Run = true;
+ }), completes);
+ });
+
+ addTearDown(() {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+
+ expect(new Future(() {
+ tearDown3Run = true;
+ }), completes);
+ });
+ });
+
+ test("test", () {
+ expect(tearDown1Run, isFalse);
+ expect(tearDown2Run, isFalse);
+ expect(tearDown3Run, isFalse);
+ });
+ });
+
+ expect(tearDown1Run, isTrue);
+ expect(tearDown2Run, isTrue);
+ expect(tearDown3Run, isTrue);
+ });
+
+ test("blocks additional tearDownAlls on in-band async", () async {
+ var groupTearDownRun = false;
+ var testTearDownRun = false;
+ await expectTestsPass(() {
+ tearDownAll(() async {
+ expect(groupTearDownRun, isFalse);
+ expect(testTearDownRun, isTrue);
+ await pumpEventQueue();
+ groupTearDownRun = true;
+ });
+
+ setUpAll(() {
+ addTearDown(() async {
+ expect(groupTearDownRun, isFalse);
+ expect(testTearDownRun, isFalse);
+ await pumpEventQueue();
+ testTearDownRun = true;
+ });
+ });
+
+ test("test", () {
+ expect(groupTearDownRun, isFalse);
+ expect(testTearDownRun, isFalse);
+ });
+ });
+
+ expect(groupTearDownRun, isTrue);
+ expect(testTearDownRun, isTrue);
+ });
+
+ test("doesn't block additional tearDownAlls on out-of-band async",
+ () async {
+ var groupTearDownRun = false;
+ var testTearDownRun = false;
+ await expectTestsPass(() {
+ tearDownAll(() {
+ expect(groupTearDownRun, isFalse);
+ expect(testTearDownRun, isFalse);
+
+ expect(new Future(() {
+ groupTearDownRun = true;
+ }), completes);
+ });
+
+ setUpAll(() {
+ addTearDown(() {
+ expect(groupTearDownRun, isFalse);
+ expect(testTearDownRun, isFalse);
+
+ expect(new Future(() {
+ testTearDownRun = true;
+ }), completes);
+ });
+ });
+
+ test("test", () {
+ expect(groupTearDownRun, isFalse);
+ expect(testTearDownRun, isFalse);
+ });
+ });
+
+ expect(groupTearDownRun, isTrue);
+ expect(testTearDownRun, isTrue);
+ });
+ });
+
+ group("with an error", () {
+ test("reports the error", () async {
+ var engine = declareEngine(() {
+ setUpAll(() {
+ addTearDown(() => throw new TestFailure("fail"));
+ });
+
+ test("test", () {});
+ });
+
+ var queue = new StreamQueue(engine.onTestStarted);
+ queue.skip(2);
+ var liveTestFuture = queue.next;
+
+ expect(await engine.run(), isFalse);
+
+ var liveTest = await liveTestFuture;
+ expect(liveTest.test.name, equals("(tearDownAll)"));
+ expectTestFailed(liveTest, "fail");
+ });
+
+ test("runs further tearDowns", () async {
+ // Declare this in the outer test so if it doesn't run, the outer test
+ // will fail.
+ var shouldRun = expectAsync0(() {});
+
+ var engine = declareEngine(() {
+ setUpAll(() {
+ addTearDown(() => throw "error");
+ addTearDown(shouldRun);
+ });
+
+ test("test", () {});
+ });
+
+ expect(await engine.run(), isFalse);
+ });
+
+ test("runs further tearDownAlls", () async {
+ // Declare this in the outer test so if it doesn't run, the outer test
+ // will fail.
+ var shouldRun = expectAsync0(() {});
+
+ var engine = declareEngine(() {
+ tearDownAll(shouldRun);
+
+ setUpAll(() {
+ addTearDown(() => throw "error");
+ });
+
+ test("test", () {});
+ });
+
+ expect(await engine.run(), isFalse);
+ });
});
});
}
diff --git a/test/runner/json_reporter_test.dart b/test/runner/json_reporter_test.dart
index e6cb35a..daa936d 100644
--- a/test/runner/json_reporter_test.dart
+++ b/test/runner/json_reporter_test.dart
@@ -447,6 +447,8 @@
_testDone(3, hidden: true),
_testStart(4, "success", line: 9, column: 9),
_testDone(4),
+ _testStart(5, "(tearDownAll)"),
+ _testDone(5, hidden: true),
_done()
]);
});