Skip to content

Commit

Permalink
fix: include stack of uncaught exceptions
Browse files Browse the repository at this point in the history
PR-URL: nodejs/node#44614
Fixes: nodejs/node#44611
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
(cherry picked from commit cb7e0c59df10a42cd6930ca7f99d3acee1ce7627)
  • Loading branch information
MoLow committed Feb 2, 2023
1 parent 6755536 commit c50f844
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 10 deletions.
6 changes: 3 additions & 3 deletions lib/internal/test_runner/tap_stream.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// https://github.com/nodejs/node/blob/59527de13d39327eb3dfa8dedc92241eb40066d5/lib/internal/test_runner/tap_stream.js
// https://github.com/nodejs/node/blob/cb7e0c59df10a42cd6930ca7f99d3acee1ce7627/lib/internal/test_runner/tap_stream.js

'use strict'

Expand Down Expand Up @@ -177,7 +177,7 @@ function jsToYaml (indent, name, value) {
}

if (isErrorObj) {
const { kTestCodeFailure, kHookFailure } = lazyLoadTest()
const { kTestCodeFailure, kUnwrapErrors } = lazyLoadTest()
const {
cause,
code,
Expand All @@ -191,7 +191,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)) {
if (code === 'ERR_TEST_FAILURE' && kUnwrapErrors.has(failureType)) {
errStack = cause?.stack ?? errStack
errCode = cause?.code ?? errCode
if (failureType === kTestCodeFailure) {
Expand Down
17 changes: 15 additions & 2 deletions lib/internal/test_runner/test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// https://github.com/nodejs/node/blob/a165193c5c8e4bcfbd12b2c3f6e55a81a251c258/lib/internal/test_runner/test.js
// https://github.com/nodejs/node/blob/cb7e0c59df10a42cd6930ca7f99d3acee1ce7627/lib/internal/test_runner/test.js

'use strict'

Expand All @@ -16,6 +16,7 @@ const {
PromiseResolve,
ReflectApply,
SafeMap,
SafeSet,
SafePromiseAll,
SafePromiseRace,
Symbol
Expand Down Expand Up @@ -63,6 +64,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()
.add(kTestCodeFailure).add(kHookFailure)
.add('uncaughtException').add('unhandledRejection')

function stopTest (timeout, signal) {
if (timeout === kDefaultTimeout) {
Expand Down Expand Up @@ -725,4 +729,13 @@ class Suite extends Test {
}
}

module.exports = { kCancelledByParent, kDefaultIndent, kSubtestsFailed, kTestCodeFailure, Test, Suite, ItTest }
module.exports = {
ItTest,
kCancelledByParent,
kDefaultIndent,
kSubtestsFailed,
kTestCodeFailure,
kUnwrapErrors,
Suite,
Test
}
5 changes: 5 additions & 0 deletions test/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const stackTraceLine = /^\s+\*$/
const stackTraceEndLine = /^\s+\.\.\.$/

const nodejs14NotEmittedWarn = /^# Warning:.*\breject/
const nodejs14NotEmittedUnhandledRejection = /'unhandledRejection'/

// https://github.com/nodejs/node/blob/1aab13cad9c800f4121c1d35b554b78c1b17bdbd/test/message/testcfg.py#L53
async function IsFailureOutput (self, output) {
Expand All @@ -38,6 +39,10 @@ async function IsFailureOutput (self, output) {

// Node.js 14 doesn't emit some warnings
if (process.version.startsWith('v14.') && nodejs14NotEmittedWarn.test(line)) continue
if (process.version.startsWith('v14.') && nodejs14NotEmittedUnhandledRejection.test(line)) {
patterns.push(WAIT_FOR_ELLIPSIS)
continue
}

// Sometimes Node.js won't have any stack trace, but we would
if (stackTraceEndLine.test(line) && patterns[patterns.length - 1].toString().endsWith("code: 'ERR_TEST_FAILURE'$")) {
Expand Down
1 change: 1 addition & 0 deletions test/message/test_runner_desctibe_it.out
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,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
Expand Down
14 changes: 13 additions & 1 deletion test/message/test_runner_output.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// https://github.com/nodejs/node/blob/a3e110820ff98702e1761831e7beaf0f5f1f75e7/test/message/test_runner_output.js
// https://github.com/nodejs/node/blob/cb7e0c59df10a42cd6930ca7f99d3acee1ce7627/test/message/test_runner_output.js
// Flags: --no-warnings
'use strict'
require('../common')
Expand Down Expand Up @@ -371,3 +371,15 @@ test('rejected thenable', () => {
}
}
})

test('unfinished test with uncaughtException', async () => {
await new Promise(() => {
setTimeout(() => { throw new Error('foo') })
})
})

test('unfinished test with unhandledRejection', async () => {
await new Promise(() => {
setTimeout(() => Promise.reject(new Error('bar')))
})
})
33 changes: 29 additions & 4 deletions test/message/test_runner_output.out
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -601,8 +602,32 @@ not ok 62 - rejected thenable
error: 'custom error'
code: 'ERR_TEST_FAILURE'
...
# Subtest: unfinished test with uncaughtException
not ok 63 - unfinished test with uncaughtException
---
duration_ms: *
failureType: 'uncaughtException'
error: 'foo'
code: 'ERR_TEST_FAILURE'
stack: |-
*
*
*
...
# Subtest: unfinished test with unhandledRejection
not ok 64 - unfinished test with 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'
Expand All @@ -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
Expand Down

0 comments on commit c50f844

Please sign in to comment.