blob: d8d7d397010487cc5f96dd43f3eb543edfc0dd2b [file] [log] [blame]
// 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, runZonedGuarded;
import 'dart:io' show exit, stderr;
import 'dart:isolate' show Capability, Isolate, ReceivePort;
import 'log.dart' show StdoutLogger;
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) {
StdoutLogger().logUncaughtError(error, stackTrace);
if (!completer.isCompleted) {
completer.completeError(error, stackTrace);
} else if (handleLateError != null) {
handleLateError(error, stackTrace);
} else {
String errorString = "error.toString() failed.";
try {
errorString = "$error";
} catch (_) {
// Ignored.
}
stderr
.write("$errorString\n" + (stackTrace == null ? "" : "$stackTrace"));
stderr.flush();
exit(255);
}
}
ZoneSpecification specification = new ZoneSpecification(print: printWrapper);
ReceivePort errorPort = new ReceivePort();
Future errorFuture = errorPort.listen((_errors) {
List errors = _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.setErrorsFatal(false);
Isolate.current.addErrorListener(errorPort.sendPort);
return acknowledgeControlMessages(Isolate.current).then((_) {
runZonedGuarded(
() => new Future(f).then(completer.complete),
handleUncaughtError,
zoneSpecification: specification,
);
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));
}
}