Skip to content

Commit 780f288

Browse files
committed
Default failure summary for expanded reporter
1 parent db0c811 commit 780f288

File tree

6 files changed

+116
-6
lines changed

6 files changed

+116
-6
lines changed

pkgs/test/test/runner/compact_reporter_test.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,7 @@ void main() {
562562
);
563563
});
564564
}, testOn: 'windows');
565+
565566
}
566567

567568
Future<void> _expectReport(

pkgs/test/test/runner/expanded_reporter_test.dart

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,12 @@ void main() {
6161
oh no
6262
test.dart 8:33 main.<fn>
6363
64-
+0 -3: Some tests failed.''',
64+
+0 -3: Some tests failed.
65+
66+
Failing tests:
67+
test.dart: failure 1
68+
test.dart: failure 2
69+
test.dart: failure 3''',
6570
);
6671
});
6772

@@ -102,7 +107,11 @@ void main() {
102107
test.dart 8:33 main.<fn>
103108
104109
+1 -2: success 2
105-
+2 -2: Some tests failed.''',
110+
+2 -2: Some tests failed.
111+
112+
Failing tests:
113+
test.dart: failure 1
114+
test.dart: failure 2''',
106115
);
107116
});
108117

@@ -156,7 +165,10 @@ void main() {
156165
test.dart 12:18 main.<fn>
157166
158167
+0 -1: wait
159-
+1 -1: Some tests failed.''',
168+
+1 -1: Some tests failed.
169+
170+
Failing tests:
171+
test.dart: failures''',
160172
);
161173
});
162174

@@ -260,7 +272,10 @@ void main() {
260272
five
261273
six
262274
+0 -1: wait
263-
+1 -1: Some tests failed.''',
275+
+1 -1: Some tests failed.
276+
277+
Failing tests:
278+
test.dart: test''',
264279
);
265280
});
266281
});
@@ -340,7 +355,11 @@ void main() {
340355
341356
+1 ~1 -2: skip 2
342357
+1 ~2 -2: success 2
343-
+2 ~2 -2: Some tests failed.''',
358+
+2 ~2 -2: Some tests failed.
359+
360+
Failing tests:
361+
test.dart: failure 1
362+
test.dart: failure 2''',
344363
);
345364
});
346365

@@ -386,6 +405,9 @@ void main() {
386405
387406
+0 -1: Some tests failed.
388407
408+
Failing tests:
409+
test.dart: failure 1
410+
389411
Consider enabling the flag chain-stack-traces to receive more detailed exceptions.
390412
For example, 'dart test --chain-stack-traces'.''',
391413
chainStackTraces: false,

pkgs/test_core/lib/src/runner/configuration.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,7 @@ class Configuration {
517517
pauseAfterLoad: null,
518518
debug: null,
519519
color: null,
520+
520521
configurationPath: null,
521522
reporter: null,
522523
fileReporters: null,
@@ -584,6 +585,7 @@ class Configuration {
584585
pauseAfterLoad: null,
585586
debug: null,
586587
color: null,
588+
587589
configurationPath: null,
588590
reporter: null,
589591
fileReporters: null,
@@ -656,6 +658,7 @@ class Configuration {
656658
version: null,
657659
debug: null,
658660
color: null,
661+
659662
configurationPath: null,
660663
coverage: null,
661664
coverageLcov: null,
@@ -718,6 +721,7 @@ class Configuration {
718721
pauseAfterLoad: null,
719722
debug: null,
720723
color: null,
724+
721725
configurationPath: null,
722726
reporter: null,
723727
fileReporters: null,
@@ -883,6 +887,7 @@ class Configuration {
883887
pauseAfterLoad: null,
884888
debug: null,
885889
color: null,
890+
886891
configurationPath: null,
887892
reporter: null,
888893
fileReporters: null,

pkgs/test_core/lib/src/runner/configuration/args.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,6 @@ final ArgParser _parser =
262262
'color',
263263
help: 'Use terminal colors.\n(auto-detected by default)',
264264
);
265-
266265
/// The following options are used only by the internal Google test runner.
267266
/// They're hidden and not supported as stable API surface outside Google.
268267

pkgs/test_core/lib/src/runner/reporter/expanded.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import '../engine.dart';
1313
import '../load_exception.dart';
1414
import '../load_suite.dart';
1515
import '../reporter.dart';
16+
import 'failure_summary.dart';
1617

1718
/// A reporter that prints each test on its own line.
1819
///
@@ -264,6 +265,14 @@ class ExpandedReporter implements Reporter {
264265
);
265266
}
266267
_progressLine('Some tests failed.', color: _red);
268+
269+
writeFailureSummary(
270+
_sink,
271+
failed: _engine.failed,
272+
active: _engine.active,
273+
red: _red,
274+
noColor: _noColor,
275+
);
267276
} else if (_engine.passed.isEmpty) {
268277
_progressLine('All tests skipped.');
269278
} else {
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright (c) 2026, 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 'package:test_api/src/backend/live_test.dart'; // ignore: implementation_imports
6+
7+
/// The maximum number of individual failing tests to list in the summary.
8+
const maxFailureSummaryCount = 5;
9+
10+
/// Writes a summary of failed and incomplete tests to [sink].
11+
///
12+
/// [failed] are tests that completed with a failure or error. [active] are
13+
/// tests that did not complete before the run ended. Active tests are
14+
/// annotated with `(did not complete)`.
15+
///
16+
/// The list is sorted by `(suite.path, test.name)` for deterministic output.
17+
/// At most [maxFailureSummaryCount] tests are listed individually; if there
18+
/// are more, a "... and N more" line is appended.
19+
void writeFailureSummary(
20+
StringSink sink, {
21+
required Iterable<LiveTest> failed,
22+
required Iterable<LiveTest> active,
23+
required String red,
24+
required String noColor,
25+
}) {
26+
final entries = <_SummaryEntry>[
27+
for (final test in failed)
28+
_SummaryEntry(test.suite.path, test.test.name, didNotComplete: false),
29+
for (final test in active)
30+
_SummaryEntry(test.suite.path, test.test.name, didNotComplete: true),
31+
];
32+
33+
if (entries.isEmpty) return;
34+
35+
entries.sort();
36+
37+
sink.writeln('');
38+
sink.writeln('${red}Failing tests:$noColor');
39+
40+
final displayCount =
41+
entries.length > maxFailureSummaryCount
42+
? maxFailureSummaryCount
43+
: entries.length;
44+
45+
for (var i = 0; i < displayCount; i++) {
46+
final entry = entries[i];
47+
final suffix = entry.didNotComplete ? ' (did not complete)' : '';
48+
if (entry.path != null) {
49+
sink.writeln(' ${entry.path}: ${entry.name}$suffix');
50+
} else {
51+
sink.writeln(' ${entry.name}$suffix');
52+
}
53+
}
54+
55+
final remaining = entries.length - displayCount;
56+
if (remaining > 0) {
57+
sink.writeln(' ... and $remaining more');
58+
}
59+
}
60+
61+
class _SummaryEntry implements Comparable<_SummaryEntry> {
62+
final String? path;
63+
final String name;
64+
final bool didNotComplete;
65+
66+
_SummaryEntry(this.path, this.name, {required this.didNotComplete});
67+
68+
@override
69+
int compareTo(_SummaryEntry other) {
70+
final pathCmp = (path ?? '').compareTo(other.path ?? '');
71+
if (pathCmp != 0) return pathCmp;
72+
return name.compareTo(other.name);
73+
}
74+
}

0 commit comments

Comments
 (0)