| import * as common from '../common/index.mjs'; |
| import * as fixtures from '../common/fixtures.mjs'; |
| import assert from 'node:assert'; |
| import { describe, it } from 'node:test'; |
| import { writeFileSync, readFileSync } from 'node:fs'; |
| import { setTimeout } from 'node:timers/promises'; |
| import { NodeInstance } from '../common/inspector-helper.js'; |
| |
| |
| if (common.isIBMi) |
| common.skip('IBMi does not support `fs.watch()`'); |
| |
| common.skipIfInspectorDisabled(); |
| |
| let gettingDebuggedPid = false; |
| |
| async function getDebuggedPid(instance, waitForLog = true) { |
| gettingDebuggedPid = true; |
| const session = await instance.connectInspectorSession(); |
| await session.send({ method: 'Runtime.enable' }); |
| if (waitForLog) { |
| await session.waitForConsoleOutput('log', 'safe to debug now'); |
| } |
| const { value: innerPid } = (await session.send({ |
| 'method': 'Runtime.evaluate', 'params': { 'expression': 'process.pid' } |
| })).result; |
| session.disconnect(); |
| gettingDebuggedPid = false; |
| return innerPid; |
| } |
| |
| function restart(file) { |
| writeFileSync(file, readFileSync(file)); |
| const interval = setInterval(() => { |
| if (!gettingDebuggedPid) { |
| writeFileSync(file, readFileSync(file)); |
| } |
| }, common.platformTimeout(500)); |
| return () => clearInterval(interval); |
| } |
| |
| describe('watch mode - inspect', () => { |
| it('should start debugger on inner process', async () => { |
| const file = fixtures.path('watch-mode/inspect.js'); |
| const instance = new NodeInstance(['--inspect=0', '--watch'], undefined, file); |
| let stderr = ''; |
| const stdout = []; |
| instance.on('stderr', (data) => { stderr += data; }); |
| instance.on('stdout', (data) => { stdout.push(data); }); |
| |
| const pids = [instance.pid]; |
| pids.push(await getDebuggedPid(instance)); |
| instance.resetPort(); |
| const stopRestarting = restart(file); |
| pids.push(await getDebuggedPid(instance)); |
| stopRestarting(); |
| |
| await setTimeout(common.platformTimeout(500)); |
| await instance.kill(); |
| |
| // There should be a process per restart and one per parent process. |
| // Message about Debugger should appear once per restart. |
| // On some systems restart can happen multiple times. |
| const restarts = stdout.filter((line) => line === 'safe to debug now').length; |
| assert.ok(stderr.match(/Debugger listening on ws:\/\//g).length >= restarts); |
| assert.ok(new Set(pids).size >= restarts + 1); |
| }); |
| |
| it('should prevent attaching debugger with SIGUSR1 to outer process', { skip: common.isWindows }, async () => { |
| const file = fixtures.path('watch-mode/inspect_with_signal.js'); |
| const instance = new NodeInstance(['--inspect-port=0', '--watch'], undefined, file); |
| let stderr = ''; |
| instance.on('stderr', (data) => { stderr += data; }); |
| |
| const loggedPid = await new Promise((resolve) => { |
| instance.on('stdout', (data) => { |
| const matches = data.match(/pid is (\d+)/); |
| if (matches) resolve(Number(matches[1])); |
| }); |
| }); |
| |
| |
| process.kill(instance.pid, 'SIGUSR1'); |
| process.kill(loggedPid, 'SIGUSR1'); |
| const debuggedPid = await getDebuggedPid(instance, false); |
| |
| await instance.kill(); |
| |
| // Message about Debugger should only appear once in inner process. |
| assert.strictEqual(stderr.match(/Debugger listening on ws:\/\//g).length, 1); |
| assert.strictEqual(loggedPid, debuggedPid); |
| }); |
| }); |