| // Copyright (c) 2018, 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 'dart:async'; | |
| import 'package:stack_trace/stack_trace.dart'; | |
| import 'invoker.dart'; | |
| import 'stack_trace_mapper.dart'; | |
| /// The key used to look up [StackTraceFormatter.current] in a zone. | |
| final _currentKey = Object(); | |
| /// A class that tracks how to format a stack trace according to the user's | |
| /// configuration. | |
| /// | |
| /// This can convert JavaScript stack traces to Dart using source maps, and fold | |
| /// irrelevant frames out of the stack trace. | |
| final class StackTraceFormatter { | |
| /// A class that converts [trace] into a Dart stack trace, or `null` to use it | |
| /// as-is. | |
| StackTraceMapper? _mapper; | |
| /// The set of packages to fold when producing terse [Chain]s. | |
| var _except = {'matcher', 'stream_channel', 'test', 'test_api'}; | |
| /// If non-empty, all packages not in this list will be folded when producing | |
| /// terse [Chain]s. | |
| var _only = <String>{}; | |
| /// Returns the current manager, or `null` if this isn't called within a call | |
| /// to [asCurrent]. | |
| static StackTraceFormatter? get current => | |
| Zone.current[_currentKey] as StackTraceFormatter?; | |
| /// Runs [body] with this as [StackTraceFormatter.current]. | |
| /// | |
| /// This is zone-scoped, so this will be the current configuration in any | |
| /// asynchronous callbacks transitively created by [body]. | |
| T asCurrent<T>(T Function() body) => | |
| runZoned(body, zoneValues: {_currentKey: this}); | |
| /// Configure how stack traces are formatted. | |
| /// | |
| /// The [mapper] is used to convert JavaScript traces into Dart traces. The | |
| /// [except] set indicates packages whose frames should be folded away. If | |
| /// [only] is non-empty, it indicates packages whose frames should *not* be | |
| /// folded away. | |
| void configure({ | |
| StackTraceMapper? mapper, | |
| Set<String>? except, | |
| Set<String>? only, | |
| }) { | |
| if (mapper != null) _mapper = mapper; | |
| if (except != null) _except = except; | |
| if (only != null) _only = only; | |
| } | |
| /// Converts [stackTrace] to a [Chain] and formats it according to the user's | |
| /// preferences. | |
| /// | |
| /// If [verbose] is `true`, this doesn't fold out irrelevant stack frames. It | |
| /// defaults to the current test's [Metadata.verboseTrace] configuration, or | |
| /// `false` if there is no current test. | |
| Chain formatStackTrace(StackTrace stackTrace, {bool? verbose}) { | |
| verbose ??= Invoker.current?.liveTest.test.metadata.verboseTrace ?? false; | |
| var chain = Chain.forTrace( | |
| _mapper?.mapStackTrace(stackTrace) ?? stackTrace, | |
| ); | |
| if (verbose) return chain; | |
| return chain.foldFrames((frame) { | |
| if (_only.isNotEmpty) return !_only.contains(frame.package); | |
| return _except.contains(frame.package); | |
| }, terse: true); | |
| } | |
| } |