Index: lib/src/runner/engine.dart |
diff --git a/lib/src/runner/engine.dart b/lib/src/runner/engine.dart |
index 6bd51bfe726bfa0d875b6575a35ecce311629110..ed88544469073593e889b37943542cd38def2047 100644 |
--- a/lib/src/runner/engine.dart |
+++ b/lib/src/runner/engine.dart |
@@ -215,18 +215,35 @@ class Engine { |
return; |
} |
- for (var entry in group.entries) { |
- if (_closed) return; |
+ var setUpAllSucceeded = true; |
+ if (group.setUpAll != null) { |
+ var liveTest = group.setUpAll.load(suite); |
+ await _runLiveTest(liveTest, countSuccess: false); |
+ setUpAllSucceeded = liveTest.state.result == Result.success; |
+ } |
- if (entry is Group) { |
- await _runGroup(suite, entry); |
- } else if (entry.metadata.skip) { |
- await _runLiveTest(_skippedTest(suite, entry)); |
- } else { |
- var test = entry as Test; |
- await _runLiveTest(test.load(suite)); |
+ if (!_closed && setUpAllSucceeded) { |
+ for (var entry in group.entries) { |
+ if (_closed) return; |
+ |
+ if (entry is Group) { |
+ await _runGroup(suite, entry); |
+ } else if (entry.metadata.skip) { |
+ await _runLiveTest(_skippedTest(suite, entry)); |
+ } else { |
+ var test = entry as Test; |
+ await _runLiveTest(test.load(suite)); |
+ } |
} |
} |
+ |
+ // Even if we're closed or setUpAll failed, we want to run all the teardowns |
+ // to ensure that any state is properly cleaned up. |
+ if (group.tearDownAll != null) { |
+ var liveTest = group.tearDownAll.load(suite); |
+ await _runLiveTest(liveTest, countSuccess: false); |
+ if (_closed) await liveTest.close(); |
+ } |
} |
/// Returns a dummy [LiveTest] for a test or group marked as "skip". |
@@ -244,7 +261,10 @@ class Engine { |
} |
/// Runs [liveTest]. |
- Future _runLiveTest(LiveTest liveTest) async { |
+ /// |
+ /// If [countSuccess] is `true` (the default), the test is put into [passed] |
+ /// if it succeeds. Otherwise, it's removed from [liveTests] entirely. |
+ Future _runLiveTest(LiveTest liveTest, {bool countSuccess: true}) async { |
_liveTests.add(liveTest); |
_active.add(liveTest); |
@@ -270,8 +290,10 @@ class Engine { |
_failed.add(liveTest); |
} else if (liveTest.test.metadata.skip) { |
_skipped.add(liveTest); |
- } else { |
+ } else if (countSuccess) { |
_passed.add(liveTest); |
+ } else { |
+ _liveTests.remove(liveTest); |
} |
}); |
@@ -342,15 +364,19 @@ class Engine { |
/// the engine indicates that no more output should be emitted. |
Future close() async { |
_closed = true; |
- if (_closedBeforeDone == null) _closedBeforeDone = true; |
+ if (_closedBeforeDone != null) _closedBeforeDone = true; |
_suiteController.close(); |
// Close the running tests first so that we're sure to wait for them to |
// finish before we close their suites and cause them to become unloaded. |
var allLiveTests = liveTests.toSet()..addAll(_activeLoadTests); |
- await Future.wait(allLiveTests.map((liveTest) => liveTest.close())); |
- |
- var allSuites = allLiveTests.map((liveTest) => liveTest.suite).toSet(); |
- await Future.wait(allSuites.map((suite) => suite.close())); |
+ var futures = allLiveTests.map((liveTest) => liveTest.close()).toList(); |
+ |
+ // Closing the load pool will close the test suites as soon as their tests |
+ // are done. For browser suites this is effectively immediate since their |
+ // tests shut down as soon as they're closed, but for VM suites we may need |
+ // to wait for tearDowns or tearDownAlls to run. |
+ futures.add(_loadPool.close()); |
+ await Future.wait(futures, eagerError: true); |
} |
} |