From 57c40c22496b7a58c34f374788fe7c6358e1a86a Mon Sep 17 00:00:00 2001 From: Moshe Atlow Date: Mon, 12 Sep 2022 16:37:20 +0300 Subject: [PATCH 1/3] test_runner: include stack of uncaught exceptions --- lib/internal/test_runner/tap_stream.js | 5 +++- test/message/test_runner_describe_it.out | 1 + test/message/test_runner_output.js | 12 +++++++++ test/message/test_runner_output.out | 33 +++++++++++++++++++++--- 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/lib/internal/test_runner/tap_stream.js b/lib/internal/test_runner/tap_stream.js index 2d7b6e7b68eb15..094cccc5c4f1da 100644 --- a/lib/internal/test_runner/tap_stream.js +++ b/lib/internal/test_runner/tap_stream.js @@ -187,7 +187,10 @@ function jsToYaml(indent, name, value) { // If the ERR_TEST_FAILURE came from an error provided by user code, // then try to unwrap the original error message and stack. - if (code === 'ERR_TEST_FAILURE' && (failureType === kTestCodeFailure || failureType === kHookFailure)) { + if (code === 'ERR_TEST_FAILURE' && ( + failureType === kTestCodeFailure || failureType === kHookFailure || + failureType === 'uncaughtException' || failureType === 'unhandledRejection' + )) { errStack = cause?.stack ?? errStack; errCode = cause?.code ?? errCode; if (failureType === kTestCodeFailure) { diff --git a/test/message/test_runner_describe_it.out b/test/message/test_runner_describe_it.out index cef7f8c94a8098..a1fa5fc1faaf04 100644 --- a/test/message/test_runner_describe_it.out +++ b/test/message/test_runner_describe_it.out @@ -418,6 +418,7 @@ not ok 49 - callback async throw code: 'ERR_TEST_FAILURE' stack: |- * + * ... # Subtest: callback async throw after done ok 50 - callback async throw after done diff --git a/test/message/test_runner_output.js b/test/message/test_runner_output.js index 8fce194f56d2b7..064527832ac400 100644 --- a/test/message/test_runner_output.js +++ b/test/message/test_runner_output.js @@ -371,3 +371,15 @@ test('rejected thenable', () => { }, }; }); + +test('uncaughtException', async () => { + await new Promise(() => { + setTimeout(() => { throw new Error('foo'); }); + }); +}); + +test('unhandledRejection', async () => { + await new Promise(() => { + setTimeout(() => Promise.reject(new Error('bar'))); + }); +}); diff --git a/test/message/test_runner_output.out b/test/message/test_runner_output.out index 49a19fe302bbb0..677da5a6aff142 100644 --- a/test/message/test_runner_output.out +++ b/test/message/test_runner_output.out @@ -463,6 +463,7 @@ not ok 51 - callback async throw code: 'ERR_TEST_FAILURE' stack: |- * + * ... # Subtest: callback async throw after done ok 52 - callback async throw after done @@ -601,8 +602,32 @@ not ok 62 - rejected thenable error: 'custom error' code: 'ERR_TEST_FAILURE' ... +# Subtest: uncaughtException +not ok 63 - uncaughtException + --- + duration_ms: * + failureType: 'uncaughtException' + error: 'foo' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + ... +# Subtest: unhandledRejection +not ok 64 - unhandledRejection + --- + duration_ms: * + failureType: 'unhandledRejection' + error: 'bar' + code: 'ERR_TEST_FAILURE' + stack: |- + * + * + * + ... # Subtest: invalid subtest fail -not ok 63 - invalid subtest fail +not ok 65 - invalid subtest fail --- duration_ms: * failureType: 'parentAlreadyFinished' @@ -611,16 +636,16 @@ not ok 63 - invalid subtest fail stack: |- * ... -1..63 +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: 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. # Warning: Test "callback async throw after done" generated asynchronous activity after the test ended. This activity created the error "Error: thrown from callback async throw after done" and would have caused the test to fail, but instead triggered an uncaughtException event. -# tests 63 +# tests 65 # pass 27 -# fail 19 +# fail 21 # cancelled 2 # skipped 10 # todo 5 From ce9c1ff9ce945f2008c3634c56c705c18a4b0ee6 Mon Sep 17 00:00:00 2001 From: Moshe Atlow Date: Mon, 12 Sep 2022 17:29:20 +0300 Subject: [PATCH 2/3] CR --- lib/internal/test_runner/tap_stream.js | 7 ++----- lib/internal/test_runner/test.js | 4 +++- test/message/test_runner_output.js | 4 ++-- test/message/test_runner_output.out | 8 ++++---- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/internal/test_runner/tap_stream.js b/lib/internal/test_runner/tap_stream.js index 094cccc5c4f1da..ebd825a3dc9236 100644 --- a/lib/internal/test_runner/tap_stream.js +++ b/lib/internal/test_runner/tap_stream.js @@ -173,7 +173,7 @@ function jsToYaml(indent, name, value) { } if (isErrorObj) { - const { kTestCodeFailure, kHookFailure } = lazyLoadTest(); + const { kTestCodeFailure, kUnwrapErrors } = lazyLoadTest(); const { cause, code, @@ -187,10 +187,7 @@ function jsToYaml(indent, name, value) { // If the ERR_TEST_FAILURE came from an error provided by user code, // then try to unwrap the original error message and stack. - if (code === 'ERR_TEST_FAILURE' && ( - failureType === kTestCodeFailure || failureType === kHookFailure || - failureType === 'uncaughtException' || failureType === 'unhandledRejection' - )) { + if (code === 'ERR_TEST_FAILURE' && kUnwrapErrors.has(failureType)) { errStack = cause?.stack ?? errStack; errCode = cause?.code ?? errCode; if (failureType === kTestCodeFailure) { diff --git a/lib/internal/test_runner/test.js b/lib/internal/test_runner/test.js index ffe22495f993f4..ed9cb5487e42e6 100644 --- a/lib/internal/test_runner/test.js +++ b/lib/internal/test_runner/test.js @@ -13,6 +13,7 @@ const { PromiseResolve, ReflectApply, SafeMap, + SafeSet, SafePromiseAll, SafePromiseRace, Symbol, @@ -60,6 +61,7 @@ const testOnlyFlag = !isTestRunner && getOptionValue('--test-only'); const kShouldAbort = Symbol('kShouldAbort'); const kRunHook = Symbol('kRunHook'); const kHookNames = ObjectSeal(['before', 'after', 'beforeEach', 'afterEach']); +const kUnwrapErrors = new SafeSet([kTestCodeFailure, kHookFailure, 'uncaughtException', 'unhandledRejection']); function stopTest(timeout, signal) { @@ -726,9 +728,9 @@ module.exports = { ItTest, kCancelledByParent, kDefaultIndent, - kHookFailure, kSubtestsFailed, kTestCodeFailure, + kUnwrapErrors, Suite, Test, }; diff --git a/test/message/test_runner_output.js b/test/message/test_runner_output.js index 064527832ac400..2a71cd3e16e143 100644 --- a/test/message/test_runner_output.js +++ b/test/message/test_runner_output.js @@ -372,13 +372,13 @@ test('rejected thenable', () => { }; }); -test('uncaughtException', async () => { +test('unfinished test with uncaughtException', async () => { await new Promise(() => { setTimeout(() => { throw new Error('foo'); }); }); }); -test('unhandledRejection', async () => { +test('unfinished test with unhandledRejection', async () => { await new Promise(() => { setTimeout(() => Promise.reject(new Error('bar'))); }); diff --git a/test/message/test_runner_output.out b/test/message/test_runner_output.out index 677da5a6aff142..fe5698e9d031f0 100644 --- a/test/message/test_runner_output.out +++ b/test/message/test_runner_output.out @@ -602,8 +602,8 @@ not ok 62 - rejected thenable error: 'custom error' code: 'ERR_TEST_FAILURE' ... -# Subtest: uncaughtException -not ok 63 - uncaughtException +# Subtest: unfinished test with uncaughtException +not ok 63 - unfinished test with uncaughtException --- duration_ms: * failureType: 'uncaughtException' @@ -614,8 +614,8 @@ not ok 63 - uncaughtException * * ... -# Subtest: unhandledRejection -not ok 64 - unhandledRejection +# Subtest: unfinished test with unhandledRejection +not ok 64 - unfinished test with unhandledRejection --- duration_ms: * failureType: 'unhandledRejection' From e1fe810c0cd3d2843da800d84f6b31164b1e1abd Mon Sep 17 00:00:00 2001 From: Moshe Atlow Date: Mon, 12 Sep 2022 21:42:25 +0300 Subject: [PATCH 3/3] Update lib/internal/test_runner/test.js Co-authored-by: Antoine du Hamel --- lib/internal/test_runner/test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/internal/test_runner/test.js b/lib/internal/test_runner/test.js index ed9cb5487e42e6..f02c173bff35eb 100644 --- a/lib/internal/test_runner/test.js +++ b/lib/internal/test_runner/test.js @@ -61,7 +61,9 @@ const testOnlyFlag = !isTestRunner && getOptionValue('--test-only'); const kShouldAbort = Symbol('kShouldAbort'); const kRunHook = Symbol('kRunHook'); const kHookNames = ObjectSeal(['before', 'after', 'beforeEach', 'afterEach']); -const kUnwrapErrors = new SafeSet([kTestCodeFailure, kHookFailure, 'uncaughtException', 'unhandledRejection']); +const kUnwrapErrors = new SafeSet() + .add(kTestCodeFailure).add(kHookFailure) + .add('uncaughtException').add('unhandledRejection'); function stopTest(timeout, signal) {