Skip to content

Commit beff449

Browse files
authored
Conditional chaining (#584)
* plumb chain-stack-traces configuration through * correctly plumb new flag through and add corresponding tests * add example command * chains -> chained
1 parent 6d921ed commit beff449

File tree

7 files changed

+129
-32
lines changed

7 files changed

+129
-32
lines changed

lib/src/backend/invoker.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,12 @@ class Invoker {
289289
_controller.addError(error, stackTrace);
290290
removeAllOutstandingCallbacks();
291291

292+
if (!liveTest.test.metadata.chainStackTraces) {
293+
_printsOnFailure.add("Consider enabling the flag chain-stack-traces to "
294+
"recieve more detailed exceptions.\n"
295+
"For example, 'pub run test --chain-stack-traces'.");
296+
}
297+
292298
if (_printsOnFailure.isNotEmpty) {
293299
print(_printsOnFailure.join("\n\n"));
294300
_printsOnFailure.clear();
@@ -357,6 +363,6 @@ class Invoker {
357363
print: (self, parent, zone, line) =>
358364
_controller.message(new Message.print(line))),
359365
onError: _handleError);
360-
});
366+
}, when: liveTest.test.metadata.chainStackTraces);
361367
}
362368
}

lib/src/backend/metadata.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ class Metadata {
4242
bool get verboseTrace => _verboseTrace ?? false;
4343
final bool _verboseTrace;
4444

45+
/// Whether to chain stack traces.
46+
bool get chainStackTraces => _chainStackTraces ?? true;
47+
final bool _chainStackTraces;
48+
4549
/// The user-defined tags attached to the test or suite.
4650
final Set<String> tags;
4751

@@ -135,6 +139,7 @@ class Metadata {
135139
Timeout timeout,
136140
bool skip,
137141
bool verboseTrace,
142+
bool chainStackTraces,
138143
String skipReason,
139144
Iterable<String> tags,
140145
Map<PlatformSelector, Metadata> onPlatform,
@@ -145,6 +150,7 @@ class Metadata {
145150
timeout: timeout,
146151
skip: skip,
147152
verboseTrace: verboseTrace,
153+
chainStackTraces: chainStackTraces,
148154
skipReason: skipReason,
149155
tags: tags,
150156
onPlatform: onPlatform,
@@ -178,13 +184,15 @@ class Metadata {
178184
bool skip,
179185
this.skipReason,
180186
bool verboseTrace,
187+
bool chainStackTraces,
181188
Iterable<String> tags,
182189
Map<PlatformSelector, Metadata> onPlatform,
183190
Map<BooleanSelector, Metadata> forTag})
184191
: testOn = testOn == null ? PlatformSelector.all : testOn,
185192
timeout = timeout == null ? const Timeout.factor(1) : timeout,
186193
_skip = skip,
187194
_verboseTrace = verboseTrace,
195+
_chainStackTraces = chainStackTraces,
188196
tags = new UnmodifiableSetView(tags == null ? new Set() : tags.toSet()),
189197
onPlatform =
190198
onPlatform == null ? const {} : new UnmodifiableMapView(onPlatform),
@@ -201,6 +209,7 @@ class Metadata {
201209
Timeout timeout,
202210
skip,
203211
bool verboseTrace,
212+
bool chainStackTraces,
204213
Map<String, dynamic> onPlatform,
205214
tags})
206215
: testOn = testOn == null
@@ -209,6 +218,7 @@ class Metadata {
209218
timeout = timeout == null ? const Timeout.factor(1) : timeout,
210219
_skip = skip == null ? null : skip != false,
211220
_verboseTrace = verboseTrace,
221+
_chainStackTraces = chainStackTraces,
212222
skipReason = skip is String ? skip : null,
213223
onPlatform = _parseOnPlatform(onPlatform),
214224
tags = _parseTags(tags),
@@ -230,6 +240,7 @@ class Metadata {
230240
_skip = serialized['skip'],
231241
skipReason = serialized['skipReason'],
232242
_verboseTrace = serialized['verboseTrace'],
243+
_chainStackTraces = serialized['chainStackTraces'],
233244
tags = new Set.from(serialized['tags']),
234245
onPlatform = new Map.fromIterable(serialized['onPlatform'],
235246
key: (pair) => new PlatformSelector.parse(pair.first),
@@ -272,6 +283,7 @@ class Metadata {
272283
skip: other._skip ?? _skip,
273284
skipReason: other.skipReason ?? skipReason,
274285
verboseTrace: other._verboseTrace ?? _verboseTrace,
286+
chainStackTraces: other._chainStackTraces ?? _chainStackTraces,
275287
tags: tags.union(other.tags),
276288
onPlatform: mergeMaps(onPlatform, other.onPlatform,
277289
value: (metadata1, metadata2) => metadata1.merge(metadata2)),
@@ -284,6 +296,7 @@ class Metadata {
284296
Timeout timeout,
285297
bool skip,
286298
bool verboseTrace,
299+
bool chainStackTraces,
287300
String skipReason,
288301
Map<PlatformSelector, Metadata> onPlatform,
289302
Set<String> tags,
@@ -292,6 +305,7 @@ class Metadata {
292305
timeout ??= this.timeout;
293306
skip ??= this._skip;
294307
verboseTrace ??= this._verboseTrace;
308+
chainStackTraces ??= this._chainStackTraces;
295309
skipReason ??= this.skipReason;
296310
onPlatform ??= this.onPlatform;
297311
tags ??= this.tags;
@@ -301,6 +315,7 @@ class Metadata {
301315
timeout: timeout,
302316
skip: skip,
303317
verboseTrace: verboseTrace,
318+
chainStackTraces: chainStackTraces,
304319
skipReason: skipReason,
305320
onPlatform: onPlatform,
306321
tags: tags,
@@ -335,6 +350,7 @@ class Metadata {
335350
'skip': _skip,
336351
'skipReason': skipReason,
337352
'verboseTrace': _verboseTrace,
353+
'chainStackTraces': _chainStackTraces,
338354
'tags': tags.toList(),
339355
'onPlatform': serializedOnPlatform,
340356
'forTag': mapMap(forTag,

lib/src/runner/configuration.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ class Configuration {
213213
// Test-level configuration
214214
Timeout timeout,
215215
bool verboseTrace,
216+
bool chainStackTraces,
216217
bool skip,
217218
String skipReason,
218219
PlatformSelector testOn,
@@ -249,6 +250,7 @@ class Configuration {
249250
// Test-level configuration
250251
timeout: timeout,
251252
verboseTrace: verboseTrace,
253+
chainStackTraces: chainStackTraces,
252254
skip: skip,
253255
skipReason: skipReason,
254256
testOn: testOn,
@@ -418,6 +420,7 @@ class Configuration {
418420
// Test-level configuration
419421
Timeout timeout,
420422
bool verboseTrace,
423+
bool chainStackTraces,
421424
bool skip,
422425
String skipReason,
423426
PlatformSelector testOn,
@@ -451,6 +454,7 @@ class Configuration {
451454
onPlatform: onPlatform,
452455
timeout: timeout,
453456
verboseTrace: verboseTrace,
457+
chainStackTraces: chainStackTraces,
454458
skip: skip,
455459
skipReason: skipReason,
456460
testOn: testOn,

lib/src/runner/configuration/args.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ final ArgParser _parser = (() {
8585
'Implies --concurrency=1 and --timeout=none.\n'
8686
'Currently only supported for browser tests.',
8787
negatable: false);
88+
parser.addFlag("chain-stack-traces",
89+
help: 'Chained stack traces to provide greater exception details\n'
90+
'especially for asynchronous code. It may be useful to disable\n'
91+
'to provide improved test performance but at the cost of\n'
92+
'debuggability.',
93+
defaultsTo: true);
8894

8995
parser.addSeparator("======== Output");
9096
parser.addOption("reporter",
@@ -192,6 +198,7 @@ class _Parser {
192198
help: _ifParsed('help'),
193199
version: _ifParsed('version'),
194200
verboseTrace: _ifParsed('verbose-trace'),
201+
chainStackTraces: _ifParsed('chain-stack-traces'),
195202
jsTrace: _ifParsed('js-trace'),
196203
pauseAfterLoad: _ifParsed('pause-after-load'),
197204
color: _ifParsed('color'),

lib/src/runner/configuration/suite.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ class SuiteConfiguration {
133133
// Test-level configuration
134134
Timeout timeout,
135135
bool verboseTrace,
136+
bool chainStackTraces,
136137
bool skip,
137138
String skipReason,
138139
PlatformSelector testOn,
@@ -151,6 +152,7 @@ class SuiteConfiguration {
151152
metadata: new Metadata(
152153
timeout: timeout,
153154
verboseTrace: verboseTrace,
155+
chainStackTraces: chainStackTraces,
154156
skip: skip,
155157
skipReason: skipReason,
156158
testOn: testOn,
@@ -254,6 +256,7 @@ class SuiteConfiguration {
254256
// Test-level configuration
255257
Timeout timeout,
256258
bool verboseTrace,
259+
bool chainStackTraces,
257260
bool skip,
258261
String skipReason,
259262
PlatformSelector testOn,
@@ -272,6 +275,7 @@ class SuiteConfiguration {
272275
metadata: _metadata.change(
273276
timeout: timeout,
274277
verboseTrace: verboseTrace,
278+
chainStackTraces: chainStackTraces,
275279
skip: skip,
276280
skipReason: skipReason,
277281
testOn: testOn,

test/backend/invoker_test.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,25 @@ void main() {
570570
});
571571
});
572572

573+
group("chainStackTraces", () {
574+
test(
575+
"if disabled, directs users to run with the flag enabled when "
576+
"failures occur", () {
577+
expect(() async {
578+
var liveTest = _localTest(() {
579+
expect(true, isFalse);
580+
}, metadata: new Metadata(chainStackTraces: false))
581+
.load(suite);
582+
liveTest.onError.listen(expectAsync1((_) {}, count: 1));
583+
584+
await liveTest.run();
585+
},
586+
prints("Consider enabling the flag chain-stack-traces to "
587+
"recieve more detailed exceptions.\n"
588+
"For example, 'pub run test --chain-stack-traces'.\n"));
589+
});
590+
});
591+
573592
group("printOnFailure:", () {
574593
test("doesn't print anything if the test succeeds", () {
575594
expect(() async {

test/runner/runner_test.dart

Lines changed: 72 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,20 @@ void main() {
3333
}
3434
""";
3535

36+
final _asyncFailure = """
37+
import 'dart:async';
38+
39+
import 'package:test/test.dart';
40+
41+
void main() {
42+
test("failure", () async{
43+
await new Future((){});
44+
await new Future((){});
45+
throw "oh no";
46+
});
47+
}
48+
""";
49+
3650
final _defaultConcurrency = math.max(1, Platform.numberOfProcessors ~/ 2);
3751

3852
final _browsers =
@@ -44,52 +58,58 @@ final _browsers =
4458
final _usage = """
4559
Usage: pub run test [files or directories...]
4660
47-
-h, --help Shows this usage information.
48-
--version Shows the package's version.
61+
-h, --help Shows this usage information.
62+
--version Shows the package's version.
4963
5064
======== Selecting Tests
51-
-n, --name A substring of the name of the test to run.
52-
Regular expression syntax is supported.
53-
If passed multiple times, tests must match all substrings.
65+
-n, --name A substring of the name of the test to run.
66+
Regular expression syntax is supported.
67+
If passed multiple times, tests must match all substrings.
5468
55-
-N, --plain-name A plain-text substring of the name of the test to run.
56-
If passed multiple times, tests must match all substrings.
69+
-N, --plain-name A plain-text substring of the name of the test to run.
70+
If passed multiple times, tests must match all substrings.
5771
58-
-t, --tags Run only tests with all of the specified tags.
59-
Supports boolean selector syntax.
72+
-t, --tags Run only tests with all of the specified tags.
73+
Supports boolean selector syntax.
6074
61-
-x, --exclude-tags Don't run tests with any of the specified tags.
62-
Supports boolean selector syntax.
75+
-x, --exclude-tags Don't run tests with any of the specified tags.
76+
Supports boolean selector syntax.
6377
64-
--[no-]run-skipped Run skipped tests instead of skipping them.
78+
--[no-]run-skipped Run skipped tests instead of skipping them.
6579
6680
======== Running Tests
67-
-p, --platform The platform(s) on which to run the tests.
68-
$_browsers
81+
-p, --platform The platform(s) on which to run the tests.
82+
$_browsers
6983
70-
-P, --preset The configuration preset(s) to use.
71-
-j, --concurrency=<threads> The number of concurrent test suites run.
72-
(defaults to "$_defaultConcurrency")
84+
-P, --preset The configuration preset(s) to use.
85+
-j, --concurrency=<threads> The number of concurrent test suites run.
86+
(defaults to "$_defaultConcurrency")
7387
74-
--pub-serve=<port> The port of a pub serve instance serving "test/".
75-
--timeout The default test timeout. For example: 15s, 2x, none
76-
(defaults to "30s")
88+
--pub-serve=<port> The port of a pub serve instance serving "test/".
89+
--timeout The default test timeout. For example: 15s, 2x, none
90+
(defaults to "30s")
7791
78-
--pause-after-load Pauses for debugging before any tests execute.
79-
Implies --concurrency=1 and --timeout=none.
80-
Currently only supported for browser tests.
92+
--pause-after-load Pauses for debugging before any tests execute.
93+
Implies --concurrency=1 and --timeout=none.
94+
Currently only supported for browser tests.
95+
96+
--[no-]chain-stack-traces Chained stack traces to provide greater exception details
97+
especially for asynchronous code. It may be useful to disable
98+
to provide improved test performance but at the cost of
99+
debuggability.
100+
(defaults to on)
81101
82102
======== Output
83-
-r, --reporter The runner used to print test results.
103+
-r, --reporter The runner used to print test results.
84104
85-
[compact] A single line, updated continuously.
86-
[expanded] A separate line for each update.
87-
[json] A machine-readable format (see https://goo.gl/gBsV1a).
105+
[compact] A single line, updated continuously.
106+
[expanded] A separate line for each update.
107+
[json] A machine-readable format (see https://goo.gl/gBsV1a).
88108
89-
--verbose-trace Whether to emit stack traces with core library frames.
90-
--js-trace Whether to emit raw JavaScript stack traces for browser tests.
91-
--[no-]color Whether to use terminal colors.
92-
(auto-detected by default)
109+
--verbose-trace Whether to emit stack traces with core library frames.
110+
--js-trace Whether to emit raw JavaScript stack traces for browser tests.
111+
--[no-]color Whether to use terminal colors.
112+
(auto-detected by default)
93113
""";
94114

95115
void main() {
@@ -323,6 +343,27 @@ $_usage""");
323343
});
324344

325345
group("runs failing tests", () {
346+
test("defaults to chaining stack traces", () {
347+
d.file("test.dart", _asyncFailure).create();
348+
349+
var test = runTest(["test.dart"]);
350+
test.stdout.expect(consumeThrough(contains("asynchronous gap")));
351+
test.shouldExit(1);
352+
});
353+
354+
test("respects the chain-stack-traces flag", () {
355+
d.file("test.dart", _asyncFailure).create();
356+
357+
var test = runTest(["test.dart", "--no-chain-stack-traces"]);
358+
test.stdout.expect(containsInOrder([
359+
"00:00 +0: failure",
360+
"00:00 +0 -1: failure [E]",
361+
"oh no",
362+
"test.dart 9:5 main.<fn>",
363+
]));
364+
test.shouldExit(1);
365+
});
366+
326367
test("defined in a single file", () {
327368
d.file("test.dart", _failure).create();
328369

0 commit comments

Comments
 (0)