Skip to content

Commit 17b5dde

Browse files
Clement Skaucommit-bot@chromium.org
Clement Skau
authored andcommitted
[SDK] Adds tests for async stacktraces.
Bug: #37668 Change-Id: Id29704d086dbae066c8b34e347b75cd374b1ce2b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/121986 Commit-Queue: Clement Skau <[email protected]> Reviewed-by: Martin Kustermann <[email protected]>
1 parent 0f2787c commit 17b5dde

File tree

4 files changed

+290
-0
lines changed

4 files changed

+290
-0
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// VMOptions=--no-causal-async-stacks
6+
7+
import 'dart:async';
8+
9+
import 'utils.dart';
10+
11+
Future<void> main(List<String> args) async => doTestsNoCausal();
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// VMOptions=--causal-async-stacks
6+
7+
import 'dart:async';
8+
9+
import 'utils.dart';
10+
11+
Future<void> main(List<String> args) async => doTestsCausal();
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:async';
6+
import 'dart:io';
7+
import 'dart:math';
8+
import 'dart:typed_data';
9+
10+
import 'package:path/path.dart' as path;
11+
import 'package:expect/expect.dart';
12+
import 'package:expect/matchers_lite.dart';
13+
14+
Matcher startsWith(String expected) {
15+
return (Object actual) {
16+
if (actual is String) {
17+
Expect.equals(
18+
expected, actual.substring(0, min(expected.length, actual.length)));
19+
return;
20+
}
21+
Expect.fail('Expected String.');
22+
};
23+
}
24+
25+
void assertStack(Map expected, StackTrace stack_trace) {
26+
final List<String> frames = stack_trace.toString().split('\n');
27+
for (int i in expected.keys) {
28+
expect(frames[i], startsWith(expected[i]));
29+
}
30+
}
31+
32+
Future<void> doTest(Future f(), Map<int, String> expected_stack) async {
33+
// Caller catches exception.
34+
try {
35+
await f();
36+
Expect.fail('No exception thrown!');
37+
} on String catch (e, s) {
38+
assertStack(expected_stack, s);
39+
}
40+
41+
// Caller catches but a then is set.
42+
try {
43+
await f().then((e) {
44+
// Ignore.
45+
});
46+
Expect.fail('No exception thrown!');
47+
} on String catch (e, s) {
48+
assertStack(expected_stack, s);
49+
}
50+
51+
// Caller doesn't catch, but we have a catchError set.
52+
StackTrace stack_trace;
53+
await f().catchError((e, s) {
54+
stack_trace = s;
55+
});
56+
assertStack(expected_stack, stack_trace);
57+
}
58+
59+
// Test functions:
60+
61+
Future<void> throwSync() {
62+
throw '';
63+
}
64+
65+
Future<void> throwAsync() async {
66+
await 0;
67+
throw '';
68+
}
69+
70+
// ----
71+
// Scenario: All async functions yielded at least once before throw:
72+
// ----
73+
Future<void> allYield() async {
74+
await 0;
75+
await allYield2();
76+
}
77+
78+
Future<void> allYield2() async {
79+
await 0;
80+
await allYield3();
81+
}
82+
83+
Future<void> allYield3() async {
84+
await 0;
85+
throwSync();
86+
}
87+
88+
// For: --causal-async-stacks
89+
Map<int, String> allYieldMapCausal = {
90+
0: '#0 throwSync ',
91+
1: '#1 allYield3 ',
92+
2: '<asynchronous suspension>',
93+
3: '#2 allYield2 ',
94+
4: '<asynchronous suspension>',
95+
5: '#3 allYield ',
96+
4: '<asynchronous suspension>',
97+
// Callers, like doTest and main ..
98+
};
99+
100+
// For: --no-causal-async-stacks
101+
Map<int, String> allYieldMapNoCausal = {
102+
0: '#0 throwSync ',
103+
1: '#1 allYield3 ',
104+
2: '#2 _RootZone.runUnary ',
105+
// The rest are more Dart internal async mechanisms..
106+
};
107+
108+
// ----
109+
// Scenario: None of the async functions yieled before the throw:
110+
// ----
111+
Future<void> noYields() async {
112+
await noYields2();
113+
}
114+
115+
Future<void> noYields2() async {
116+
await noYields3();
117+
}
118+
119+
Future<void> noYields3() async {
120+
throwSync();
121+
}
122+
123+
// For: --causal-async-stacks
124+
Map<int, String> noYieldsMapCausal = {
125+
0: '#0 throwSync ',
126+
1: '#1 noYields3 ',
127+
2: '<asynchronous suspension>',
128+
3: '#2 noYields2 ',
129+
4: '<asynchronous suspension>',
130+
5: '#3 noYields ',
131+
4: '<asynchronous suspension>',
132+
// Callers, like doTest and main ..
133+
};
134+
135+
// For: --no-causal-async-stacks
136+
Map<int, String> noYieldsMapNoCausal = {
137+
0: '#0 throwSync ',
138+
1: '#1 noYields3 ',
139+
// Skip: _AsyncAwaitCompleter.start
140+
3: '#3 noYields3 ',
141+
4: '#4 noYields2 ',
142+
// Skip: _AsyncAwaitCompleter.start
143+
6: '#6 noYields2 ',
144+
7: '#7 noYields ',
145+
// Skip: _AsyncAwaitCompleter.start
146+
9: '#9 noYields ',
147+
// Calling functions like doTest and main ..
148+
};
149+
150+
// ----
151+
// Scenario: Mixed yielding and non-yielding frames:
152+
// ----
153+
Future<void> mixedYields() async {
154+
await mixedYields2();
155+
}
156+
157+
Future<void> mixedYields2() async {
158+
await 0;
159+
await mixedYields3();
160+
}
161+
162+
Future<void> mixedYields3() async {
163+
return throwAsync();
164+
}
165+
166+
// For: --causal-async-stacks
167+
Map<int, String> mixedYieldsMapCausal = {
168+
0: '#0 throwAsync ',
169+
1: '<asynchronous suspension>',
170+
2: '#1 mixedYields3 ',
171+
3: '<asynchronous suspension>',
172+
4: '#2 mixedYields2 ',
173+
5: '<asynchronous suspension>',
174+
6: '#3 mixedYields ',
175+
7: '<asynchronous suspension>',
176+
// Callers, like doTest and main ..
177+
};
178+
179+
// For: --no-causal-async-stacks
180+
Map<int, String> mixedYieldsMapNoCausal = {
181+
0: '#0 throwAsync ',
182+
1: '#1 _RootZone.runUnary ',
183+
// The rest are more Dart internal async mechanisms..
184+
};
185+
186+
// ----
187+
// Scenario: Non-async frame:
188+
// ----
189+
Future<void> syncSuffix() async {
190+
await syncSuffix2();
191+
}
192+
193+
Future<void> syncSuffix2() async {
194+
await 0;
195+
await syncSuffix3();
196+
}
197+
198+
Future<void> syncSuffix3() {
199+
return throwAsync();
200+
}
201+
202+
// For: --causal-async-stacks
203+
Map<int, String> syncSuffixMapCausal = {
204+
0: '#0 throwAsync ',
205+
1: '<asynchronous suspension>',
206+
2: '#1 syncSuffix3 ',
207+
3: '#2 syncSuffix2 ',
208+
4: '<asynchronous suspension>',
209+
5: '#3 syncSuffix ',
210+
6: '<asynchronous suspension>',
211+
// Callers, like doTest and main ..
212+
};
213+
214+
// For: --no-causal-async-stacks
215+
Map<int, String> syncSuffixMapNoCausal = {
216+
0: '#0 throwAsync ',
217+
1: '#1 _RootZone.runUnary ',
218+
// The rest are more Dart internal async mechanisms..
219+
};
220+
221+
// ----
222+
// Scenario: Caller is non-async, has no upwards stack:
223+
// ----
224+
225+
Future nonAsyncNoStack() async => await nonAsyncNoStack1();
226+
227+
Future nonAsyncNoStack1() async => await nonAsyncNoStack2();
228+
229+
Future nonAsyncNoStack2() async => Future.value(0).then((_) => throwAsync());
230+
231+
// For: --causal-async-stacks
232+
Map<int, String> nonAsyncNoStackMapCausal = {
233+
0: '#0 throwAsync ',
234+
1: '<asynchronous suspension>',
235+
2: '#1 nonAsyncNoStack2.<anonymous closure> ',
236+
3: '#2 _RootZone.runUnary ',
237+
// The rest are more Dart internal async mechanisms..
238+
};
239+
240+
// For: --no-causal-async-stacks
241+
Map<int, String> nonAsyncNoStackMapNoCausal = {
242+
0: '#0 throwAsync ',
243+
1: '#1 _RootZone.runUnary ',
244+
// The rest are more Dart internal async mechanisms..
245+
};
246+
247+
// ----
248+
// Test "Suites":
249+
// ----
250+
251+
Future<void> doTestsCausal() async {
252+
await doTest(allYield, allYieldMapCausal);
253+
await doTest(noYields, noYieldsMapCausal);
254+
await doTest(mixedYields, mixedYieldsMapCausal);
255+
await doTest(syncSuffix, syncSuffixMapCausal);
256+
await doTest(nonAsyncNoStack, nonAsyncNoStackMapCausal);
257+
}
258+
259+
Future<void> doTestsNoCausal() async {
260+
await doTest(allYield, allYieldMapNoCausal);
261+
await doTest(noYields, noYieldsMapNoCausal);
262+
await doTest(mixedYields, mixedYieldsMapNoCausal);
263+
await doTest(syncSuffix, syncSuffixMapNoCausal);
264+
await doTest(nonAsyncNoStack, nonAsyncNoStackMapNoCausal);
265+
}

runtime/tests/vm/vm.status

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ dart/transferable_throws_oom_test: SkipByDesign # This test tries to allocate to
2525
[ $builder_tag == crossword ]
2626
dart/emit_aot_size_info_flag_test: SkipByDesign # The test itself cannot determine the location of gen_snapshot (only tools/test.py knows where it is).
2727

28+
[ $builder_tag == obfuscated ]
29+
dart/causal_stacks/*: SkipByDesign # Asserts exact stacktrace output.
30+
2831
[ $builder_tag == optimization_counter_threshold ]
2932
cc/*: Skip # Many tests want see unoptimized code running
3033
dart/appjit*: SkipByDesign # Test needs to a particular opt-counter value

0 commit comments

Comments
 (0)