test: name the in-flight file and the native error when bun dies before its summary (#3037)

This commit is contained in:
garrytan committed 2026-10-05 02:59:45 +00:00
1 parent 5fce3cdc02
commit 8ba14433a1
2 files changed
+20

No files matched your search

+9
View File
@@ -676,6 +676,8 @@ export interface FreeRunReport {
inFlight: string[];
/** Planned files never observed in the stream (silent passers + never-flushed wedges). */
filesWithNoOutput: number;
/** The last nonblank line either stream printed: a native abort's message when bun dies without a summary. */
lastOutputLine: string | null;
}
interface FileProgress {
@@ -711,6 +713,7 @@ export class FreeRunReporter {
private testsRan: number | null = null;
private filesRan: number | null = null;
private sawSummary = false;
private lastOutputLine: string | null = null;
constructor(
private readonly plannedFiles: string[],
@@ -758,6 +761,7 @@ export class FreeRunReporter {
unhandledErrors: [...this.unhandled],
inFlight,
filesWithNoOutput: this.plannedFiles.filter((f) => !this.progress.has(normalizeRelativePath(f))).length,
lastOutputLine: this.lastOutputLine,
};
}
@@ -768,6 +772,7 @@ export class FreeRunReporter {
// (fail) lines land under a second phantom file (observed on the first
// Linux run: 5 real failures reported as 10 across 2 files).
const line = stripAnsiLine(rawLine).replace(/^::group::/, '');
if (line.trim() !== '') this.lastOutputLine = line.trim();
let visible = false;
const failedCount = this.failureSummary.consume(line, origin);
if (failedCount !== null) {
@@ -903,6 +908,10 @@ export function buildRunEpilogue(
for (const u of report.unhandledErrors) {
lines.push(` ⚠ unhandled error between tests (around ${u.file ?? 'unknown file'})`);
}
if (status === 'failed' && !report.sawTerminalSummary) {
lines.push(` ⚠ test process ended before its summary; in flight: ${report.inFlight.length > 0 ? report.inFlight.join(', ') : 'unknown'}`);
if (report.lastOutputLine) lines.push(` ⚠ last output: ${report.lastOutputLine}`);
}
if (status === 'timed-out') {
if (report.inFlight.length > 0) {
lines.push(` ⏱ in flight at kill: ${report.inFlight.join(', ')}`);
+11
View File
@@ -805,6 +805,17 @@ describe('test-free-shards: output contract (log capture, quiet console, failure
expect(lines.some((l) => l.startsWith('[test:free] FAIL — '))).toBe(true);
}, 30_000);
test('a test process that aborts before its summary names the in-flight file and its last output', () => {
const reporter = new FreeRunReporter(['test/done.test.ts', 'test/aborted.test.ts']);
reporter.write('::group::test/done.test.ts:\n(pass) fine\n::endgroup::\n', 'stdout');
reporter.write('::group::test/aborted.test.ts:\n(pass) first\n', 'stdout');
reporter.write('GetQueuedCompletionStatusEx: (735) ERROR_ABANDONED_WAIT_0\n', 'stderr');
reporter.end();
const lines = buildRunEpilogue('failed', reporter.report(), 16_000, '/tmp/x.log');
expect(lines).toContain(' ⚠ test process ended before its summary; in flight: test/aborted.test.ts');
expect(lines).toContain(' ⚠ last output: GetQueuedCompletionStatusEx: (735) ERROR_ABANDONED_WAIT_0');
});
test('timeout with no observable header falls back to the buffered-parallel explanation', () => {
const reporter = new FreeRunReporter(['test/a.test.ts', 'test/b.test.ts']);
reporter.end();