Skip to content

Commit e2a3bf3

Browse files
MoLowdanielleadams
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 ad537e6 commit e2a3bf3

File tree

4 files changed

+43
-21
lines changed

4 files changed

+43
-21
lines changed

lib/internal/test_runner/harness.js

Lines changed: 7 additions & 3 deletions
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();
Lines changed: 6 additions & 0 deletions
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));
Lines changed: 5 additions & 0 deletions
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

Lines changed: 25 additions & 18 deletions
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)
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