• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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