diff --git a/lib/internal/test_runner/harness.js b/lib/internal/test_runner/harness.js index e5abc8bc12296f..1bdd6e99ed1c3b 100644 --- a/lib/internal/test_runner/harness.js +++ b/lib/internal/test_runner/harness.js @@ -75,15 +75,14 @@ function setup(root) { const rejectionHandler = createProcessEventHandler('unhandledRejection', root); - process.on('uncaughtException', exceptionHandler); - process.on('unhandledRejection', rejectionHandler); - process.on('beforeExit', () => { + const exitHandler = () => { root.postRun(); let passCount = 0; let failCount = 0; let skipCount = 0; let todoCount = 0; + let cancelledCount = 0; for (let i = 0; i < root.subtests.length; i++) { const test = root.subtests[i]; @@ -94,6 +93,8 @@ function setup(root) { skipCount++; } else if (test.isTodo) { todoCount++; + } else if (test.cancelled) { + cancelledCount++; } else if (!test.passed) { failCount++; } else { @@ -110,6 +111,7 @@ function setup(root) { root.reporter.diagnostic(root.indent, `tests ${root.subtests.length}`); root.reporter.diagnostic(root.indent, `pass ${passCount}`); root.reporter.diagnostic(root.indent, `fail ${failCount}`); + root.reporter.diagnostic(root.indent, `cancelled ${cancelledCount}`); root.reporter.diagnostic(root.indent, `skipped ${skipCount}`); root.reporter.diagnostic(root.indent, `todo ${todoCount}`); root.reporter.diagnostic(root.indent, `duration_ms ${process.uptime()}`); @@ -119,10 +121,21 @@ function setup(root) { process.removeListener('unhandledRejection', rejectionHandler); process.removeListener('uncaughtException', exceptionHandler); - if (failCount > 0) { + if (failCount > 0 || cancelledCount > 0) { process.exitCode = 1; } - }); + }; + + const terminationHandler = () => { + exitHandler(); + process.exit(); + }; + + process.on('uncaughtException', exceptionHandler); + process.on('unhandledRejection', rejectionHandler); + process.on('beforeExit', exitHandler); + process.on('SIGINT', terminationHandler); + process.on('SIGTERM', terminationHandler); root.reporter.pipe(process.stdout); root.reporter.version(); diff --git a/test/message/test_runner_desctibe_it.out b/test/message/test_runner_desctibe_it.out index 8479837d333e7b..f4968b37c63eb0 100644 --- a/test/message/test_runner_desctibe_it.out +++ b/test/message/test_runner_desctibe_it.out @@ -509,6 +509,7 @@ not ok 54 - invalid subtest fail # tests 54 # pass 23 # fail 17 +# cancelled 0 # skipped 9 # todo 5 # duration_ms * diff --git a/test/message/test_runner_no_refs.out b/test/message/test_runner_no_refs.out index 078ab7c390e488..63b79cd57d777f 100644 --- a/test/message/test_runner_no_refs.out +++ b/test/message/test_runner_no_refs.out @@ -23,7 +23,8 @@ not ok 1 - does not keep event loop alive 1..1 # tests 1 # pass 0 -# fail 1 +# fail 0 +# cancelled 1 # skipped 0 # todo 0 # duration_ms * diff --git a/test/message/test_runner_only_tests.out b/test/message/test_runner_only_tests.out index b65958b010f8ad..c471a5284e7ebc 100644 --- a/test/message/test_runner_only_tests.out +++ b/test/message/test_runner_only_tests.out @@ -120,6 +120,7 @@ ok 11 - only = true, with subtests # tests 11 # pass 1 # fail 0 +# cancelled 0 # skipped 10 # todo 0 # duration_ms * diff --git a/test/message/test_runner_output.out b/test/message/test_runner_output.out index 2f6e2502749068..bada2fdacae9a9 100644 --- a/test/message/test_runner_output.out +++ b/test/message/test_runner_output.out @@ -582,6 +582,7 @@ not ok 57 - invalid subtest fail # tests 57 # pass 24 # fail 18 +# cancelled 0 # skipped 10 # todo 5 # duration_ms * diff --git a/test/message/test_runner_unresolved_promise.out b/test/message/test_runner_unresolved_promise.out index 58b3a4ed89a616..d4e868cc9ef3c3 100644 --- a/test/message/test_runner_unresolved_promise.out +++ b/test/message/test_runner_unresolved_promise.out @@ -27,7 +27,8 @@ not ok 3 - fail 1..3 # tests 3 # pass 1 -# fail 2 +# fail 0 +# cancelled 2 # skipped 0 # todo 0 # duration_ms * diff --git a/test/parallel/test-runner-exit-code.js b/test/parallel/test-runner-exit-code.js index 0e72f77783e9a9..af9e29372e5307 100644 --- a/test/parallel/test-runner-exit-code.js +++ b/test/parallel/test-runner-exit-code.js @@ -1,7 +1,8 @@ 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const { spawnSync } = require('child_process'); +const { setTimeout } = require('timers/promises'); if (process.argv[2] === 'child') { const test = require('node:test'); @@ -10,12 +11,18 @@ if (process.argv[2] === 'child') { test('passing test', () => { assert.strictEqual(true, true); }); - } else { + } else if (process.argv[3] === 'fail') { assert.strictEqual(process.argv[3], 'fail'); test('failing test', () => { assert.strictEqual(true, false); }); - } + } else if (process.argv[3] === 'never_ends') { + assert.strictEqual(process.argv[3], 'never_ends'); + test('never ending test', () => { + return setTimeout(100_000_000); + }); + process.kill(process.pid, 'SIGINT'); + } else assert.fail('unreachable'); } else { let child = spawnSync(process.execPath, [__filename, 'child', 'pass']); assert.strictEqual(child.status, 0); @@ -24,4 +31,15 @@ if (process.argv[2] === 'child') { child = spawnSync(process.execPath, [__filename, 'child', 'fail']); assert.strictEqual(child.status, 1); assert.strictEqual(child.signal, null); + + child = spawnSync(process.execPath, [__filename, 'child', 'never_ends']); + assert.strictEqual(child.status, 1); + assert.strictEqual(child.signal, null); + if (common.isWindows) { + common.printSkipMessage('signals are not supported in windows'); + } else { + const stdout = child.stdout.toString(); + assert.match(stdout, /not ok 1 - never ending test/); + assert.match(stdout, /# cancelled 1/); + } }