1'use strict';
2const common = require('../common');
3common.skipIfInspectorDisabled();
4
5// Test inspector open()/close()/url() API. It uses ephemeral ports so can be
6// run safely in parallel.
7
8const assert = require('assert');
9const fork = require('child_process').fork;
10const net = require('net');
11const url = require('url');
12
13const kFirstOpen = 0;
14const kOpenWhileOpen = 1;
15const kReOpen = 2;
16
17if (process.env.BE_CHILD)
18  return beChild();
19
20const child = fork(__filename,
21                   { env: { ...process.env, BE_CHILD: 1 } });
22
23child.once('message', common.mustCall((msg) => {
24  assert.strictEqual(msg.cmd, 'started');
25
26  child.send({ cmd: 'open', args: [kFirstOpen] });
27  child.once('message', common.mustCall(firstOpen));
28}));
29
30let firstPort;
31
32function firstOpen(msg) {
33  assert.strictEqual(msg.cmd, 'url');
34  const port = url.parse(msg.url).port;
35  ping(port, common.mustSucceed(() => {
36    // Inspector is already open, and won't be reopened, so args don't matter.
37    child.send({ cmd: 'open', args: [kOpenWhileOpen] });
38    child.once('message', common.mustCall(tryToOpenWhenOpen));
39    firstPort = port;
40  }));
41}
42
43function tryToOpenWhenOpen(msg) {
44  assert.strictEqual(msg.cmd, 'url');
45  const port = url.parse(msg.url).port;
46  // Reopen didn't do anything, the port was already open, and has not changed.
47  assert.strictEqual(port, firstPort);
48  ping(port, common.mustSucceed(() => {
49    child.send({ cmd: 'close' });
50    child.once('message', common.mustCall(closeWhenOpen));
51  }));
52}
53
54function closeWhenOpen(msg) {
55  assert.strictEqual(msg.cmd, 'url');
56  assert.strictEqual(msg.url, undefined);
57  ping(firstPort, (err) => {
58    assert(err);
59    child.send({ cmd: 'close' });
60    child.once('message', common.mustCall(tryToCloseWhenClosed));
61  });
62}
63
64function tryToCloseWhenClosed(msg) {
65  assert.strictEqual(msg.cmd, 'url');
66  assert.strictEqual(msg.url, undefined);
67  child.send({ cmd: 'open', args: [kReOpen] });
68  child.once('message', common.mustCall(reopenAfterClose));
69}
70
71function reopenAfterClose(msg) {
72  assert.strictEqual(msg.cmd, 'url');
73  const port = url.parse(msg.url).port;
74  ping(port, common.mustSucceed(() => {
75    process.exit();
76  }));
77}
78
79function ping(port, callback) {
80  net.connect({ port, family: 4 })
81    .on('connect', function() { close(this); })
82    .on('error', function(err) { close(this, err); });
83
84  function close(self, err) {
85    self.end();
86    self.on('close', () => callback(err));
87  }
88}
89
90function beChild() {
91  const inspector = require('inspector');
92
93  process.send({ cmd: 'started' });
94
95  process.on('message', (msg) => {
96    if (msg.cmd === 'open') {
97      if (msg.args[0] === kFirstOpen) {
98        inspector.open(0, false, undefined);
99      } else if (msg.args[0] === kOpenWhileOpen) {
100        assert.throws(() => {
101          inspector.open(0, false, undefined);
102        }, {
103          code: 'ERR_INSPECTOR_ALREADY_ACTIVATED'
104        });
105      } else if (msg.args[0] === kReOpen) {
106        inspector.open(0, false, undefined);
107      }
108    }
109    if (msg.cmd === 'close') {
110      inspector.close();
111    }
112    process.send({ cmd: 'url', url: inspector.url() });
113  });
114}
115