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

Lines changed: 21 additions & 3 deletions
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

Lines changed: 4 additions & 1 deletion
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 = {
Lines changed: 49 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy