From 986fb05a98a7f4320ecd9f99ab3d0ea66cbb4103 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Thu, 1 Oct 2026 08:16:26 +0200 Subject: [PATCH 1/3] test: split process timeout report coverage Move the blocked Worker case into its own test. Signed-off-by: Filip Skokan Assisted-by: Codex --- .../test-report-process-timeout-worker.js | 57 +++++++++++++++++++ test/report/test-report-process-timeout.js | 47 --------------- 2 files changed, 57 insertions(+), 47 deletions(-) create mode 100644 test/report/test-report-process-timeout-worker.js diff --git a/test/report/test-report-process-timeout-worker.js b/test/report/test-report-process-timeout-worker.js new file mode 100644 index 000000000000..a42e2513ccba --- /dev/null +++ b/test/report/test-report-process-timeout-worker.js @@ -0,0 +1,57 @@ +'use strict'; + +// Tests --report-on-process-timeout with a blocked Worker. + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const { spawnSync } = require('child_process'); +const fixtures = require('../common/fixtures'); +const helper = require('../common/report'); +const tmpdir = require('../common/tmpdir'); + +tmpdir.refresh(); + +// A Worker thread that is blocked in a synchronous native call cannot +// provide its part of the report. It is left out, so that the report is +// completed before the process is forced to exit. +// If the Worker thread was not blocked yet when the deadline expired, which +// the fixture signals by creating a file, it is included in the report. Run +// the fixture again with a longer timeout in that case. +let child; +let report; +for (let timeout = common.platformTimeout(1000); ; timeout *= 2) { + // Child processes of a previous attempt may still be running, so use a + // different file for each attempt. + const marker = tmpdir.resolve(`blocked-worker.${timeout}.ready`); + child = spawnSync(process.execPath, [ + `--process-timeout=${timeout}ms`, + '--report-on-process-timeout', + fixtures.path('process-timeout', 'blocked-worker.js'), + marker, + ], { cwd: tmpdir.path, encoding: 'utf8' }); + + const reports = helper.findReports(child.pid, tmpdir.path); + assert.strictEqual(reports.length, 1, child.stderr); + helper.validate(reports[0], [ + ['header.event', 'Process timed out (--process-timeout)'], + ['header.trigger', 'ProcessTimeout'], + ]); + report = JSON.parse(fs.readFileSync(reports[0], 'utf8')); + if ((fs.existsSync(marker) && report.workers.length === 0) || + timeout >= common.platformTimeout(16000)) { + break; + } +} + +assert.strictEqual(child.signal, null); +assert.strictEqual(child.status, 124, child.stderr); +assert.match(child.stderr, /^ {4}Worker \(thread 1, name 'blocked'\)$/m); +// The report is completed before the process is forced to exit, so the +// message about that is printed on its own line. +assert.match(child.stderr, new RegExp( + '^Writing Node\\.js report to file: report\\.\\S+\\.json\\r?\\n' + + 'Node\\.js report completed\\r?\\n' + + '\\(node:\\d+\\) The process did not finish exiting within 5000ms after ' + + '--process-timeout expired\\. Forcing exit\\.$', 'm')); +assert.deepStrictEqual(report.workers, []); diff --git a/test/report/test-report-process-timeout.js b/test/report/test-report-process-timeout.js index 367ab9f5d450..2ff049443ae0 100644 --- a/test/report/test-report-process-timeout.js +++ b/test/report/test-report-process-timeout.js @@ -6,7 +6,6 @@ const common = require('../common'); const assert = require('assert'); const fs = require('fs'); const { spawnSync } = require('child_process'); -const fixtures = require('../common/fixtures'); const helper = require('../common/report'); const tmpdir = require('../common/tmpdir'); @@ -46,49 +45,3 @@ helper.validate(reports[0], [ const report = JSON.parse(fs.readFileSync(reports[0], 'utf8')); assert.match(report.javascriptStack.stack[0], /^at spin \(\[eval\]:1:\d+\)$/); - -{ - // A Worker thread that is blocked in a synchronous native call cannot - // provide its part of the report. It is left out, so that the report is - // completed before the process is forced to exit. - // If the Worker thread was not blocked yet when the deadline expired, which - // the fixture signals by creating a file, it is included in the report. Run - // the fixture again with a longer timeout in that case. - let child; - let report; - for (let timeout = common.platformTimeout(1000); ; timeout *= 2) { - // Child processes of a previous attempt may still be running, so use a - // different file for each attempt. - const marker = tmpdir.resolve(`blocked-worker.${timeout}.ready`); - child = spawnSync(process.execPath, [ - `--process-timeout=${timeout}ms`, - '--report-on-process-timeout', - fixtures.path('process-timeout', 'blocked-worker.js'), - marker, - ], { cwd: tmpdir.path, encoding: 'utf8' }); - - const reports = helper.findReports(child.pid, tmpdir.path); - assert.strictEqual(reports.length, 1, child.stderr); - helper.validate(reports[0], [ - ['header.event', 'Process timed out (--process-timeout)'], - ['header.trigger', 'ProcessTimeout'], - ]); - report = JSON.parse(fs.readFileSync(reports[0], 'utf8')); - if ((fs.existsSync(marker) && report.workers.length === 0) || - timeout >= common.platformTimeout(16000)) { - break; - } - } - - assert.strictEqual(child.signal, null); - assert.strictEqual(child.status, 124, child.stderr); - assert.match(child.stderr, /^ {4}Worker \(thread 1, name 'blocked'\)$/m); - // The report is completed before the process is forced to exit, so the - // message about that is printed on its own line. - assert.match(child.stderr, new RegExp( - '^Writing Node\\.js report to file: report\\.\\S+\\.json\\r?\\n' + - 'Node\\.js report completed\\r?\\n' + - '\\(node:\\d+\\) The process did not finish exiting within 5000ms after ' + - '--process-timeout expired\\. Forcing exit\\.$', 'm')); - assert.deepStrictEqual(report.workers, []); -} From f9a6e699b56e9416e141e320c2dc27bb5c839099 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Thu, 1 Oct 2026 08:16:52 +0200 Subject: [PATCH 2/3] test: deflake process timeout report cleanup The grandchild can briefly outlive the timed-out process while it waits to detect its parent's exit. Give it a working directory outside the test's temporary directory so it does not block cleanup on Windows. Signed-off-by: Filip Skokan Assisted-by: Codex --- test/fixtures/process-timeout/blocked-worker.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/fixtures/process-timeout/blocked-worker.js b/test/fixtures/process-timeout/blocked-worker.js index 010ba8dabada..3aa7770e8551 100644 --- a/test/fixtures/process-timeout/blocked-worker.js +++ b/test/fixtures/process-timeout/blocked-worker.js @@ -10,6 +10,9 @@ const script = path.join(__dirname, 'wait-for-parent.js'); const args = [script, String(process.pid), process.argv[2]]; new Worker(` require('child_process').execFileSync(process.execPath, ${JSON.stringify(args)}, { + // The child can briefly outlive the parent. Do not keep the test's + // temporary directory open, which prevents cleanup on Windows. + cwd: ${JSON.stringify(__dirname)}, stdio: 'ignore', }); `, { eval: true, name: 'blocked' }); From a8fb011e1b49e4be509cad89da60b62bc69852bf Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Thu, 1 Oct 2026 08:16:55 +0200 Subject: [PATCH 3/3] test: mark worker init failure flaky on SmartOS Track the descriptor-dependent V8 entropy failure until the libuv update in #66282 removes the dependency on opening /dev/urandom. Signed-off-by: Filip Skokan Assisted-by: Codex --- test/parallel/parallel.status | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/parallel/parallel.status b/test/parallel/parallel.status index 15fc53543b03..6ad0e2b29f9d 100644 --- a/test/parallel/parallel.status +++ b/test/parallel/parallel.status @@ -38,6 +38,8 @@ test-crypto-keygen: SKIP test-debugger-heap-profiler: PASS, FLAKY [$system==solaris] # Also applies to SmartOS +# https://github.com/nodejs/node/pull/66282#issuecomment-5833089308 +test-worker-init-failure: PASS, FLAKY # https://github.com/nodejs/node/issues/43457 test-domain-no-error-handler-abort-on-uncaught-0: PASS, FLAKY test-domain-no-error-handler-abort-on-uncaught-1: PASS,FLAKY