Fix async gap handling. (#84)
Fix an issue where an async gap at the end of a stack trace would not
get parsed correctly due to the trailing newline being `trim()`'d.
Add tests to cover this case.
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 @@
static List<Frame> _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 @@
/// The line used in the string representation of VM stack chains to represent
/// the gap between traces.
-const vmChainGap = '<asynchronous suspension>\n';
+final vmChainGap = RegExp(r'^<asynchronous suspension>\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 @@
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.<fn>.bar\n'
+ 'https://dart.dev/foo/baz.dart Foo.<fn>.bar\n'
+ 'https://dart.dev/foo/bang.dart 10:11 Foo.<fn>.bar\n'
+ 'https://dart.dev/foo/quux.dart Foo.<fn>.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 @@
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'
+ '<asynchronous suspension>\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'
+ '<asynchronous suspension>\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', () {