Files
node/test/parallel/test-debugger-run-restart-init.js
T
Trivikram Kamat c2f20136b1 debugger: await initialization after run and restart
Ensure the debugger CLI waits for post-connect initialization before
resolving the run and restart commands. This prevents tests and users
from observing a prompt before Runtime.runIfWaitingForDebugger() has
released the new debuggee.

Refs: https://github.com/nodejs/node/issues/61762

Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com>
Assisted-by: openai:gpt-5.5
PR-URL: https://github.com/nodejs/node/pull/63607
Refs: https://github.com/nodejs/node/issues/61762
Reviewed-By: Daijiro Wachi <daijiro.wachi@gmail.com>
2026-06-15 19:01:19 +00:00

120 lines
3.0 KiB
JavaScript

// Flags: --expose-internals
'use strict';
const common = require('../common');
common.skipIfInspectorDisabled();
const assert = require('assert');
const { EventEmitter } = require('events');
const { PassThrough } = require('stream');
const createRepl = require('internal/debugger/inspect_repl');
function createGate() {
let resolve;
const promise = new Promise((res) => {
resolve = res;
});
return { promise, resolve };
}
function createAgent(domain, calls, gates) {
const agent = new EventEmitter();
const method = (name) => async () => {
calls.push(`${domain}.${name}`);
if (domain === 'Runtime' && name === 'runIfWaitingForDebugger') {
const gate = gates.shift();
if (gate) {
await gate.promise;
}
}
};
agent.enable = method('enable');
agent.setSamplingInterval = method('setSamplingInterval');
agent.setAsyncCallStackDepth = method('setAsyncCallStackDepth');
agent.setBlackboxPatterns = method('setBlackboxPatterns');
agent.setPauseOnExceptions = method('setPauseOnExceptions');
agent.runIfWaitingForDebugger = method('runIfWaitingForDebugger');
return agent;
}
function evalCommand(repl, command) {
return new Promise((resolve, reject) => {
repl.eval(
command,
repl.context,
'debugger-repl-test',
(error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
},
);
});
}
async function assertCommandWaitsForInit(repl, command, gate, calls) {
let settled = false;
const promise = evalCommand(repl, command).then(() => {
settled = true;
});
await new Promise(setImmediate);
assert.strictEqual(
settled,
false,
`${command} resolved before post-connect initialization completed: ${calls}`,
);
gate.resolve();
await promise;
assert.strictEqual(settled, true);
}
(async () => {
const calls = [];
const runGate = createGate();
const restartGate = createGate();
const gates = [null, runGate, restartGate];
const inspector = {
client: new EventEmitter(),
domainNames: ['Debugger', 'HeapProfiler', 'Profiler', 'Runtime'],
stdin: new PassThrough(),
stdout: new PassThrough(),
run: common.mustCall(async () => {
calls.push('inspector.run');
}, 2),
suspendReplWhile(fn) {
return fn();
},
};
for (const domain of inspector.domainNames) {
inspector[domain] = createAgent(domain, calls, gates);
}
const repl = await createRepl(inspector)();
await assertCommandWaitsForInit(repl, 'run', runGate, calls);
await assertCommandWaitsForInit(repl, 'restart', restartGate, calls);
assert.deepStrictEqual(
calls.filter((call) => (
call === 'inspector.run' ||
call === 'Runtime.runIfWaitingForDebugger'
)),
[
'Runtime.runIfWaitingForDebugger',
'inspector.run',
'Runtime.runIfWaitingForDebugger',
'inspector.run',
'Runtime.runIfWaitingForDebugger',
],
);
repl.close();
})().then(common.mustCall());