diff --git a/CHANGELOG.md b/CHANGELOG.md index c95d405..a738145 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## 1.10.0-nullsafety.3-dev +* Fix bug parsing asynchronous suspension gap markers at the end of stack + traces. + ## 1.10.0-nullsafety.2 * Forward fix for a change in SDK type promotion behavior. diff --git a/lib/src/trace.dart b/lib/src/trace.dart index dc7fd5d..7e30b95 100644 --- a/lib/src/trace.dart +++ b/lib/src/trace.dart @@ -146,7 +146,11 @@ class Trace implements StackTrace { static List _parseVM(String trace) { // Ignore [vmChainGap]. This matches the behavior of // `Chain.parse().toTrace()`. - var lines = trace.trim().replaceAll(vmChainGap, '').split('\n'); + var lines = trace + .trim() + .replaceAll(vmChainGap, '') + .split('\n') + .where((line) => line.isNotEmpty); var frames = lines .take(lines.length - 1) .map((line) => Frame.parseVM(line)) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 838a093..0dd1755 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -8,7 +8,7 @@ const chainGap = '===== asynchronous gap ===========================\n'; /// The line used in the string representation of VM stack chains to represent /// the gap between traces. -const vmChainGap = '\n'; +final vmChainGap = RegExp(r'^\n?$', multiLine: true); // TODO(nweiz): When cross-platform imports work, use them to set this. /// Whether we're running in a JS context. diff --git a/test/trace_test.dart b/test/trace_test.dart index a10c69d..ea48e03 100644 --- a/test/trace_test.dart +++ b/test/trace_test.dart @@ -249,6 +249,25 @@ void main() { equals(Uri.parse('https://dart.dev/foo/quux.dart'))); }); + test('parses a package:stack_trace stack chain with end gap correctly', () { + var trace = + Trace.parse('https://dart.dev/foo/bar.dart 10:11 Foo..bar\n' + 'https://dart.dev/foo/baz.dart Foo..bar\n' + 'https://dart.dev/foo/bang.dart 10:11 Foo..bar\n' + 'https://dart.dev/foo/quux.dart Foo..bar' + '===== asynchronous gap ===========================\n'); + + expect(trace.frames.length, 4); + expect(trace.frames[0].uri, + equals(Uri.parse('https://dart.dev/foo/bar.dart'))); + expect(trace.frames[1].uri, + equals(Uri.parse('https://dart.dev/foo/baz.dart'))); + expect(trace.frames[2].uri, + equals(Uri.parse('https://dart.dev/foo/bang.dart'))); + expect(trace.frames[3].uri, + equals(Uri.parse('https://dart.dev/foo/quux.dart'))); + }); + test('parses a real package:stack_trace stack trace correctly', () { var traceString = Trace.current().toString(); expect(Trace.parse(traceString).toString(), equals(traceString)); @@ -259,6 +278,28 @@ void main() { expect(trace.frames, isEmpty); expect(trace.toString(), equals('')); }); + + test('parses trace with async gap correctly', () { + var trace = Trace.parse('#0 bop (file:///pull.dart:42:23)\n' + '\n' + '#1 twist (dart:the/future.dart:0:2)\n' + '#2 main (dart:my/file.dart:4:6)\n'); + + expect(trace.frames.length, 3); + expect(trace.frames[0].uri, equals(Uri.parse('file:///pull.dart'))); + expect(trace.frames[1].uri, equals(Uri.parse('dart:the/future.dart'))); + expect(trace.frames[2].uri, equals(Uri.parse('dart:my/file.dart'))); + }); + + test('parses trace with async gap at end correctly', () { + var trace = Trace.parse('#0 bop (file:///pull.dart:42:23)\n' + '#1 twist (dart:the/future.dart:0:2)\n' + '\n'); + + expect(trace.frames.length, 2); + expect(trace.frames[0].uri, equals(Uri.parse('file:///pull.dart'))); + expect(trace.frames[1].uri, equals(Uri.parse('dart:the/future.dart'))); + }); }); test('.toString() nicely formats the stack trace', () {