// 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/async_helper.dart'; import 'package:expect/expect.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 debugZoneRegisterCallback( Zone self, ZoneDelegate parent, Zone origin, R f(), ) { List savedTrace = [stackTrace]..addAll(restoredStackTrace); return parent.registerCallback(origin, () { restoredStackTrace = savedTrace; return f(); }); } ZoneUnaryCallback debugZoneRegisterUnaryCallback( 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(Zone self, ZoneDelegate parent, Zone origin, T f()) { stackTrace++; restoredStackTrace = []; return parent.run(origin, f); } R debugZoneRunUnary( 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(); }); }