1import * as common from '../common/index.mjs'; 2import * as fixtures from '../common/fixtures.mjs'; 3import assert from 'node:assert'; 4import { describe, it } from 'node:test'; 5import { writeFileSync, readFileSync } from 'node:fs'; 6import { setTimeout } from 'node:timers/promises'; 7import { NodeInstance } from '../common/inspector-helper.js'; 8 9 10if (common.isIBMi) 11 common.skip('IBMi does not support `fs.watch()`'); 12 13common.skipIfInspectorDisabled(); 14 15let gettingDebuggedPid = false; 16 17async function getDebuggedPid(instance, waitForLog = true) { 18 gettingDebuggedPid = true; 19 const session = await instance.connectInspectorSession(); 20 await session.send({ method: 'Runtime.enable' }); 21 if (waitForLog) { 22 await session.waitForConsoleOutput('log', 'safe to debug now'); 23 } 24 const { value: innerPid } = (await session.send({ 25 'method': 'Runtime.evaluate', 'params': { 'expression': 'process.pid' } 26 })).result; 27 session.disconnect(); 28 gettingDebuggedPid = false; 29 return innerPid; 30} 31 32function restart(file) { 33 writeFileSync(file, readFileSync(file)); 34 const interval = setInterval(() => { 35 if (!gettingDebuggedPid) { 36 writeFileSync(file, readFileSync(file)); 37 } 38 }, common.platformTimeout(500)); 39 return () => clearInterval(interval); 40} 41 42describe('watch mode - inspect', () => { 43 it('should start debugger on inner process', async () => { 44 const file = fixtures.path('watch-mode/inspect.js'); 45 const instance = new NodeInstance(['--inspect=0', '--watch'], undefined, file); 46 let stderr = ''; 47 const stdout = []; 48 instance.on('stderr', (data) => { stderr += data; }); 49 instance.on('stdout', (data) => { stdout.push(data); }); 50 51 const pids = [instance.pid]; 52 pids.push(await getDebuggedPid(instance)); 53 instance.resetPort(); 54 const stopRestarting = restart(file); 55 pids.push(await getDebuggedPid(instance)); 56 stopRestarting(); 57 58 await setTimeout(common.platformTimeout(500)); 59 await instance.kill(); 60 61 // There should be a process per restart and one per parent process. 62 // Message about Debugger should appear once per restart. 63 // On some systems restart can happen multiple times. 64 const restarts = stdout.filter((line) => line === 'safe to debug now').length; 65 assert.ok(stderr.match(/Debugger listening on ws:\/\//g).length >= restarts); 66 assert.ok(new Set(pids).size >= restarts + 1); 67 }); 68 69 it('should prevent attaching debugger with SIGUSR1 to outer process', { skip: common.isWindows }, async () => { 70 const file = fixtures.path('watch-mode/inspect_with_signal.js'); 71 const instance = new NodeInstance(['--inspect-port=0', '--watch'], undefined, file); 72 let stderr = ''; 73 instance.on('stderr', (data) => { stderr += data; }); 74 75 const loggedPid = await new Promise((resolve) => { 76 instance.on('stdout', (data) => { 77 const matches = data.match(/pid is (\d+)/); 78 if (matches) resolve(Number(matches[1])); 79 }); 80 }); 81 82 83 process.kill(instance.pid, 'SIGUSR1'); 84 process.kill(loggedPid, 'SIGUSR1'); 85 const debuggedPid = await getDebuggedPid(instance, false); 86 87 await instance.kill(); 88 89 // Message about Debugger should only appear once in inner process. 90 assert.strictEqual(stderr.match(/Debugger listening on ws:\/\//g).length, 1); 91 assert.strictEqual(loggedPid, debuggedPid); 92 }); 93}); 94