Skip to content

Commit 04fdc3e

Browse files
MoLowruyadorno
authored andcommitted
test_runner: graceful termination on --test only
PR-URL: #43977 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Nitzan Uziely <linkgoron@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
1 parent 72a9ecf commit 04fdc3e

File tree

4 files changed

+43
-21
lines changed

4 files changed

+43
-21
lines changed

lib/internal/test_runner/harness.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ const {
1313
ERR_TEST_FAILURE,
1414
},
1515
} = require('internal/errors');
16+
const { getOptionValue } = require('internal/options');
1617
const { Test, ItTest, Suite } = require('internal/test_runner/test');
1718

18-
19+
const isTestRunner = getOptionValue('--test');
1920
const testResources = new SafeMap();
2021
const root = new Test({ __proto__: null, name: '<root>' });
2122
let wasRootSetup = false;
@@ -134,8 +135,11 @@ function setup(root) {
134135
process.on('uncaughtException', exceptionHandler);
135136
process.on('unhandledRejection', rejectionHandler);
136137
process.on('beforeExit', exitHandler);
137-
process.on('SIGINT', terminationHandler);
138-
process.on('SIGTERM', terminationHandler);
138+
// TODO(MoLow): Make it configurable to hook when isTestRunner === false.
139+
if (isTestRunner) {
140+
process.on('SIGINT', terminationHandler);
141+
process.on('SIGTERM', terminationHandler);
142+
}
139143

140144
root.reporter.pipe(process.stdout);
141145
root.reporter.version();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const test = require('node:test');
2+
const { setTimeout } = require('timers/promises');
3+
4+
// We are using a very large timeout value to ensure that the parent process
5+
// will have time to send a SIGINT signal to cancel the test.
6+
test('never ending test', () => setTimeout(100_000_000));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const test = require('node:test');
2+
3+
test('never ending test', () => {
4+
while (true);
5+
});

test/parallel/test-runner-exit-code.js

+25-18
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,29 @@
22
const common = require('../common');
33
const fixtures = require('../common/fixtures');
44
const assert = require('assert');
5-
const { spawnSync } = require('child_process');
6-
const { setTimeout } = require('timers/promises');
5+
const { spawnSync, spawn } = require('child_process');
6+
const { once } = require('events');
7+
const { finished } = require('stream/promises');
8+
9+
async function runAndKill(file) {
10+
if (common.isWindows) {
11+
common.printSkipMessage(`signals are not supported in windows, skipping ${file}`);
12+
return;
13+
}
14+
let stdout = '';
15+
const child = spawn(process.execPath, ['--test', file]);
16+
child.stdout.setEncoding('utf8');
17+
child.stdout.on('data', (chunk) => {
18+
if (!stdout.length) child.kill('SIGINT');
19+
stdout += chunk;
20+
});
21+
const [code, signal] = await once(child, 'exit');
22+
await finished(child.stdout);
23+
assert.match(stdout, /not ok 1/);
24+
assert.match(stdout, /# cancelled 1\n/);
25+
assert.strictEqual(signal, null);
26+
assert.strictEqual(code, 1);
27+
}
728

829
if (process.argv[2] === 'child') {
930
const test = require('node:test');
@@ -17,12 +38,6 @@ if (process.argv[2] === 'child') {
1738
test('failing test', () => {
1839
assert.strictEqual(true, false);
1940
});
20-
} else if (process.argv[3] === 'never_ends') {
21-
assert.strictEqual(process.argv[3], 'never_ends');
22-
test('never ending test', () => {
23-
return setTimeout(100_000_000);
24-
});
25-
process.kill(process.pid, 'SIGINT');
2641
} else assert.fail('unreachable');
2742
} else {
2843
let child = spawnSync(process.execPath, [__filename, 'child', 'pass']);
@@ -37,14 +52,6 @@ if (process.argv[2] === 'child') {
3752
assert.strictEqual(child.status, 1);
3853
assert.strictEqual(child.signal, null);
3954

40-
child = spawnSync(process.execPath, [__filename, 'child', 'never_ends']);
41-
assert.strictEqual(child.status, 1);
42-
assert.strictEqual(child.signal, null);
43-
if (common.isWindows) {
44-
common.printSkipMessage('signals are not supported in windows');
45-
} else {
46-
const stdout = child.stdout.toString();
47-
assert.match(stdout, /not ok 1 - never ending test/);
48-
assert.match(stdout, /# cancelled 1/);
49-
}
55+
runAndKill(fixtures.path('test-runner', 'never_ending_sync.js')).then(common.mustCall());
56+
runAndKill(fixtures.path('test-runner', 'never_ending_async.js')).then(common.mustCall());
5057
}

0 commit comments

Comments
 (0)