Skip to content

Commit 87ac6cf

Browse files
jakecastelliRafaelGSS
authored andcommitted
test_runner: improve --test-timeout to be per test
Previously `--test-timeout` is set on per test execution, this is obviously a bug as per test execution is hard to be expected, this patch addresses the issue by setting `timeout` from per execution to per test. This patch also fixes a minor issue that `--test-timeout` is not being respected when running without `--test`. PR-URL: #57672 Fixes: #57656 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Pietro Marchini <pietro.marchini94@gmail.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
1 parent 406b06b commit 87ac6cf

File tree

8 files changed

+96
-8
lines changed

8 files changed

+96
-8
lines changed

lib/internal/test_runner/runner.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ class FileTest extends Test {
191191
column: 1,
192192
file: resolve(this.name),
193193
};
194+
this.timeout = null;
194195
}
195196

196197
#skipReporting() {

lib/internal/test_runner/test.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,12 @@ class Test extends AsyncResource {
608608
if (timeout != null && timeout !== Infinity) {
609609
validateNumber(timeout, 'options.timeout', 0, TIMEOUT_MAX);
610610
this.timeout = timeout;
611+
} else if (timeout == null) {
612+
const cliTimeout = this.config.timeout;
613+
if (cliTimeout != null && cliTimeout !== Infinity) {
614+
validateNumber(cliTimeout, 'this.config.timeout', 0, TIMEOUT_MAX);
615+
this.timeout = cliTimeout;
616+
}
611617
}
612618

613619
if (skip) {
@@ -1394,6 +1400,7 @@ class Suite extends Test {
13941400
reportedType = 'suite';
13951401
constructor(options) {
13961402
super(options);
1403+
this.timeout = null;
13971404

13981405
if (this.config.testNamePatterns !== null &&
13991406
this.config.testSkipPatterns !== null &&

lib/internal/test_runner/utils.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ function parseCommandLine() {
196196
const sourceMaps = getOptionValue('--enable-source-maps');
197197
const updateSnapshots = getOptionValue('--test-update-snapshots');
198198
const watch = getOptionValue('--watch');
199+
const timeout = getOptionValue('--test-timeout') || Infinity;
199200
const isChildProcess = process.env.NODE_TEST_CONTEXT === 'child';
200201
const isChildProcessV8 = process.env.NODE_TEST_CONTEXT === 'child-v8';
201202
let concurrency;
@@ -211,7 +212,6 @@ function parseCommandLine() {
211212
let shard;
212213
let testNamePatterns = mapPatternFlagToRegExArray('--test-name-pattern');
213214
let testSkipPatterns = mapPatternFlagToRegExArray('--test-skip-pattern');
214-
let timeout;
215215

216216
if (isChildProcessV8) {
217217
kBuiltinReporters.set('v8-serializer', 'internal/test_runner/reporter/v8-serializer');
@@ -242,7 +242,6 @@ function parseCommandLine() {
242242

243243
if (isTestRunner) {
244244
isolation = getOptionValue('--test-isolation');
245-
timeout = getOptionValue('--test-timeout') || Infinity;
246245

247246
if (isolation === 'none') {
248247
concurrency = 1;
@@ -271,7 +270,6 @@ function parseCommandLine() {
271270
};
272271
}
273272
} else {
274-
timeout = Infinity;
275273
concurrency = 1;
276274
const testNamePatternFlag = getOptionValue('--test-name-pattern');
277275
only = getOptionValue('--test-only');
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Flags: --test-timeout=20
2+
'use strict';
3+
const { describe, test } = require('node:test');
4+
const { setTimeout } = require('node:timers/promises');
5+
6+
describe('--test-timeout is set to 20ms', () => {
7+
test('should timeout after 20ms', async () => {
8+
await setTimeout(200000, undefined, { ref: false });
9+
});
10+
test('should timeout after 5ms', { timeout: 5 }, async () => {
11+
await setTimeout(200000, undefined, { ref: false });
12+
});
13+
14+
test('should not timeout', { timeout: 50000 }, async () => {
15+
await setTimeout(1);
16+
});
17+
18+
test('should pass', async () => {});
19+
});
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
TAP version 13
2+
# Subtest: --test-timeout is set to 20ms
3+
# Subtest: should timeout after 20ms
4+
not ok 1 - should timeout after 20ms
5+
---
6+
duration_ms: *
7+
type: 'test'
8+
location: '/test/fixtures/test-runner/output/test-timeout-flag.js:(LINE):3'
9+
failureType: 'testTimeoutFailure'
10+
error: 'test timed out after 20ms'
11+
code: 'ERR_TEST_FAILURE'
12+
stack: |-
13+
async Promise.all (index 0)
14+
...
15+
# Subtest: should timeout after 5ms
16+
not ok 2 - should timeout after 5ms
17+
---
18+
duration_ms: *
19+
type: 'test'
20+
location: '/test/fixtures/test-runner/output/test-timeout-flag.js:(LINE):3'
21+
failureType: 'testTimeoutFailure'
22+
error: 'test timed out after 5ms'
23+
code: 'ERR_TEST_FAILURE'
24+
...
25+
# Subtest: should not timeout
26+
ok 3 - should not timeout
27+
---
28+
duration_ms: *
29+
type: 'test'
30+
...
31+
# Subtest: should pass
32+
ok 4 - should pass
33+
---
34+
duration_ms: *
35+
type: 'test'
36+
...
37+
1..4
38+
not ok 1 - --test-timeout is set to 20ms
39+
---
40+
duration_ms: *
41+
type: 'suite'
42+
location: '/test/fixtures/test-runner/output/test-timeout-flag.js:(LINE):1'
43+
failureType: 'subtestsFailed'
44+
error: '2 subtests failed'
45+
code: 'ERR_TEST_FAILURE'
46+
...
47+
1..1
48+
# tests 4
49+
# suites 1
50+
# pass 2
51+
# fail 0
52+
# cancelled 2
53+
# skipped 0
54+
# todo 0
55+
# duration_ms *

test/parallel/test-runner-filetest-location.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const fixtures = require('../common/fixtures');
44
const { strictEqual } = require('node:assert');
55
const { relative } = require('node:path');
66
const { run } = require('node:test');
7-
const fixture = fixtures.path('test-runner', 'never_ending_sync.js');
7+
const fixture = fixtures.path('test-runner', 'index.js');
88
const relativePath = relative(process.cwd(), fixture);
99
const stream = run({
1010
files: [relativePath],
@@ -13,7 +13,7 @@ const stream = run({
1313

1414
stream.on('test:fail', common.mustCall((result) => {
1515
strictEqual(result.name, relativePath);
16-
strictEqual(result.details.error.failureType, 'testTimeoutFailure');
16+
strictEqual(result.details.error.failureType, 'testCodeFailure');
1717
strictEqual(result.line, 1);
1818
strictEqual(result.column, 1);
1919
strictEqual(result.file, fixture);

test/parallel/test-runner-output.mjs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,15 @@ const tests = [
132132
name: 'test-runner/output/timeout_in_before_each_should_not_affect_further_tests.js',
133133
flags: ['--test-reporter=tap'],
134134
},
135+
{
136+
name: 'test-runner/output/test-timeout-flag.js',
137+
flags: ['--test-reporter=tap'],
138+
},
139+
// --test-timeout should work with or without --test flag
140+
{
141+
name: 'test-runner/output/test-timeout-flag.js',
142+
flags: ['--test-reporter=tap', '--test'],
143+
},
135144
{
136145
name: 'test-runner/output/hooks-with-no-global-test.js',
137146
flags: ['--test-reporter=tap'],

test/parallel/test-runner-run.mjs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,9 @@ describe('require(\'node:test\').run', { concurrency: true }, () => {
7070

7171
it('should support timeout', async () => {
7272
const stream = run({ timeout: 50, files: [
73-
fixtures.path('test-runner', 'never_ending_sync.js'),
74-
fixtures.path('test-runner', 'never_ending_async.js'),
73+
fixtures.path('test-runner', 'timeout-basic.mjs'),
7574
] });
76-
stream.on('test:fail', common.mustCall(2));
75+
stream.on('test:fail', common.mustCall(1));
7776
stream.on('test:pass', common.mustNotCall());
7877
// eslint-disable-next-line no-unused-vars
7978
for await (const _ of stream);

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