From 18146fc8c1062333d63db3609591531e7c3e9a7e Mon Sep 17 00:00:00 2001 From: cjihrig Date: Thu, 16 Feb 2023 17:58:37 -0500 Subject: [PATCH] test_runner: handle errors not bound to tests This commit addresses a previously untested branch of the code. It is possible when using the test runner that an error occurs outside of a test. In this case, the test runner would simply rethrow the error. This commit updates the logic to handle the error in the same fashion as other uncaughtExceptions. PR-URL: https://github.com/nodejs/node/pull/46962 Reviewed-By: Moshe Atlow Reviewed-By: Yagiz Nizipli --- lib/internal/test_runner/harness.js | 26 +++++++++++-------- test/message/test_runner_output.js | 6 +++++ test/message/test_runner_output.out | 1 + test/message/test_runner_output_cli.out | 1 + .../test_runner_output_spec_reporter.out | 1 + 5 files changed, 24 insertions(+), 11 deletions(-) diff --git a/lib/internal/test_runner/harness.js b/lib/internal/test_runner/harness.js index 285dd96d139653..26f5a6ef49142c 100644 --- a/lib/internal/test_runner/harness.js +++ b/lib/internal/test_runner/harness.js @@ -41,17 +41,21 @@ function createProcessEventHandler(eventName, rootTest) { // Check if this error is coming from a test. If it is, fail the test. const test = testResources.get(executionAsyncId()); - if (!test) { - throw err; - } - - if (test.finished) { - // If the test is already finished, report this as a top level - // diagnostic since this is a malformed test. - const msg = `Warning: Test "${test.name}" generated asynchronous ` + - 'activity after the test ended. This activity created the error ' + - `"${err}" and would have caused the test to fail, but instead ` + - `triggered an ${eventName} event.`; + if (!test || test.finished) { + // If the test is already finished or the resource that created the error + // is not mapped to a Test, report this as a top level diagnostic. + let msg; + + if (test) { + msg = `Warning: Test "${test.name}" generated asynchronous ` + + 'activity after the test ended. This activity created the error ' + + `"${err}" and would have caused the test to fail, but instead ` + + `triggered an ${eventName} event.`; + } else { + msg = 'Warning: A resource generated asynchronous activity after ' + + `the test ended. This activity created the error "${err}" which ` + + `triggered an ${eventName} event, caught by the test runner.`; + } rootTest.diagnostic(msg); process.exitCode = kGenericUserError; diff --git a/test/message/test_runner_output.js b/test/message/test_runner_output.js index 593f069d8f4606..a53ded3dfde3ac 100644 --- a/test/message/test_runner_output.js +++ b/test/message/test_runner_output.js @@ -383,3 +383,9 @@ test('unfinished test with unhandledRejection', async () => { setTimeout(() => Promise.reject(new Error('bar'))); }); }); + +// Verify that uncaught exceptions outside of any tests are handled after the +// test harness has finished bootstrapping itself. +setImmediate(() => { + throw new Error('uncaught from outside of a test'); +}); diff --git a/test/message/test_runner_output.out b/test/message/test_runner_output.out index 2609833304e246..b6f254708010e9 100644 --- a/test/message/test_runner_output.out +++ b/test/message/test_runner_output.out @@ -637,6 +637,7 @@ not ok 65 - invalid subtest fail 1..65 # Warning: Test "unhandled rejection - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: rejected from unhandled rejection fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. # Warning: Test "async unhandled rejection - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: rejected from async unhandled rejection fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. +# Warning: A resource generated asynchronous activity after the test ended. This activity created the error "Error: uncaught from outside of a test" which triggered an uncaughtException event, caught by the test runner. # Warning: Test "immediate throw - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: thrown from immediate throw fail" and would have caused the test to fail, but instead triggered an uncaughtException event. # Warning: Test "immediate reject - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: rejected from immediate reject fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. # Warning: Test "callback called twice in different ticks" generated asynchronous activity after the test ended. This activity created the error "Error [ERR_TEST_FAILURE]: callback invoked multiple times" and would have caused the test to fail, but instead triggered an uncaughtException event. diff --git a/test/message/test_runner_output_cli.out b/test/message/test_runner_output_cli.out index 72957397c05454..3baeb534704b11 100644 --- a/test/message/test_runner_output_cli.out +++ b/test/message/test_runner_output_cli.out @@ -636,6 +636,7 @@ not ok 65 - invalid subtest fail ... # Warning: Test "unhandled rejection - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: rejected from unhandled rejection fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. # Warning: Test "async unhandled rejection - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: rejected from async unhandled rejection fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. +# Warning: A resource generated asynchronous activity after the test ended. This activity created the error "Error: uncaught from outside of a test" which triggered an uncaughtException event, caught by the test runner. # Warning: Test "immediate throw - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: thrown from immediate throw fail" and would have caused the test to fail, but instead triggered an uncaughtException event. # Warning: Test "immediate reject - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: rejected from immediate reject fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. # Warning: Test "callback called twice in different ticks" generated asynchronous activity after the test ended. This activity created the error "Error [ERR_TEST_FAILURE]: callback invoked multiple times" and would have caused the test to fail, but instead triggered an uncaughtException event. diff --git a/test/message/test_runner_output_spec_reporter.out b/test/message/test_runner_output_spec_reporter.out index 591dc9a76d64d0..3ff9aefe7a42ce 100644 --- a/test/message/test_runner_output_spec_reporter.out +++ b/test/message/test_runner_output_spec_reporter.out @@ -270,6 +270,7 @@ Warning: Test "unhandled rejection - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: rejected from unhandled rejection fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. Warning: Test "async unhandled rejection - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: rejected from async unhandled rejection fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. + Warning: A resource generated asynchronous activity after the test ended. This activity created the error "Error: uncaught from outside of a test" which triggered an uncaughtException event, caught by the test runner. Warning: Test "immediate throw - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: thrown from immediate throw fail" and would have caused the test to fail, but instead triggered an uncaughtException event. Warning: Test "immediate reject - passes but warns" generated asynchronous activity after the test ended. This activity created the error "Error: rejected from immediate reject fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. Warning: Test "callback called twice in different ticks" generated asynchronous activity after the test ended. This activity created the error "Error [ERR_TEST_FAILURE]: callback invoked multiple times" and would have caused the test to fail, but instead triggered an uncaughtException event.