Index: pkg/testing/lib/src/zone_helper.dart |
diff --git a/pkg/testing/lib/src/zone_helper.dart b/pkg/testing/lib/src/zone_helper.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6f8364a5aef7e6c7ba4b517752f63ba79c70f7f5 |
--- /dev/null |
+++ b/pkg/testing/lib/src/zone_helper.dart |
@@ -0,0 +1,89 @@ |
+// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE.md file. |
+ |
+/// Helper functions for running code in a Zone. |
+library testing.zone_helper; |
+ |
+import 'dart:async' show |
+ Completer, |
+ Future, |
+ ZoneSpecification, |
+ runZoned; |
+ |
+import 'dart:isolate' show |
+ Capability, |
+ Isolate, |
+ ReceivePort; |
+ |
+import 'log.dart' show |
+ logUncaughtError; |
+ |
+Future runGuarded( |
+ Future f(), |
+ {void printLineOnStdout(line), |
+ void handleLateError(error, StackTrace stackTrace)}) { |
+ |
+ var printWrapper; |
+ if (printLineOnStdout != null) { |
+ printWrapper = (_1, _2, _3, String line) { |
+ printLineOnStdout(line); |
+ }; |
+ } |
+ |
+ Completer completer = new Completer(); |
+ |
+ handleUncaughtError(error, StackTrace stackTrace) { |
+ logUncaughtError(error, stackTrace); |
+ if (!completer.isCompleted) { |
+ completer.completeError(error, stackTrace); |
+ } else if (handleLateError != null) { |
+ handleLateError(error, stackTrace); |
+ } else { |
+ // Delegate to parent. |
+ throw error; |
+ } |
+ } |
+ |
+ ZoneSpecification specification = new ZoneSpecification(print: printWrapper); |
+ |
+ ReceivePort errorPort = new ReceivePort(); |
+ Future errorFuture = errorPort.listen((List errors) { |
+ Isolate.current.removeErrorListener(errorPort.sendPort); |
+ errorPort.close(); |
+ var error = errors[0]; |
+ var stackTrace = errors[1]; |
+ if (stackTrace != null) { |
+ stackTrace = new StackTrace.fromString(stackTrace); |
+ } |
+ handleUncaughtError(error, stackTrace); |
+ }).asFuture(); |
+ |
+ Isolate.current.addErrorListener(errorPort.sendPort); |
+ return acknowledgeControlMessages(Isolate.current).then((_) { |
+ runZoned( |
+ () => new Future(f).then(completer.complete), |
+ zoneSpecification: specification, |
+ onError: handleUncaughtError); |
+ |
+ return completer.future.whenComplete(() { |
+ errorPort.close(); |
+ Isolate.current.removeErrorListener(errorPort.sendPort); |
+ return acknowledgeControlMessages(Isolate.current) |
+ .then((_) => errorFuture); |
+ }); |
+ }); |
+} |
+ |
+/// Ping [isolate] to ensure control messages have been delivered. Control |
+/// messages are things like [Isolate.addErrorListener] and |
+/// [Isolate.addOnExitListener]. |
+Future acknowledgeControlMessages(Isolate isolate, {Capability resume}) { |
+ ReceivePort ping = new ReceivePort(); |
+ Isolate.current.ping(ping.sendPort); |
+ if (resume == null) { |
+ return ping.first; |
+ } else { |
+ return ping.first.then((_) => isolate.resume(resume)); |
+ } |
+} |