Index: lib/src/executable.dart |
diff --git a/lib/src/executable.dart b/lib/src/executable.dart |
index fe403b2a1f26423aed50fcc52059e55c28de2aba..b70a5066a2a6c3824fb12ff26cdb206c2dbe1154 100644 |
--- a/lib/src/executable.dart |
+++ b/lib/src/executable.dart |
@@ -25,6 +25,16 @@ import 'utils.dart'; |
/// The argument parser used to parse the executable arguments. |
final _parser = new ArgParser(allowTrailingOptions: true); |
+/// A merged stream of all signals that tell the test runner to shut down |
+/// gracefully. |
+/// |
+/// Signals will only be captured as long as this has an active subscription. |
+/// Otherwise, they'll be handled by Dart's default signal handler, which |
+/// terminates the program immediately. |
+final _signals = mergeStreams([ |
+ ProcessSignal.SIGTERM.watch(), ProcessSignal.SIGINT.watch() |
+]); |
+ |
void main(List<String> args) { |
_parser.addFlag("help", abbr: "h", negatable: false, |
help: "Shows this usage information."); |
@@ -65,6 +75,15 @@ void main(List<String> args) { |
var platforms = options["platform"].map(TestPlatform.find); |
var loader = new Loader(platforms, |
packageRoot: options["package-root"], color: color); |
+ |
+ var signalSubscription; |
+ var closed = false; |
+ signalSubscription = _signals.listen((_) { |
+ signalSubscription.cancel(); |
+ closed = true; |
+ loader.close(); |
+ }); |
+ |
new Future.sync(() { |
var paths = options.rest; |
if (paths.isEmpty) { |
@@ -82,6 +101,7 @@ void main(List<String> args) { |
throw new LoadException(path, 'Does not exist.'); |
})); |
}).then((suites) { |
+ if (closed) return null; |
suites = flatten(suites); |
var pattern; |
@@ -116,10 +136,30 @@ void main(List<String> args) { |
} |
var reporter = new CompactReporter(flatten(suites), color: color); |
+ |
+ // Override the signal handler to close [reporter]. [loader] will still be |
+ // closed in the [whenComplete] below. |
+ signalSubscription.onData((_) { |
+ signalSubscription.cancel(); |
+ closed = true; |
+ |
+ // Wait a bit to print this message, since printing it eagerly looks weird |
+ // if the tests then finish immediately. |
+ var timer = new Timer(new Duration(seconds: 1), () { |
+ print("Waiting for current test to finish."); |
+ print("Press Control-C again to terminate immediately."); |
+ }); |
+ |
+ reporter.close().then((_) => timer.cancel()); |
+ }); |
+ |
return reporter.run().then((success) { |
exitCode = success ? 0 : 1; |
- }).whenComplete(() => reporter.close()); |
- }).catchError((error, stackTrace) { |
+ }).whenComplete(() { |
+ signalSubscription.cancel(); |
+ return reporter.close(); |
+ }); |
+ }).whenComplete(signalSubscription.cancel).catchError((error, stackTrace) { |
if (error is LoadException) { |
stderr.writeln(error.toString(color: color)); |