blob: 7b1013111dd524f3494de55486cc3225c00d41d4 [file] [log] [blame]
// Copyright (c) 2013, 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 file.
import 'package:expect/expect.dart';
import 'package:async_helper/async_helper.dart';
import 'dart:async';
/**
* We represent the current stack trace by an integer. From time to time we
* increment the variable. This corresponds to a new stack trace.
*/
int stackTrace = 0;
List restoredStackTrace = [];
List events = [];
ZoneCallback<R> debugZoneRegisterCallback<R>(
Zone self, ZoneDelegate parent, Zone origin, R f()) {
List savedTrace = [stackTrace]..addAll(restoredStackTrace);
return parent.registerCallback(origin, () {
restoredStackTrace = savedTrace;
return f();
});
}
ZoneUnaryCallback<R, T> debugZoneRegisterUnaryCallback<R, T>(
Zone self, ZoneDelegate parent, Zone origin, R f(T arg)) {
List savedTrace = [stackTrace]..addAll(restoredStackTrace);
return parent.registerUnaryCallback(origin, (arg) {
restoredStackTrace = savedTrace;
return f(arg);
});
}
T debugZoneRun<T>(Zone self, ZoneDelegate parent, Zone origin, T f()) {
stackTrace++;
restoredStackTrace = [];
return parent.run(origin, f);
}
R debugZoneRunUnary<R, T>(
Zone self, ZoneDelegate parent, Zone origin, R f(T arg), T arg) {
stackTrace++;
restoredStackTrace = [];
return parent.runUnary(origin, f, arg);
}
late List expectedDebugTrace;
void debugUncaughtHandler(
Zone self,
ZoneDelegate parent,
Zone origin,
error,
StackTrace? stackTrace) {
events.add("handling uncaught error $error");
Expect.listEquals(expectedDebugTrace, restoredStackTrace);
// Suppress the error and don't propagate to parent.
}
const DEBUG_SPECIFICATION = const ZoneSpecification(
registerCallback: debugZoneRegisterCallback,
registerUnaryCallback: debugZoneRegisterUnaryCallback,
run: debugZoneRun,
runUnary: debugZoneRunUnary,
handleUncaughtError: debugUncaughtHandler);
main() {
Completer done = new Completer();
// runGuarded calls run, captures the synchronous error (if any) and
// gives that one to handleUncaughtError.
Expect.identical(Zone.root, Zone.current);
Zone forked;
forked = Zone.current.fork(specification: DEBUG_SPECIFICATION);
asyncStart();
int openTests = 0;
openTests++;
forked.run(() {
int forkTrace = stackTrace;
scheduleMicrotask(() {
int scheduleMicrotaskTrace = stackTrace;
scheduleMicrotask(() {
expectedDebugTrace = [scheduleMicrotaskTrace, forkTrace];
openTests--;
if (openTests == 0) {
done.complete();
}
throw "foo";
});
expectedDebugTrace = [forkTrace];
throw "bar";
});
});
Expect.listEquals([], restoredStackTrace);
Zone forked2 = forked.fork();
Zone forked3 = forked2.fork();
int fork2Trace;
int fork3Trace;
var f2;
var globalTrace = stackTrace;
var f = forked3.bindCallback(() {
Expect.identical(forked3, Zone.current);
fork2Trace = stackTrace;
f2 = forked2.bindCallback(() {
Expect.identical(forked2, Zone.current);
Expect.listEquals([fork2Trace, globalTrace], restoredStackTrace);
fork3Trace = stackTrace;
openTests--;
if (openTests == 0) {
done.complete();
}
scheduleMicrotask(() {
expectedDebugTrace = [fork3Trace, fork2Trace, globalTrace];
throw "gee";
});
});
});
openTests++;
f();
f2();
done.future.whenComplete(() {
// We don't really care for the order.
events.sort();
Expect.listEquals([
"handling uncaught error bar",
"handling uncaught error foo",
"handling uncaught error gee"
], events);
asyncEnd();
});
}