From 3dc06aef21d681ed5eb09df5ced583e415df2c8f Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Thu, 23 Apr 2026 21:16:00 +0200 Subject: [PATCH] test: normalize known inspector crash as completion This works around a pre-existing inspector issue: if the debuggee exits too quickly the inspector can segfault while tearing down. For now normalize the trailing segfault as completion to keep the CI green until the upstream bug is fixed, since it only reproduces on some slow CI machines and is not what the probe tests care about. PR-URL: https://github.com/nodejs/node/pull/62851 Refs: https://github.com/nodejs/node/issues/62765 Refs: https://github.com/nodejs/node/issues/58245 Reviewed-By: Jan Martin Reviewed-By: Chengzhong Wu Reviewed-By: Jacob Smith Reviewed-By: Filip Skokan --- test/common/debugger-probe.js | 42 +++++++++++++++++-- ...-debugger-probe-child-inspect-port-zero.js | 5 +-- ...test-debugger-probe-global-option-order.js | 5 +-- .../test-debugger-probe-json-preview.js | 8 ++-- ...test-debugger-probe-json-special-values.js | 8 ++-- test/parallel/test-debugger-probe-json.js | 5 +-- test/parallel/test-debugger-probe-miss.js | 5 +-- ...test-debugger-probe-text-special-values.js | 8 ++-- test/parallel/test-debugger-probe-text.js | 11 +++-- test/parallel/test-debugger-probe-timeout.js | 5 +-- 10 files changed, 69 insertions(+), 33 deletions(-) diff --git a/test/common/debugger-probe.js b/test/common/debugger-probe.js index 4ad693e0363..e4c7f831bab 100644 --- a/test/common/debugger-probe.js +++ b/test/common/debugger-probe.js @@ -1,5 +1,6 @@ 'use strict'; +const assert = require('assert'); const fixtures = require('./fixtures'); const path = require('path'); @@ -7,12 +8,47 @@ function debuggerFixturePath(name) { return path.relative(process.cwd(), fixtures.path('debugger', name)); } -function escapeRegex(string) { - return string.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); +// Work around a pre-existing inspector issue: if the debuggee exits too quickly +// the inspector can segfault while tearing down. For now normalize the segfault +// back to the expected terminal event (e.g. "completed" or "miss") +// until the upstream bug is fixed. +// See https://github.com/nodejs/node/issues/62765 +// https://github.com/nodejs/node/issues/58245 +const probeTargetExitSignal = 'SIGSEGV'; + +function assertProbeJson(output, expected) { + const normalized = JSON.parse(output); + const lastResult = normalized.results?.[normalized.results.length - 1]; + + if (lastResult?.event === 'error' && + lastResult.error?.code === 'probe_target_exit' && + lastResult.error?.signal === probeTargetExitSignal) { + // Log to facilitate debugging if this normalization is occurring. + console.log('Normalizing trailing SIGSEGV in JSON probe output'); + normalized.results[normalized.results.length - 1] = expected.results.at(-1); + } + + assert.deepStrictEqual(normalized, expected); +} + +function assertProbeText(output, expected) { + const signalPrefix = `Target exited with signal ${probeTargetExitSignal}`; + const idx = output.indexOf(signalPrefix); + let normalized; + if (idx !== -1) { + // Log to facilitate debugging if this normalization is occurring. + console.log('Normalizing trailing SIGSEGV in text probe output'); + const lineStart = output.lastIndexOf('\n', idx); + normalized = (lineStart === -1 ? '' : output.slice(0, lineStart)) + '\nCompleted'; + } else { + normalized = output; + } + assert.strictEqual(normalized, expected); } module.exports = { - escapeRegex, + assertProbeJson, + assertProbeText, missScript: debuggerFixturePath('probe-miss.js'), probeScript: debuggerFixturePath('probe.js'), throwScript: debuggerFixturePath('probe-throw.js'), diff --git a/test/parallel/test-debugger-probe-child-inspect-port-zero.js b/test/parallel/test-debugger-probe-child-inspect-port-zero.js index a6f03ad9f41..2abc81441ed 100644 --- a/test/parallel/test-debugger-probe-child-inspect-port-zero.js +++ b/test/parallel/test-debugger-probe-child-inspect-port-zero.js @@ -4,9 +4,8 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const assert = require('assert'); const { spawnSyncAndAssert } = require('../common/child_process'); -const { probeScript } = require('../common/debugger-probe'); +const { assertProbeJson, probeScript } = require('../common/debugger-probe'); spawnSyncAndAssert(process.execPath, [ 'inspect', @@ -18,7 +17,7 @@ spawnSyncAndAssert(process.execPath, [ probeScript, ], { stdout(output) { - assert.deepStrictEqual(JSON.parse(output), { + assertProbeJson(output, { v: 1, probes: [{ expr: 'finalValue', diff --git a/test/parallel/test-debugger-probe-global-option-order.js b/test/parallel/test-debugger-probe-global-option-order.js index 9e247c32131..19487c8f623 100644 --- a/test/parallel/test-debugger-probe-global-option-order.js +++ b/test/parallel/test-debugger-probe-global-option-order.js @@ -4,9 +4,8 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const assert = require('assert'); const { spawnSyncAndAssert } = require('../common/child_process'); -const { probeScript } = require('../common/debugger-probe'); +const { assertProbeJson, probeScript } = require('../common/debugger-probe'); spawnSyncAndAssert(process.execPath, [ 'inspect', @@ -16,7 +15,7 @@ spawnSyncAndAssert(process.execPath, [ probeScript, ], { stdout(output) { - assert.deepStrictEqual(JSON.parse(output), { + assertProbeJson(output, { v: 1, probes: [{ expr: 'finalValue', diff --git a/test/parallel/test-debugger-probe-json-preview.js b/test/parallel/test-debugger-probe-json-preview.js index 62b9edfe489..853939075aa 100644 --- a/test/parallel/test-debugger-probe-json-preview.js +++ b/test/parallel/test-debugger-probe-json-preview.js @@ -4,9 +4,11 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const assert = require('assert'); const { spawnSyncAndAssert } = require('../common/child_process'); -const { probeTypesScript } = require('../common/debugger-probe'); +const { + assertProbeJson, + probeTypesScript, +} = require('../common/debugger-probe'); const location = `${probeTypesScript}:17`; @@ -23,7 +25,7 @@ spawnSyncAndAssert(process.execPath, [ probeTypesScript, ], { stdout(output) { - assert.deepStrictEqual(JSON.parse(output), { + assertProbeJson(output, { v: 1, probes: [ { expr: 'objectValue', target: [probeTypesScript, 17] }, diff --git a/test/parallel/test-debugger-probe-json-special-values.js b/test/parallel/test-debugger-probe-json-special-values.js index ea18a33f1e9..97782c9e314 100644 --- a/test/parallel/test-debugger-probe-json-special-values.js +++ b/test/parallel/test-debugger-probe-json-special-values.js @@ -4,9 +4,11 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const assert = require('assert'); const { spawnSyncAndAssert } = require('../common/child_process'); -const { probeTypesScript } = require('../common/debugger-probe'); +const { + assertProbeJson, + probeTypesScript, +} = require('../common/debugger-probe'); const location = `${probeTypesScript}:17`; @@ -38,7 +40,7 @@ spawnSyncAndAssert(process.execPath, [ probeTypesScript, ], { stdout(output) { - assert.deepStrictEqual(JSON.parse(output), { + assertProbeJson(output, { v: 1, probes: [ { expr: 'stringValue', target: [probeTypesScript, 17] }, diff --git a/test/parallel/test-debugger-probe-json.js b/test/parallel/test-debugger-probe-json.js index e95b7ffbd72..be2288ee78d 100644 --- a/test/parallel/test-debugger-probe-json.js +++ b/test/parallel/test-debugger-probe-json.js @@ -4,9 +4,8 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const assert = require('assert'); const { spawnSyncAndAssert } = require('../common/child_process'); -const { probeScript } = require('../common/debugger-probe'); +const { assertProbeJson, probeScript } = require('../common/debugger-probe'); spawnSyncAndAssert(process.execPath, [ 'inspect', @@ -20,7 +19,7 @@ spawnSyncAndAssert(process.execPath, [ probeScript, ], { stdout(output) { - assert.deepStrictEqual(JSON.parse(output), { + assertProbeJson(output, { v: 1, probes: [ { expr: 'index', target: [probeScript, 8] }, diff --git a/test/parallel/test-debugger-probe-miss.js b/test/parallel/test-debugger-probe-miss.js index 7dc52e9a07b..2908b8092ae 100644 --- a/test/parallel/test-debugger-probe-miss.js +++ b/test/parallel/test-debugger-probe-miss.js @@ -4,9 +4,8 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const assert = require('assert'); const { spawnSyncAndAssert } = require('../common/child_process'); -const { missScript } = require('../common/debugger-probe'); +const { assertProbeJson, missScript } = require('../common/debugger-probe'); spawnSyncAndAssert(process.execPath, [ 'inspect', @@ -16,7 +15,7 @@ spawnSyncAndAssert(process.execPath, [ missScript, ], { stdout(output) { - assert.deepStrictEqual(JSON.parse(output), { + assertProbeJson(output, { v: 1, probes: [{ expr: '42', target: [missScript, 99] }], results: [{ diff --git a/test/parallel/test-debugger-probe-text-special-values.js b/test/parallel/test-debugger-probe-text-special-values.js index 17ff62ef4a0..3886eb66dae 100644 --- a/test/parallel/test-debugger-probe-text-special-values.js +++ b/test/parallel/test-debugger-probe-text-special-values.js @@ -4,9 +4,11 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const assert = require('assert'); const { spawnSyncAndAssert } = require('../common/child_process'); -const { probeTypesScript } = require('../common/debugger-probe'); +const { + assertProbeText, + probeTypesScript, +} = require('../common/debugger-probe'); const location = `${probeTypesScript}:17`; @@ -37,7 +39,7 @@ spawnSyncAndAssert(process.execPath, [ probeTypesScript, ], { stdout(output) { - assert.strictEqual(output, [ + assertProbeText(output, [ `Hit 1 at ${location}`, ' stringValue = "hello"', `Hit 1 at ${location}`, diff --git a/test/parallel/test-debugger-probe-text.js b/test/parallel/test-debugger-probe-text.js index 75be98611d3..30e77b25985 100644 --- a/test/parallel/test-debugger-probe-text.js +++ b/test/parallel/test-debugger-probe-text.js @@ -4,9 +4,8 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const assert = require('assert'); const { spawnSyncAndAssert } = require('../common/child_process'); -const { probeScript } = require('../common/debugger-probe'); +const { assertProbeText, probeScript } = require('../common/debugger-probe'); spawnSyncAndAssert(process.execPath, [ 'inspect', @@ -15,10 +14,10 @@ spawnSyncAndAssert(process.execPath, [ probeScript, ], { stdout(output) { - assert.strictEqual(output, - `Hit 1 at ${probeScript}:12\n` + - ' finalValue = 81\n' + - 'Completed'); + assertProbeText(output, + `Hit 1 at ${probeScript}:12\n` + + ' finalValue = 81\n' + + 'Completed'); }, trim: true, }); diff --git a/test/parallel/test-debugger-probe-timeout.js b/test/parallel/test-debugger-probe-timeout.js index d4728407bc5..fe641f31943 100644 --- a/test/parallel/test-debugger-probe-timeout.js +++ b/test/parallel/test-debugger-probe-timeout.js @@ -4,9 +4,8 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const assert = require('assert'); const { spawnSyncAndExit } = require('../common/child_process'); -const { timeoutScript } = require('../common/debugger-probe'); +const { assertProbeJson, timeoutScript } = require('../common/debugger-probe'); spawnSyncAndExit(process.execPath, [ 'inspect', @@ -19,7 +18,7 @@ spawnSyncAndExit(process.execPath, [ signal: null, status: 1, stdout(output) { - assert.deepStrictEqual(JSON.parse(output), { + assertProbeJson(output, { v: 1, probes: [{ expr: '1', target: [timeoutScript, 99] }], results: [{