Skip to content

Commit 19cfa31

Browse files
cjihrigtargos
authored andcommitted
test_runner: close and flush destinations on forced exit
This commit updates the test runner to explicitly close and flush all destination file streams when the --test-force-exit flag is used. Fixes: #54327 PR-URL: #55099 Reviewed-By: Chemi Atlow <chemi@atlow.co.il> Reviewed-By: Jake Yuesong Li <jake.yuesong@gmail.com>
1 parent 9aeba48 commit 19cfa31

File tree

3 files changed

+74
-4
lines changed

3 files changed

+74
-4
lines changed

lib/internal/test_runner/test.js

+21-3
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ const {
1414
NumberPrototypeToFixed,
1515
ObjectDefineProperty,
1616
ObjectSeal,
17+
Promise,
1718
PromisePrototypeThen,
1819
PromiseResolve,
1920
ReflectApply,
2021
RegExpPrototypeExec,
2122
SafeMap,
2223
SafePromiseAll,
24+
SafePromiseAllReturnVoid,
2325
SafePromisePrototypeFinally,
2426
SafePromiseRace,
2527
SafeSet,
@@ -46,6 +48,7 @@ const {
4648
createDeferredCallback,
4749
countCompletedTest,
4850
isTestFailureError,
51+
reporterScope,
4952
} = require('internal/test_runner/utils');
5053
const {
5154
createDeferredPromise,
@@ -973,10 +976,25 @@ class Test extends AsyncResource {
973976
// any remaining ref'ed handles, then do that now. It is theoretically
974977
// possible that a ref'ed handle could asynchronously create more tests,
975978
// but the user opted into this behavior.
976-
this.reporter.once('close', () => {
977-
process.exit();
978-
});
979+
const promises = [];
980+
981+
for (let i = 0; i < reporterScope.reporters.length; i++) {
982+
const { destination } = reporterScope.reporters[i];
983+
984+
ArrayPrototypePush(promises, new Promise((resolve) => {
985+
destination.on('unpipe', () => {
986+
if (!destination.closed && typeof destination.close === 'function') {
987+
destination.close(resolve);
988+
} else {
989+
resolve();
990+
}
991+
});
992+
}));
993+
}
994+
979995
this.harness.teardown();
996+
await SafePromiseAllReturnVoid(promises);
997+
process.exit();
980998
}
981999
}
9821000

lib/internal/test_runner/utils.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ function shouldColorizeTestFiles(destinations) {
144144

145145
async function getReportersMap(reporters, destinations) {
146146
return SafePromiseAllReturnArrayLike(reporters, async (name, i) => {
147-
const destination = kBuiltinDestinations.get(destinations[i]) ?? createWriteStream(destinations[i]);
147+
const destination = kBuiltinDestinations.get(destinations[i]) ??
148+
createWriteStream(destinations[i], { __proto__: null, flush: true });
148149

149150
// Load the test reporter passed to --test-reporter
150151
let reporter = tryBuiltinReporter(name);
@@ -301,6 +302,8 @@ function parseCommandLine() {
301302
const { reporter, destination } = reportersMap[i];
302303
compose(rootReporter, reporter).pipe(destination);
303304
}
305+
306+
reporterScope.reporters = reportersMap;
304307
});
305308

306309
globalTestOptions = {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
'use strict';
2+
require('../common');
3+
const fixtures = require('../common/fixtures');
4+
const tmpdir = require('../common/tmpdir');
5+
const { match, strictEqual } = require('node:assert');
6+
const { spawnSync } = require('node:child_process');
7+
const { readFileSync } = require('node:fs');
8+
const { test } = require('node:test');
9+
10+
function runWithReporter(reporter) {
11+
const destination = tmpdir.resolve(`${reporter}.out`);
12+
const args = [
13+
'--test-force-exit',
14+
`--test-reporter=${reporter}`,
15+
`--test-reporter-destination=${destination}`,
16+
fixtures.path('test-runner', 'reporters.js'),
17+
];
18+
const child = spawnSync(process.execPath, args);
19+
strictEqual(child.stdout.toString(), '');
20+
strictEqual(child.stderr.toString(), '');
21+
strictEqual(child.status, 1);
22+
return destination;
23+
}
24+
25+
tmpdir.refresh();
26+
27+
test('junit reporter', () => {
28+
const output = readFileSync(runWithReporter('junit'), 'utf8');
29+
match(output, /<!-- tests 4 -->/);
30+
match(output, /<!-- pass 2 -->/);
31+
match(output, /<!-- fail 2 -->/);
32+
match(output, /<!-- duration_ms/);
33+
match(output, /<\/testsuites>/);
34+
});
35+
36+
test('spec reporter', () => {
37+
const output = readFileSync(runWithReporter('spec'), 'utf8');
38+
match(output, /tests 4/);
39+
match(output, /pass 2/);
40+
match(output, /fail 2/);
41+
});
42+
43+
test('tap reporter', () => {
44+
const output = readFileSync(runWithReporter('tap'), 'utf8');
45+
match(output, /# tests 4/);
46+
match(output, /# pass 2/);
47+
match(output, /# fail 2/);
48+
match(output, /# duration_ms/);
49+
});

0 commit comments

Comments
 (0)