summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorEugene Ostroukhov <eostroukhov@chromium.org>2017-07-24 09:23:04 -0700
committerEugene Ostroukhov <eostroukhov@chromium.org>2017-08-10 16:19:11 -0700
commit2296b677fb1e2ea71e86a899b028ba9e817e86ad (patch)
tree9f4685d529c5e48c67c027bb149ddb22fc47bab3 /test
parent340b3be1df9e8ade94c4767c7ff6d7c6d3003ea6 (diff)
downloadandroid-node-v8-2296b677fb1e2ea71e86a899b028ba9e817e86ad.tar.gz
android-node-v8-2296b677fb1e2ea71e86a899b028ba9e817e86ad.tar.bz2
android-node-v8-2296b677fb1e2ea71e86a899b028ba9e817e86ad.zip
inspector: rewrite inspector test helper
Helper was rewritten to rely on promises instead of manually written queue and callbacks. This simplifies the code and makes it easier to maintain and extend. PR-URL: https://github.com/nodejs/node/pull/14460 Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'test')
-rw-r--r--test/common/README.md9
-rw-r--r--test/common/index.js42
-rw-r--r--test/inspector/inspector-helper.js781
-rw-r--r--test/inspector/test-break-when-eval.js68
-rw-r--r--test/inspector/test-debug-brk-flag.js41
-rw-r--r--test/inspector/test-debug-end.js46
-rw-r--r--test/inspector/test-exception.js45
-rw-r--r--test/inspector/test-inspector-break-when-eval.js128
-rw-r--r--test/inspector/test-inspector-debug-brk.js59
-rw-r--r--test/inspector/test-inspector-exception.js64
-rw-r--r--test/inspector/test-inspector-ip-detection.js51
-rw-r--r--test/inspector/test-inspector-stop-profile-after-done.js21
-rw-r--r--test/inspector/test-inspector.js491
-rw-r--r--test/inspector/test-ip-detection.js48
-rw-r--r--test/inspector/test-not-blocked-on-idle.js28
-rw-r--r--test/inspector/test-off-no-session.js11
-rw-r--r--test/inspector/test-off-with-session-then-on.js24
-rw-r--r--test/inspector/test-port-cluster.js (renamed from test/inspector/test-inspector-port-cluster.js)0
-rw-r--r--test/inspector/test-port-zero-cluster.js (renamed from test/inspector/test-inspector-port-zero-cluster.js)0
-rw-r--r--test/inspector/test-port-zero.js (renamed from test/inspector/test-inspector-port-zero.js)0
-rw-r--r--test/inspector/test-stop-profile-after-done.js30
-rw-r--r--test/inspector/test-stops-no-file.js (renamed from test/inspector/test-inspector-stops-no-file.js)0
22 files changed, 865 insertions, 1122 deletions
diff --git a/test/common/README.md b/test/common/README.md
index aa3fcb3d4a..59b02cf52a 100644
--- a/test/common/README.md
+++ b/test/common/README.md
@@ -99,6 +99,15 @@ Tests whether `name` and `expected` are part of a raised warning.
Checks if `pathname` exists
+### fires(promise, [error], [timeoutMs])
+* promise [&lt;Promise]
+* error [&lt;String] default = 'timeout'
+* timeoutMs [&lt;Number] default = 100
+
+Returns a new promise that will propagate `promise` resolution or rejection if
+that happens within the `timeoutMs` timespan, or rejects with `error` as
+a reason otherwise.
+
### fixturesDir
* return [&lt;String>]
diff --git a/test/common/index.js b/test/common/index.js
index 54742319d2..2564b227fe 100644
--- a/test/common/index.js
+++ b/test/common/index.js
@@ -814,6 +814,32 @@ function restoreWritable(name) {
delete process[name].writeTimes;
}
+function onResolvedOrRejected(promise, callback) {
+ return promise.then((result) => {
+ callback();
+ return result;
+ }, (error) => {
+ callback();
+ throw error;
+ });
+}
+
+function timeoutPromise(error, timeoutMs) {
+ let clearCallback = null;
+ let done = false;
+ const promise = onResolvedOrRejected(new Promise((resolve, reject) => {
+ const timeout = setTimeout(() => reject(error), timeoutMs);
+ clearCallback = () => {
+ if (done)
+ return;
+ clearTimeout(timeout);
+ resolve();
+ };
+ }), () => done = true);
+ promise.clear = clearCallback;
+ return promise;
+}
+
exports.hijackStdout = hijackStdWritable.bind(null, 'stdout');
exports.hijackStderr = hijackStdWritable.bind(null, 'stderr');
exports.restoreStdout = restoreWritable.bind(null, 'stdout');
@@ -827,3 +853,19 @@ exports.firstInvalidFD = function firstInvalidFD() {
} catch (e) {}
return fd;
};
+
+exports.fires = function fires(promise, error, timeoutMs) {
+ if (!timeoutMs && util.isNumber(error)) {
+ timeoutMs = error;
+ error = null;
+ }
+ if (!error)
+ error = 'timeout';
+ if (!timeoutMs)
+ timeoutMs = 100;
+ const timeout = timeoutPromise(error, timeoutMs);
+ return Promise.race([
+ onResolvedOrRejected(promise, () => timeout.clear()),
+ timeout
+ ]);
+};
diff --git a/test/inspector/inspector-helper.js b/test/inspector/inspector-helper.js
index 2f45e21c5b..b316f04aea 100644
--- a/test/inspector/inspector-helper.js
+++ b/test/inspector/inspector-helper.js
@@ -4,61 +4,61 @@ const assert = require('assert');
const fs = require('fs');
const http = require('http');
const path = require('path');
-const spawn = require('child_process').spawn;
+const { spawn } = require('child_process');
const url = require('url');
+const _MAINSCRIPT = path.join(common.fixturesDir, 'loop.js');
const DEBUG = false;
const TIMEOUT = 15 * 1000;
-const EXPECT_ALIVE_SYMBOL = Symbol('isAlive');
-const DONT_EXPECT_RESPONSE_SYMBOL = Symbol('dontExpectResponse');
-const mainScript = path.join(common.fixturesDir, 'loop.js');
-function send(socket, message, id, callback) {
- const msg = JSON.parse(JSON.stringify(message)); // Clone!
- msg['id'] = id;
- if (DEBUG)
- console.log('[sent]', JSON.stringify(msg));
- const messageBuf = Buffer.from(JSON.stringify(msg));
-
- const wsHeaderBuf = Buffer.allocUnsafe(16);
- wsHeaderBuf.writeUInt8(0x81, 0);
- let byte2 = 0x80;
- const bodyLen = messageBuf.length;
-
- let maskOffset = 2;
- if (bodyLen < 126) {
- byte2 = 0x80 + bodyLen;
- } else if (bodyLen < 65536) {
- byte2 = 0xFE;
- wsHeaderBuf.writeUInt16BE(bodyLen, 2);
- maskOffset = 4;
+function spawnChildProcess(inspectorFlags, scriptContents, scriptFile) {
+ const args = [].concat(inspectorFlags);
+ if (scriptContents) {
+ args.push('-e', scriptContents);
} else {
- byte2 = 0xFF;
- wsHeaderBuf.writeUInt32BE(bodyLen, 2);
- wsHeaderBuf.writeUInt32BE(0, 6);
- maskOffset = 10;
+ args.push(scriptFile);
}
- wsHeaderBuf.writeUInt8(byte2, 1);
- wsHeaderBuf.writeUInt32BE(0x01020408, maskOffset);
+ const child = spawn(process.execPath, args);
- for (let i = 0; i < messageBuf.length; i++)
- messageBuf[i] = messageBuf[i] ^ (1 << (i % 4));
- socket.write(
- Buffer.concat([wsHeaderBuf.slice(0, maskOffset + 4), messageBuf]),
- callback);
+ const handler = tearDown.bind(null, child);
+ process.on('exit', handler);
+ process.on('uncaughtException', handler);
+ process.on('unhandledRejection', handler);
+ process.on('SIGINT', handler);
+
+ return child;
+}
+
+function makeBufferingDataCallback(dataCallback) {
+ let buffer = Buffer.alloc(0);
+ return (data) => {
+ const newData = Buffer.concat([buffer, data]);
+ const str = newData.toString('utf8');
+ const lines = str.split('\n');
+ if (str.endsWith('\n'))
+ buffer = Buffer.alloc(0);
+ else
+ buffer = Buffer.from(lines.pop(), 'utf8');
+ for (const line of lines)
+ dataCallback(line);
+ };
}
-function sendEnd(socket) {
- socket.write(Buffer.from([0x88, 0x80, 0x2D, 0x0E, 0x1E, 0xFA]));
+function tearDown(child, err) {
+ child.kill();
+ if (err) {
+ console.error(err);
+ process.exit(1);
+ }
}
-function parseWSFrame(buffer, handler) {
+function parseWSFrame(buffer) {
// Protocol described in https://tools.ietf.org/html/rfc6455#section-5
+ let message = null;
if (buffer.length < 2)
- return 0;
+ return { length: 0, message };
if (buffer[0] === 0x88 && buffer[1] === 0x00) {
- handler(null);
- return 2;
+ return { length: 2, message, closed: true };
}
assert.strictEqual(0x81, buffer[0]);
let dataLen = 0x7F & buffer[1];
@@ -74,482 +74,331 @@ function parseWSFrame(buffer, handler) {
bodyOffset = 10;
}
if (buffer.length < bodyOffset + dataLen)
- return 0;
- const jsonPayload =
- buffer.slice(bodyOffset, bodyOffset + dataLen).toString('utf8');
- let message;
- try {
- message = JSON.parse(jsonPayload);
- } catch (e) {
- console.error(`JSON.parse() failed for: ${jsonPayload}`);
- throw e;
- }
+ return { length: 0, message };
+ message = JSON.parse(
+ buffer.slice(bodyOffset, bodyOffset + dataLen).toString('utf8'));
if (DEBUG)
console.log('[received]', JSON.stringify(message));
- handler(message);
- return bodyOffset + dataLen;
+ return { length: bodyOffset + dataLen, message };
}
-function tearDown(child, err) {
- child.kill();
- if (err instanceof Error) {
- console.error(err.stack);
- process.exit(1);
- }
-}
+function formatWSFrame(message) {
+ const messageBuf = Buffer.from(JSON.stringify(message));
-function checkHttpResponse(host, port, path, callback, errorcb) {
- const req = http.get({ host, port, path }, function(res) {
- let response = '';
- res.setEncoding('utf8');
- res
- .on('data', (data) => response += data.toString())
- .on('end', () => {
- let err = null;
- let json = undefined;
- try {
- json = JSON.parse(response);
- } catch (e) {
- err = e;
- err.response = response;
- }
- callback(err, json);
- });
- });
- if (errorcb)
- req.on('error', errorcb);
-}
-
-function makeBufferingDataCallback(dataCallback) {
- let buffer = Buffer.alloc(0);
- return (data) => {
- const newData = Buffer.concat([buffer, data]);
- const str = newData.toString('utf8');
- const lines = str.split('\n');
- if (str.endsWith('\n'))
- buffer = Buffer.alloc(0);
- else
- buffer = Buffer.from(lines.pop(), 'utf8');
- for (const line of lines)
- dataCallback(line);
- };
-}
+ const wsHeaderBuf = Buffer.allocUnsafe(16);
+ wsHeaderBuf.writeUInt8(0x81, 0);
+ let byte2 = 0x80;
+ const bodyLen = messageBuf.length;
-function timeout(message, multiplicator) {
- return setTimeout(common.mustNotCall(message),
- TIMEOUT * (multiplicator || 1));
-}
+ let maskOffset = 2;
+ if (bodyLen < 126) {
+ byte2 = 0x80 + bodyLen;
+ } else if (bodyLen < 65536) {
+ byte2 = 0xFE;
+ wsHeaderBuf.writeUInt16BE(bodyLen, 2);
+ maskOffset = 4;
+ } else {
+ byte2 = 0xFF;
+ wsHeaderBuf.writeUInt32BE(bodyLen, 2);
+ wsHeaderBuf.writeUInt32BE(0, 6);
+ maskOffset = 10;
+ }
+ wsHeaderBuf.writeUInt8(byte2, 1);
+ wsHeaderBuf.writeUInt32BE(0x01020408, maskOffset);
-function TestSession(socket, harness) {
- this.mainScriptPath = harness.mainScriptPath;
- this.mainScriptId = null;
-
- this.harness_ = harness;
- this.socket_ = socket;
- this.expectClose_ = false;
- this.scripts_ = {};
- this.messagefilter_ = null;
- this.responseCheckers_ = {};
- this.lastId_ = 0;
- this.messages_ = {};
- this.expectedId_ = 1;
- this.lastMessageResponseCallback_ = null;
- this.closeCallback_ = null;
+ for (let i = 0; i < messageBuf.length; i++)
+ messageBuf[i] = messageBuf[i] ^ (1 << (i % 4));
- let buffer = Buffer.alloc(0);
- socket.on('data', (data) => {
- buffer = Buffer.concat([buffer, data]);
- let consumed;
- do {
- consumed = parseWSFrame(buffer, this.processMessage_.bind(this));
- if (consumed)
- buffer = buffer.slice(consumed);
- } while (consumed);
- }).on('close', () => {
- assert(this.expectClose_, 'Socket closed prematurely');
- this.closeCallback_ && this.closeCallback_();
- });
+ return Buffer.concat([wsHeaderBuf.slice(0, maskOffset + 4), messageBuf]);
}
-TestSession.prototype.scriptUrlForId = function(id) {
- return this.scripts_[id];
-};
-
-TestSession.prototype.processMessage_ = function(message) {
- if (message === null) {
- sendEnd(this.socket_);
- return;
+class InspectorSession {
+ constructor(socket, instance) {
+ this._instance = instance;
+ this._socket = socket;
+ this._nextId = 1;
+ this._commandResponsePromises = new Map();
+ this._unprocessedNotifications = [];
+ this._notificationCallback = null;
+ this._scriptsIdsByUrl = new Map();
+
+ let buffer = Buffer.alloc(0);
+ socket.on('data', (data) => {
+ buffer = Buffer.concat([buffer, data]);
+ do {
+ const { length, message, closed } = parseWSFrame(buffer);
+ if (!length)
+ break;
+
+ if (closed) {
+ socket.write(Buffer.from([0x88, 0x00])); // WS close frame
+ }
+ buffer = buffer.slice(length);
+ if (message)
+ this._onMessage(message);
+ } while (true);
+ });
+ this._terminationPromise = new Promise((resolve) => {
+ socket.once('close', resolve);
+ });
}
- const method = message['method'];
- if (method === 'Debugger.scriptParsed') {
- const script = message['params'];
- const scriptId = script['scriptId'];
- const url = script['url'];
- this.scripts_[scriptId] = url;
- if (url === mainScript)
- this.mainScriptId = scriptId;
+ waitForServerDisconnect() {
+ return this._terminationPromise;
}
- this.messagefilter_ && this.messagefilter_(message);
- const id = message['id'];
- if (id) {
- this.expectedId_++;
- if (this.responseCheckers_[id]) {
- const messageJSON = JSON.stringify(message);
- const idJSON = JSON.stringify(this.messages_[id]);
- assert(message['result'], `${messageJSON} (response to ${idJSON})`);
- this.responseCheckers_[id](message['result']);
- delete this.responseCheckers_[id];
- }
- const messageJSON = JSON.stringify(message);
- const idJSON = JSON.stringify(this.messages_[id]);
- assert(!message['error'], `${messageJSON} (replying to ${idJSON})`);
- delete this.messages_[id];
- if (id === this.lastId_) {
- this.lastMessageResponseCallback_ && this.lastMessageResponseCallback_();
- this.lastMessageResponseCallback_ = null;
- }
+
+ disconnect() {
+ this._socket.destroy();
}
-};
-TestSession.prototype.sendAll_ = function(commands, callback) {
- if (!commands.length) {
- callback();
- } else {
- let id = ++this.lastId_;
- let command = commands[0];
- if (command instanceof Array) {
- this.responseCheckers_[id] = command[1];
- command = command[0];
- }
- if (command instanceof Function)
- command = command();
- if (!command[DONT_EXPECT_RESPONSE_SYMBOL]) {
- this.messages_[id] = command;
+ _onMessage(message) {
+ if (message.id) {
+ const { resolve, reject } = this._commandResponsePromises.get(message.id);
+ this._commandResponsePromises.delete(message.id);
+ if (message.result)
+ resolve(message.result);
+ else
+ reject(message.error);
} else {
- id += 100000;
- this.lastId_--;
+ if (message.method === 'Debugger.scriptParsed') {
+ const script = message['params'];
+ const scriptId = script['scriptId'];
+ const url = script['url'];
+ this._scriptsIdsByUrl.set(scriptId, url);
+ if (url === _MAINSCRIPT)
+ this.mainScriptId = scriptId;
+ }
+
+ if (this._notificationCallback) {
+ // In case callback needs to install another
+ const callback = this._notificationCallback;
+ this._notificationCallback = null;
+ callback(message);
+ } else {
+ this._unprocessedNotifications.push(message);
+ }
}
- send(this.socket_, command, id,
- () => this.sendAll_(commands.slice(1), callback));
}
-};
-TestSession.prototype.sendInspectorCommands = function(commands) {
- if (!(commands instanceof Array))
- commands = [commands];
- return this.enqueue((callback) => {
- let timeoutId = null;
- this.lastMessageResponseCallback_ = () => {
- timeoutId && clearTimeout(timeoutId);
- callback();
- };
- this.sendAll_(commands, () => {
- timeoutId = setTimeout(() => {
- assert.fail(`Messages without response: ${
- Object.keys(this.messages_).join(', ')}`);
- }, TIMEOUT);
- });
- });
-};
+ _sendMessage(message) {
+ const msg = JSON.parse(JSON.stringify(message)); // Clone!
+ msg['id'] = this._nextId++;
+ if (DEBUG)
+ console.log('[sent]', JSON.stringify(msg));
-TestSession.prototype.sendCommandsAndExpectClose = function(commands) {
- if (!(commands instanceof Array))
- commands = [commands];
- return this.enqueue((callback) => {
- let timeoutId = null;
- let done = false;
- this.expectClose_ = true;
- this.closeCallback_ = function() {
- if (timeoutId)
- clearTimeout(timeoutId);
- done = true;
- callback();
- };
- this.sendAll_(commands, () => {
- if (!done) {
- timeoutId = timeout('Session still open');
- }
+ const responsePromise = new Promise((resolve, reject) => {
+ this._commandResponsePromises.set(msg['id'], { resolve, reject });
});
- });
-};
-TestSession.prototype.createCallbackWithTimeout_ = function(message) {
- const promise = new Promise((resolve) => {
- this.enqueue((callback) => {
- const timeoutId = timeout(message);
- resolve(() => {
- clearTimeout(timeoutId);
- callback();
- });
- });
- });
- return () => promise.then((callback) => callback());
-};
+ return new Promise(
+ (resolve) => this._socket.write(formatWSFrame(msg), resolve))
+ .then(() => responsePromise);
+ }
-TestSession.prototype.expectMessages = function(expects) {
- if (!(expects instanceof Array)) expects = [ expects ];
-
- const callback = this.createCallbackWithTimeout_(
- `Matching response was not received:\n${expects[0]}`);
- this.messagefilter_ = (message) => {
- if (expects[0](message))
- expects.shift();
- if (!expects.length) {
- this.messagefilter_ = null;
- callback();
+ send(commands) {
+ if (Array.isArray(commands)) {
+ // Multiple commands means the response does not matter. There might even
+ // never be a response.
+ return Promise
+ .all(commands.map((command) => this._sendMessage(command)))
+ .then(() => {});
+ } else {
+ return this._sendMessage(commands);
}
- };
- return this;
-};
-
-TestSession.prototype.expectStderrOutput = function(regexp) {
- this.harness_.addStderrFilter(
- regexp,
- this.createCallbackWithTimeout_(`Timed out waiting for ${regexp}`));
- return this;
-};
-
-TestSession.prototype.runNext_ = function() {
- if (this.task_) {
- setImmediate(() => {
- this.task_(() => {
- this.task_ = this.task_.next_;
- this.runNext_();
- });
- });
}
-};
-TestSession.prototype.enqueue = function(task) {
- if (!this.task_) {
- this.task_ = task;
- this.runNext_();
- } else {
- let t = this.task_;
- while (t.next_)
- t = t.next_;
- t.next_ = task;
+ waitForNotification(methodOrPredicate, description) {
+ const desc = description || methodOrPredicate;
+ const message = `Timed out waiting for matching notification (${desc}))`;
+ return common.fires(
+ this._asyncWaitForNotification(methodOrPredicate), message, TIMEOUT);
}
- return this;
-};
-
-TestSession.prototype.disconnect = function(childDone) {
- return this.enqueue((callback) => {
- this.expectClose_ = true;
- this.socket_.destroy();
- console.log('[test]', 'Connection terminated');
- callback();
- }, childDone);
-};
-
-TestSession.prototype.expectClose = function() {
- return this.enqueue((callback) => {
- this.expectClose_ = true;
- callback();
- });
-};
-
-TestSession.prototype.assertClosed = function() {
- return this.enqueue((callback) => {
- assert.strictEqual(this.closed_, true);
- callback();
- });
-};
-
-TestSession.prototype.testHttpResponse = function(path, check) {
- return this.enqueue((callback) =>
- checkHttpResponse(null, this.harness_.port, path, (err, response) => {
- check.call(this, err, response);
- callback();
- }));
-};
-
-function Harness(port, childProcess) {
- this.port = port;
- this.mainScriptPath = mainScript;
- this.stderrFilters_ = [];
- this.process_ = childProcess;
- this.result_ = {};
- this.running_ = true;
-
- childProcess.stdout.on('data', makeBufferingDataCallback(
- (line) => console.log('[out]', line)));
-
-
- childProcess.stderr.on('data', makeBufferingDataCallback((message) => {
- const pending = [];
- console.log('[err]', message);
- for (const filter of this.stderrFilters_)
- if (!filter(message)) pending.push(filter);
- this.stderrFilters_ = pending;
- }));
- childProcess.on('exit', (code, signal) => {
- this.result_ = { code, signal };
- this.running_ = false;
- });
-}
+ async _asyncWaitForNotification(methodOrPredicate) {
+ function matchMethod(notification) {
+ return notification.method === methodOrPredicate;
+ }
+ const predicate =
+ typeof methodOrPredicate === 'string' ? matchMethod : methodOrPredicate;
+ let notification = null;
+ do {
+ if (this._unprocessedNotifications.length) {
+ notification = this._unprocessedNotifications.shift();
+ } else {
+ notification = await new Promise(
+ (resolve) => this._notificationCallback = resolve);
+ }
+ } while (!predicate(notification));
+ return notification;
+ }
-Harness.prototype.addStderrFilter = function(regexp, callback) {
- this.stderrFilters_.push((message) => {
- if (message.match(regexp)) {
- callback();
+ _isBreakOnLineNotification(message, line, url) {
+ if ('Debugger.paused' === message['method']) {
+ const callFrame = message['params']['callFrames'][0];
+ const location = callFrame['location'];
+ assert.strictEqual(url, this._scriptsIdsByUrl.get(location['scriptId']));
+ assert.strictEqual(line, location['lineNumber']);
return true;
}
- });
-};
-
-Harness.prototype.assertStillAlive = function() {
- assert.strictEqual(this.running_, true,
- `Child died: ${JSON.stringify(this.result_)}`);
-};
-
-Harness.prototype.run_ = function() {
- setImmediate(() => {
- if (!this.task_[EXPECT_ALIVE_SYMBOL])
- this.assertStillAlive();
- this.task_(() => {
- this.task_ = this.task_.next_;
- if (this.task_)
- this.run_();
- });
- });
-};
+ }
-Harness.prototype.enqueue_ = function(task, expectAlive) {
- task[EXPECT_ALIVE_SYMBOL] = !!expectAlive;
- if (!this.task_) {
- this.task_ = task;
- this.run_();
- } else {
- let chain = this.task_;
- while (chain.next_)
- chain = chain.next_;
- chain.next_ = task;
+ waitForBreakOnLine(line, url) {
+ return this
+ .waitForNotification(
+ (notification) =>
+ this._isBreakOnLineNotification(notification, line, url),
+ `break on ${url}:${line}`)
+ .then((notification) =>
+ notification.params.callFrames[0].scopeChain[0].object.objectId);
}
- return this;
-};
-Harness.prototype.testHttpResponse = function(host, path, check, errorcb) {
- return this.enqueue_((doneCallback) => {
- function wrap(callback) {
- if (callback) {
- return function() {
- callback(...arguments);
- doneCallback();
- };
+ _matchesConsoleOutputNotification(notification, type, values) {
+ if (!Array.isArray(values))
+ values = [ values ];
+ if ('Runtime.consoleAPICalled' === notification['method']) {
+ const params = notification['params'];
+ if (params['type'] === type) {
+ let i = 0;
+ for (const value of params['args']) {
+ if (value['value'] !== values[i++])
+ return false;
+ }
+ return i === values.length;
}
}
- checkHttpResponse(host, this.port, path, wrap(check), wrap(errorcb));
- });
-};
+ }
-Harness.prototype.wsHandshake = function(devtoolsUrl, tests, readyCallback) {
- http.get({
- port: this.port,
- path: url.parse(devtoolsUrl).path,
- headers: {
- 'Connection': 'Upgrade',
- 'Upgrade': 'websocket',
- 'Sec-WebSocket-Version': 13,
- 'Sec-WebSocket-Key': 'key=='
- }
- }).on('upgrade', (message, socket) => {
- const session = new TestSession(socket, this);
- if (!(tests instanceof Array))
- tests = [tests];
- function enqueue(tests) {
- session.enqueue((sessionCb) => {
- if (tests.length) {
- tests[0](session);
- session.enqueue((cb2) => {
- enqueue(tests.slice(1));
- cb2();
- });
- } else {
- readyCallback();
- }
- sessionCb();
- });
- }
- enqueue(tests);
- }).on('response', common.mustNotCall('Upgrade was not received'));
-};
+ waitForConsoleOutput(type, values) {
+ const desc = `Console output matching ${JSON.stringify(values)}`;
+ return this.waitForNotification(
+ (notification) => this._matchesConsoleOutputNotification(notification,
+ type, values),
+ desc);
+ }
-Harness.prototype.runFrontendSession = function(tests) {
- return this.enqueue_((callback) => {
- checkHttpResponse(null, this.port, '/json/list', (err, response) => {
- assert.ifError(err);
- this.wsHandshake(response[0]['webSocketDebuggerUrl'], tests, callback);
+ async runToCompletion() {
+ console.log('[test]', 'Verify node waits for the frontend to disconnect');
+ await this.send({ 'method': 'Debugger.resume' });
+ await this.waitForNotification((notification) => {
+ return notification.method === 'Runtime.executionContextDestroyed' &&
+ notification.params.executionContextId === 1;
});
- });
-};
+ while ((await this._instance.nextStderrString()) !==
+ 'Waiting for the debugger to disconnect...');
+ await this.disconnect();
+ }
+}
-Harness.prototype.expectShutDown = function(errorCode) {
- this.enqueue_((callback) => {
- if (this.running_) {
- const timeoutId = timeout('Have not terminated');
- this.process_.on('exit', (code, signal) => {
- clearTimeout(timeoutId);
- assert.strictEqual(errorCode, code, JSON.stringify({ code, signal }));
- callback();
+class NodeInstance {
+ constructor(inspectorFlags = ['--inspect-brk=0'],
+ scriptContents = '',
+ scriptFile = _MAINSCRIPT) {
+ this._portCallback = null;
+ this.portPromise = new Promise((resolve) => this._portCallback = resolve);
+ this._process = spawnChildProcess(inspectorFlags, scriptContents,
+ scriptFile);
+ this._running = true;
+ this._stderrLineCallback = null;
+ this._unprocessedStderrLines = [];
+
+ this._process.stdout.on('data', makeBufferingDataCallback(
+ (line) => console.log('[out]', line)));
+
+ this._process.stderr.on('data', makeBufferingDataCallback(
+ (message) => this.onStderrLine(message)));
+
+ this._shutdownPromise = new Promise((resolve) => {
+ this._process.once('exit', (exitCode, signal) => {
+ resolve({ exitCode, signal });
+ this._running = false;
});
+ });
+ }
+
+ onStderrLine(line) {
+ console.log('[err]', line);
+ if (this._portCallback) {
+ const matches = line.match(/Debugger listening on ws:\/\/.+:(\d+)\/.+/);
+ if (matches)
+ this._portCallback(matches[1]);
+ this._portCallback = null;
+ }
+ if (this._stderrLineCallback) {
+ this._stderrLineCallback(line);
+ this._stderrLineCallback = null;
} else {
- assert.strictEqual(errorCode, this.result_.code);
- callback();
+ this._unprocessedStderrLines.push(line);
}
- }, true);
-};
-
-Harness.prototype.kill = function() {
- return this.enqueue_((callback) => {
- this.process_.kill();
- callback();
- });
-};
-
-exports.startNodeForInspectorTest = function(callback,
- inspectorFlags = ['--inspect-brk'],
- scriptContents = '',
- scriptFile = mainScript) {
- const args = [].concat(inspectorFlags);
- if (scriptContents) {
- args.push('-e', scriptContents);
- } else {
- args.push(scriptFile);
}
- const child = spawn(process.execPath, args);
-
- const timeoutId = timeout('Child process did not start properly', 4);
+ httpGet(host, path) {
+ console.log('[test]', `Testing ${path}`);
+ return this.portPromise.then((port) => new Promise((resolve, reject) => {
+ const req = http.get({ host, port, path }, (res) => {
+ let response = '';
+ res.setEncoding('utf8');
+ res
+ .on('data', (data) => response += data.toString())
+ .on('end', () => {
+ resolve(response);
+ });
+ });
+ req.on('error', reject);
+ })).then((response) => {
+ try {
+ return JSON.parse(response);
+ } catch (e) {
+ e.body = response;
+ throw e;
+ }
+ });
+ }
- let found = false;
+ wsHandshake(devtoolsUrl) {
+ return this.portPromise.then((port) => new Promise((resolve) => {
+ http.get({
+ port,
+ path: url.parse(devtoolsUrl).path,
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'websocket',
+ 'Sec-WebSocket-Version': 13,
+ 'Sec-WebSocket-Key': 'key=='
+ }
+ }).on('upgrade', (message, socket) => {
+ resolve(new InspectorSession(socket, this));
+ }).on('response', common.mustNotCall('Upgrade was not received'));
+ }));
+ }
- const dataCallback = makeBufferingDataCallback((text) => {
- clearTimeout(timeoutId);
- console.log('[err]', text);
- if (found) return;
- const match = text.match(/Debugger listening on ws:\/\/.+:(\d+)\/.+/);
- found = true;
- child.stderr.removeListener('data', dataCallback);
- assert.ok(match, text);
- callback(new Harness(match[1], child));
- });
+ async connectInspectorSession() {
+ console.log('[test]', 'Connecting to a child Node process');
+ const response = await this.httpGet(null, '/json/list');
+ const url = response[0]['webSocketDebuggerUrl'];
+ return await this.wsHandshake(url);
+ }
- child.stderr.on('data', dataCallback);
+ expectShutdown() {
+ return this._shutdownPromise;
+ }
- const handler = tearDown.bind(null, child);
+ nextStderrString() {
+ if (this._unprocessedStderrLines.length)
+ return Promise.resolve(this._unprocessedStderrLines.shift());
+ return new Promise((resolve) => this._stderrLineCallback = resolve);
+ }
- process.on('exit', handler);
- process.on('uncaughtException', handler);
- process.on('SIGINT', handler);
-};
+ kill() {
+ this._process.kill();
+ }
+}
-exports.mainScriptSource = function() {
- return fs.readFileSync(mainScript, 'utf8');
-};
+function readMainScriptSource() {
+ return fs.readFileSync(_MAINSCRIPT, 'utf8');
+}
-exports.markMessageNoResponse = function(message) {
- message[DONT_EXPECT_RESPONSE_SYMBOL] = true;
+module.exports = {
+ mainScriptPath: _MAINSCRIPT,
+ readMainScriptSource,
+ NodeInstance
};
diff --git a/test/inspector/test-break-when-eval.js b/test/inspector/test-break-when-eval.js
new file mode 100644
index 0000000000..ddd8220bb9
--- /dev/null
+++ b/test/inspector/test-break-when-eval.js
@@ -0,0 +1,68 @@
+'use strict';
+const common = require('../common');
+common.skipIfInspectorDisabled();
+const assert = require('assert');
+const { NodeInstance } = require('./inspector-helper.js');
+const path = require('path');
+
+const script = path.join(path.dirname(module.filename), 'global-function.js');
+
+async function setupDebugger(session) {
+ console.log('[test]', 'Setting up a debugger');
+ const commands = [
+ { 'method': 'Runtime.enable' },
+ { 'method': 'Debugger.enable' },
+ { 'method': 'Debugger.setAsyncCallStackDepth',
+ 'params': { 'maxDepth': 0 } },
+ { 'method': 'Runtime.runIfWaitingForDebugger' },
+ ];
+ session.send(commands);
+ await session.waitForNotification('Runtime.consoleAPICalled');
+}
+
+async function breakOnLine(session) {
+ console.log('[test]', 'Breaking in the code');
+ const commands = [
+ { 'method': 'Debugger.setBreakpointByUrl',
+ 'params': { 'lineNumber': 9,
+ 'url': script,
+ 'columnNumber': 0,
+ 'condition': ''
+ }
+ },
+ { 'method': 'Runtime.evaluate',
+ 'params': { 'expression': 'sum()',
+ 'objectGroup': 'console',
+ 'includeCommandLineAPI': true,
+ 'silent': false,
+ 'contextId': 1,
+ 'returnByValue': false,
+ 'generatePreview': true,
+ 'userGesture': true,
+ 'awaitPromise': false
+ }
+ }
+ ];
+ session.send(commands);
+ await session.waitForBreakOnLine(9, script);
+}
+
+async function stepOverConsoleStatement(session) {
+ console.log('[test]', 'Step over console statement and test output');
+ session.send({ 'method': 'Debugger.stepOver' });
+ await session.waitForConsoleOutput('log', [0, 3]);
+ await session.waitForNotification('Debugger.paused');
+}
+
+async function runTests() {
+ const child = new NodeInstance(['--inspect=0'], undefined, script);
+ const session = await child.connectInspectorSession();
+ await setupDebugger(session);
+ await breakOnLine(session);
+ await stepOverConsoleStatement(session);
+ await session.runToCompletion();
+ assert.strictEqual(0, (await child.expectShutdown()).exitCode);
+}
+
+common.crashOnUnhandledRejection();
+runTests();
diff --git a/test/inspector/test-debug-brk-flag.js b/test/inspector/test-debug-brk-flag.js
new file mode 100644
index 0000000000..f0a4d97602
--- /dev/null
+++ b/test/inspector/test-debug-brk-flag.js
@@ -0,0 +1,41 @@
+'use strict';
+const common = require('../common');
+
+common.skipIfInspectorDisabled();
+
+const assert = require('assert');
+const { mainScriptPath,
+ NodeInstance } = require('./inspector-helper.js');
+
+async function testBreakpointOnStart(session) {
+ const commands = [
+ { 'method': 'Runtime.enable' },
+ { 'method': 'Debugger.enable' },
+ { 'method': 'Debugger.setPauseOnExceptions',
+ 'params': { 'state': 'none' } },
+ { 'method': 'Debugger.setAsyncCallStackDepth',
+ 'params': { 'maxDepth': 0 } },
+ { 'method': 'Profiler.enable' },
+ { 'method': 'Profiler.setSamplingInterval',
+ 'params': { 'interval': 100 } },
+ { 'method': 'Debugger.setBlackboxPatterns',
+ 'params': { 'patterns': [] } },
+ { 'method': 'Runtime.runIfWaitingForDebugger' }
+ ];
+
+ session.send(commands);
+ await session.waitForBreakOnLine(0, mainScriptPath);
+}
+
+async function runTests() {
+ const child = new NodeInstance(['--inspect', '--debug-brk']);
+ const session = await child.connectInspectorSession();
+
+ await testBreakpointOnStart(session);
+ await session.runToCompletion();
+
+ assert.strictEqual(55, (await child.expectShutdown()).exitCode);
+}
+
+common.crashOnUnhandledRejection();
+runTests();
diff --git a/test/inspector/test-debug-end.js b/test/inspector/test-debug-end.js
new file mode 100644
index 0000000000..57ce019083
--- /dev/null
+++ b/test/inspector/test-debug-end.js
@@ -0,0 +1,46 @@
+'use strict';
+const common = require('../common');
+common.skipIfInspectorDisabled();
+const { strictEqual } = require('assert');
+const { NodeInstance } = require('./inspector-helper.js');
+
+async function testNoServerNoCrash() {
+ console.log('Test there\'s no crash stopping server that was not started');
+ const instance = new NodeInstance([],
+ `process._debugEnd();
+ process.exit(42);`);
+ strictEqual(42, (await instance.expectShutdown()).exitCode);
+}
+
+async function testNoSessionNoCrash() {
+ console.log('Test there\'s no crash stopping server without connecting');
+ const instance = new NodeInstance('--inspect=0',
+ 'process._debugEnd();process.exit(42);');
+ strictEqual(42, (await instance.expectShutdown()).exitCode);
+}
+
+async function testSessionNoCrash() {
+ console.log('Test there\'s no crash stopping server after connecting');
+ const script = `process._debugEnd();
+ process._debugProcess(process.pid);
+ setTimeout(() => {
+ console.log("Done");
+ process.exit(42);
+ });`;
+
+ const instance = new NodeInstance('--inspect-brk=0', script);
+ const session = await instance.connectInspectorSession();
+ await session.send({ 'method': 'Runtime.runIfWaitingForDebugger' });
+ await session.waitForServerDisconnect();
+ strictEqual(42, (await instance.expectShutdown()).exitCode);
+}
+
+async function runTest() {
+ await testNoServerNoCrash();
+ await testNoSessionNoCrash();
+ await testSessionNoCrash();
+}
+
+common.crashOnUnhandledRejection();
+
+runTest();
diff --git a/test/inspector/test-exception.js b/test/inspector/test-exception.js
new file mode 100644
index 0000000000..ca3994c0a0
--- /dev/null
+++ b/test/inspector/test-exception.js
@@ -0,0 +1,45 @@
+'use strict';
+const common = require('../common');
+
+common.skipIfInspectorDisabled();
+
+const assert = require('assert');
+const { NodeInstance } = require('./inspector-helper.js');
+const path = require('path');
+
+const script = path.join(common.fixturesDir, 'throws_error.js');
+
+async function testBreakpointOnStart(session) {
+ console.log('[test]',
+ 'Verifying debugger stops on start (--inspect-brk option)');
+ const commands = [
+ { 'method': 'Runtime.enable' },
+ { 'method': 'Debugger.enable' },
+ { 'method': 'Debugger.setPauseOnExceptions',
+ 'params': { 'state': 'none' } },
+ { 'method': 'Debugger.setAsyncCallStackDepth',
+ 'params': { 'maxDepth': 0 } },
+ { 'method': 'Profiler.enable' },
+ { 'method': 'Profiler.setSamplingInterval',
+ 'params': { 'interval': 100 } },
+ { 'method': 'Debugger.setBlackboxPatterns',
+ 'params': { 'patterns': [] } },
+ { 'method': 'Runtime.runIfWaitingForDebugger' }
+ ];
+
+ await session.send(commands);
+ await session.waitForBreakOnLine(0, script);
+}
+
+
+async function runTest() {
+ const child = new NodeInstance(undefined, undefined, script);
+ const session = await child.connectInspectorSession();
+ await testBreakpointOnStart(session);
+ await session.runToCompletion();
+ assert.strictEqual(1, (await child.expectShutdown()).exitCode);
+}
+
+common.crashOnUnhandledRejection();
+
+runTest();
diff --git a/test/inspector/test-inspector-break-when-eval.js b/test/inspector/test-inspector-break-when-eval.js
deleted file mode 100644
index 957d7ea4d2..0000000000
--- a/test/inspector/test-inspector-break-when-eval.js
+++ /dev/null
@@ -1,128 +0,0 @@
-'use strict';
-const common = require('../common');
-common.skipIfInspectorDisabled();
-const assert = require('assert');
-const helper = require('./inspector-helper.js');
-const path = require('path');
-
-const script = path.join(path.dirname(module.filename), 'global-function.js');
-
-
-function setupExpectBreakOnLine(line, url, session) {
- return function(message) {
- if ('Debugger.paused' === message['method']) {
- const callFrame = message['params']['callFrames'][0];
- const location = callFrame['location'];
- assert.strictEqual(url, session.scriptUrlForId(location['scriptId']));
- assert.strictEqual(line, location['lineNumber']);
- return true;
- }
- };
-}
-
-function setupExpectConsoleOutputAndBreak(type, values) {
- if (!(values instanceof Array))
- values = [ values ];
- let consoleLog = false;
- function matchConsoleLog(message) {
- if ('Runtime.consoleAPICalled' === message['method']) {
- const params = message['params'];
- if (params['type'] === type) {
- let i = 0;
- for (const value of params['args']) {
- if (value['value'] !== values[i++])
- return false;
- }
- return i === values.length;
- }
- }
- }
-
- return function(message) {
- if (consoleLog)
- return message['method'] === 'Debugger.paused';
- consoleLog = matchConsoleLog(message);
- return false;
- };
-}
-
-function setupExpectContextDestroyed(id) {
- return function(message) {
- if ('Runtime.executionContextDestroyed' === message['method'])
- return message['params']['executionContextId'] === id;
- };
-}
-
-function setupDebugger(session) {
- console.log('[test]', 'Setting up a debugger');
- const commands = [
- { 'method': 'Runtime.enable' },
- { 'method': 'Debugger.enable' },
- { 'method': 'Debugger.setAsyncCallStackDepth',
- 'params': { 'maxDepth': 0 } },
- { 'method': 'Runtime.runIfWaitingForDebugger' },
- ];
-
- session
- .sendInspectorCommands(commands)
- .expectMessages((message) => 'Runtime.consoleAPICalled' === message.method);
-}
-
-function breakOnLine(session) {
- console.log('[test]', 'Breaking in the code');
- const commands = [
- { 'method': 'Debugger.setBreakpointByUrl',
- 'params': { 'lineNumber': 9,
- 'url': script,
- 'columnNumber': 0,
- 'condition': ''
- }
- },
- { 'method': 'Runtime.evaluate',
- 'params': { 'expression': 'sum()',
- 'objectGroup': 'console',
- 'includeCommandLineAPI': true,
- 'silent': false,
- 'contextId': 1,
- 'returnByValue': false,
- 'generatePreview': true,
- 'userGesture': true,
- 'awaitPromise': false
- }
- }
- ];
- helper.markMessageNoResponse(commands[1]);
- session
- .sendInspectorCommands(commands)
- .expectMessages(setupExpectBreakOnLine(9, script, session));
-}
-
-function stepOverConsoleStatement(session) {
- console.log('[test]', 'Step over console statement and test output');
- session
- .sendInspectorCommands({ 'method': 'Debugger.stepOver' })
- .expectMessages(setupExpectConsoleOutputAndBreak('log', [0, 3]));
-}
-
-function testWaitsForFrontendDisconnect(session, harness) {
- console.log('[test]', 'Verify node waits for the frontend to disconnect');
- session.sendInspectorCommands({ 'method': 'Debugger.resume' })
- .expectMessages(setupExpectContextDestroyed(1))
- .expectStderrOutput('Waiting for the debugger to disconnect...')
- .disconnect(true);
-}
-
-function runTests(harness) {
- harness
- .runFrontendSession([
- setupDebugger,
- breakOnLine,
- stepOverConsoleStatement,
- testWaitsForFrontendDisconnect
- ]).expectShutDown(0);
-}
-
-helper.startNodeForInspectorTest(runTests,
- ['--inspect'],
- undefined,
- script);
diff --git a/test/inspector/test-inspector-debug-brk.js b/test/inspector/test-inspector-debug-brk.js
deleted file mode 100644
index 1d7af9e318..0000000000
--- a/test/inspector/test-inspector-debug-brk.js
+++ /dev/null
@@ -1,59 +0,0 @@
-'use strict';
-const common = require('../common');
-
-common.skipIfInspectorDisabled();
-
-const assert = require('assert');
-const helper = require('./inspector-helper.js');
-
-function setupExpectBreakOnLine(line, url, session, scopeIdCallback) {
- return function(message) {
- if ('Debugger.paused' === message['method']) {
- const callFrame = message['params']['callFrames'][0];
- const location = callFrame['location'];
- assert.strictEqual(url, session.scriptUrlForId(location['scriptId']));
- assert.strictEqual(line, location['lineNumber']);
- scopeIdCallback &&
- scopeIdCallback(callFrame['scopeChain'][0]['object']['objectId']);
- return true;
- }
- };
-}
-
-function testBreakpointOnStart(session) {
- const commands = [
- { 'method': 'Runtime.enable' },
- { 'method': 'Debugger.enable' },
- { 'method': 'Debugger.setPauseOnExceptions',
- 'params': { 'state': 'none' } },
- { 'method': 'Debugger.setAsyncCallStackDepth',
- 'params': { 'maxDepth': 0 } },
- { 'method': 'Profiler.enable' },
- { 'method': 'Profiler.setSamplingInterval',
- 'params': { 'interval': 100 } },
- { 'method': 'Debugger.setBlackboxPatterns',
- 'params': { 'patterns': [] } },
- { 'method': 'Runtime.runIfWaitingForDebugger' }
- ];
-
- session
- .sendInspectorCommands(commands)
- .expectMessages(setupExpectBreakOnLine(0, session.mainScriptPath, session));
-}
-
-function testWaitsForFrontendDisconnect(session, harness) {
- console.log('[test]', 'Verify node waits for the frontend to disconnect');
- session.sendInspectorCommands({ 'method': 'Debugger.resume' })
- .expectStderrOutput('Waiting for the debugger to disconnect...')
- .disconnect(true);
-}
-
-function runTests(harness) {
- harness
- .runFrontendSession([
- testBreakpointOnStart,
- testWaitsForFrontendDisconnect
- ]).expectShutDown(55);
-}
-
-helper.startNodeForInspectorTest(runTests, ['--inspect', '--debug-brk']);
diff --git a/test/inspector/test-inspector-exception.js b/test/inspector/test-inspector-exception.js
deleted file mode 100644
index 2c6432c65d..0000000000
--- a/test/inspector/test-inspector-exception.js
+++ /dev/null
@@ -1,64 +0,0 @@
-'use strict';
-const common = require('../common');
-
-common.skipIfInspectorDisabled();
-
-const assert = require('assert');
-const helper = require('./inspector-helper.js');
-const path = require('path');
-
-const script = path.join(common.fixturesDir, 'throws_error.js');
-
-
-function setupExpectBreakOnLine(line, url, session) {
- return function(message) {
- if ('Debugger.paused' === message['method']) {
- const callFrame = message['params']['callFrames'][0];
- const location = callFrame['location'];
- assert.strictEqual(url, session.scriptUrlForId(location['scriptId']));
- assert.strictEqual(line, location['lineNumber']);
- return true;
- }
- };
-}
-
-function testBreakpointOnStart(session) {
- const commands = [
- { 'method': 'Runtime.enable' },
- { 'method': 'Debugger.enable' },
- { 'method': 'Debugger.setPauseOnExceptions',
- 'params': { 'state': 'none' } },
- { 'method': 'Debugger.setAsyncCallStackDepth',
- 'params': { 'maxDepth': 0 } },
- { 'method': 'Profiler.enable' },
- { 'method': 'Profiler.setSamplingInterval',
- 'params': { 'interval': 100 } },
- { 'method': 'Debugger.setBlackboxPatterns',
- 'params': { 'patterns': [] } },
- { 'method': 'Runtime.runIfWaitingForDebugger' }
- ];
-
- session
- .sendInspectorCommands(commands)
- .expectMessages(setupExpectBreakOnLine(0, script, session));
-}
-
-function testWaitsForFrontendDisconnect(session, harness) {
- console.log('[test]', 'Verify node waits for the frontend to disconnect');
- session.sendInspectorCommands({ 'method': 'Debugger.resume' })
- .expectStderrOutput('Waiting for the debugger to disconnect...')
- .disconnect(true);
-}
-
-function runTests(harness) {
- harness
- .runFrontendSession([
- testBreakpointOnStart,
- testWaitsForFrontendDisconnect
- ]).expectShutDown(1);
-}
-
-helper.startNodeForInspectorTest(runTests,
- undefined,
- undefined,
- script);
diff --git a/test/inspector/test-inspector-ip-detection.js b/test/inspector/test-inspector-ip-detection.js
deleted file mode 100644
index be5e34a977..0000000000
--- a/test/inspector/test-inspector-ip-detection.js
+++ /dev/null
@@ -1,51 +0,0 @@
-'use strict';
-const common = require('../common');
-
-common.skipIfInspectorDisabled();
-
-const assert = require('assert');
-const helper = require('./inspector-helper.js');
-const os = require('os');
-
-const ip = pickIPv4Address();
-
-if (!ip)
- common.skip('No IP address found');
-
-function checkListResponse(instance, err, response) {
- assert.ifError(err);
- const res = response[0];
- const wsUrl = res['webSocketDebuggerUrl'];
- assert.ok(wsUrl);
- const match = wsUrl.match(/^ws:\/\/(.*):9229\/(.*)/);
- assert.strictEqual(ip, match[1]);
- assert.strictEqual(res['id'], match[2]);
- assert.strictEqual(ip, res['devtoolsFrontendUrl'].match(/.*ws=(.*):9229/)[1]);
- instance.childInstanceDone = true;
-}
-
-function checkError(instance, error) {
- // Some OSes will not allow us to connect
- if (error.code === 'EHOSTUNREACH') {
- common.printSkipMessage('Unable to connect to self');
- } else {
- throw error;
- }
- instance.childInstanceDone = true;
-}
-
-function runTests(instance) {
- instance
- .testHttpResponse(ip, '/json/list', checkListResponse.bind(null, instance),
- checkError.bind(null, instance))
- .kill();
-}
-
-function pickIPv4Address() {
- for (const i of [].concat(...Object.values(os.networkInterfaces()))) {
- if (i.family === 'IPv4' && i.address !== '127.0.0.1')
- return i.address;
- }
-}
-
-helper.startNodeForInspectorTest(runTests, '--inspect-brk=0.0.0.0');
diff --git a/test/inspector/test-inspector-stop-profile-after-done.js b/test/inspector/test-inspector-stop-profile-after-done.js
deleted file mode 100644
index db43e4ae79..0000000000
--- a/test/inspector/test-inspector-stop-profile-after-done.js
+++ /dev/null
@@ -1,21 +0,0 @@
-'use strict';
-const common = require('../common');
-common.skipIfInspectorDisabled();
-const helper = require('./inspector-helper.js');
-
-function test(session) {
- session.sendInspectorCommands([
- { 'method': 'Runtime.runIfWaitingForDebugger' },
- { 'method': 'Profiler.setSamplingInterval', 'params': { 'interval': 100 } },
- { 'method': 'Profiler.enable' },
- { 'method': 'Profiler.start' }]);
- session.expectStderrOutput('Waiting for the debugger to disconnect...');
- session.sendInspectorCommands({ 'method': 'Profiler.stop' });
- session.disconnect(true);
-}
-
-function runTests(harness) {
- harness.runFrontendSession([test]).expectShutDown(0);
-}
-
-helper.startNodeForInspectorTest(runTests, ['--inspect-brk'], 'let a = 2;');
diff --git a/test/inspector/test-inspector.js b/test/inspector/test-inspector.js
index f933232553..3139940451 100644
--- a/test/inspector/test-inspector.js
+++ b/test/inspector/test-inspector.js
@@ -4,12 +4,11 @@ const common = require('../common');
common.skipIfInspectorDisabled();
const assert = require('assert');
-const helper = require('./inspector-helper.js');
+const { mainScriptPath,
+ readMainScriptSource,
+ NodeInstance } = require('./inspector-helper.js');
-let scopeId;
-
-function checkListResponse(err, response) {
- assert.ifError(err);
+function checkListResponse(response) {
assert.strictEqual(1, response.length);
assert.ok(response[0]['devtoolsFrontendUrl']);
assert.ok(
@@ -17,8 +16,7 @@ function checkListResponse(err, response) {
.test(response[0]['webSocketDebuggerUrl']));
}
-function checkVersion(err, response) {
- assert.ifError(err);
+function checkVersion(response) {
assert.ok(response);
const expected = {
'Browser': `node.js/${process.version}`,
@@ -28,10 +26,10 @@ function checkVersion(err, response) {
JSON.stringify(expected));
}
-function checkBadPath(err, response) {
+function checkBadPath(err) {
assert(err instanceof SyntaxError);
- assert(/Unexpected token/.test(err.message));
- assert(/WebSockets request was expected/.test(err.response));
+ assert(/Unexpected token/.test(err.message), err.message);
+ assert(/WebSockets request was expected/.test(err.body), err.body);
}
function checkException(message) {
@@ -39,69 +37,26 @@ function checkException(message) {
'An exception occurred during execution');
}
-function expectMainScriptSource(result) {
- const expected = helper.mainScriptSource();
- const source = result['scriptSource'];
- assert(source && (source.includes(expected)),
- `Script source is wrong: ${source}`);
-}
-
-function setupExpectBreakOnLine(line, url, session, scopeIdCallback) {
- return function(message) {
- if ('Debugger.paused' === message['method']) {
- const callFrame = message['params']['callFrames'][0];
- const location = callFrame['location'];
- assert.strictEqual(url, session.scriptUrlForId(location['scriptId']));
- assert.strictEqual(line, location['lineNumber']);
- scopeIdCallback &&
- scopeIdCallback(callFrame['scopeChain'][0]['object']['objectId']);
- return true;
- }
- };
-}
-
-function setupExpectConsoleOutput(type, values) {
- if (!(values instanceof Array))
- values = [ values ];
- return function(message) {
- if ('Runtime.consoleAPICalled' === message['method']) {
- const params = message['params'];
- if (params['type'] === type) {
- let i = 0;
- for (const value of params['args']) {
- if (value['value'] !== values[i++])
- return false;
- }
- return i === values.length;
- }
- }
- };
+function assertNoUrlsWhileConnected(response) {
+ assert.strictEqual(1, response.length);
+ assert.ok(!response[0].hasOwnProperty('devtoolsFrontendUrl'));
+ assert.ok(!response[0].hasOwnProperty('webSocketDebuggerUrl'));
}
-function setupExpectScopeValues(expected) {
- return function(result) {
- for (const actual of result['result']) {
- const value = expected[actual['name']];
- if (value)
- assert.strictEqual(value, actual['value']['value']);
+function assertScopeValues({ result }, expected) {
+ const unmatched = new Set(Object.keys(expected));
+ for (const actual of result) {
+ const value = expected[actual['name']];
+ if (value) {
+ assert.strictEqual(value, actual['value']['value']);
+ unmatched.delete(actual['name']);
}
- };
+ }
+ if (unmatched.size)
+ assert.fail(Array.from(unmatched.values()));
}
-function setupExpectValue(value) {
- return function(result) {
- assert.strictEqual(value, result['result']['value']);
- };
-}
-
-function setupExpectContextDestroyed(id) {
- return function(message) {
- if ('Runtime.executionContextDestroyed' === message['method'])
- return message['params']['executionContextId'] === id;
- };
-}
-
-function testBreakpointOnStart(session) {
+async function testBreakpointOnStart(session) {
console.log('[test]',
'Verifying debugger stops on start (--inspect-brk option)');
const commands = [
@@ -119,262 +74,230 @@ function testBreakpointOnStart(session) {
{ 'method': 'Runtime.runIfWaitingForDebugger' }
];
- session
- .sendInspectorCommands(commands)
- .expectMessages(setupExpectBreakOnLine(0, session.mainScriptPath, session));
+ await session.send(commands);
+ await session.waitForBreakOnLine(0, mainScriptPath);
}
-function testSetBreakpointAndResume(session) {
+async function testBreakpoint(session) {
console.log('[test]', 'Setting a breakpoint and verifying it is hit');
const commands = [
{ 'method': 'Debugger.setBreakpointByUrl',
'params': { 'lineNumber': 5,
- 'url': session.mainScriptPath,
+ 'url': mainScriptPath,
'columnNumber': 0,
'condition': ''
}
},
{ 'method': 'Debugger.resume' },
- [ { 'method': 'Debugger.getScriptSource',
- 'params': { 'scriptId': session.mainScriptId } },
- expectMainScriptSource ],
];
- session
- .sendInspectorCommands(commands)
- .expectMessages([
- setupExpectConsoleOutput('log', ['A message', 5]),
- setupExpectBreakOnLine(5, session.mainScriptPath,
- session, (id) => scopeId = id),
- ]);
-}
+ await session.send(commands);
+ const { scriptSource } = await session.send({
+ 'method': 'Debugger.getScriptSource',
+ 'params': { 'scriptId': session.mainScriptId } });
+ assert(scriptSource && (scriptSource.includes(readMainScriptSource())),
+ `Script source is wrong: ${scriptSource}`);
+
+ await session.waitForConsoleOutput('log', ['A message', 5]);
+ const scopeId = await session.waitForBreakOnLine(5, mainScriptPath);
-function testInspectScope(session) {
console.log('[test]', 'Verify we can read current application state');
- session.sendInspectorCommands([
- [
- {
- 'method': 'Runtime.getProperties',
- 'params': {
- 'objectId': scopeId,
- 'ownProperties': false,
- 'accessorPropertiesOnly': false,
- 'generatePreview': true
- }
- }, setupExpectScopeValues({ t: 1001, k: 1 })
- ],
- [
- {
- 'method': 'Debugger.evaluateOnCallFrame', 'params': {
- 'callFrameId': '{"ordinal":0,"injectedScriptId":1}',
- 'expression': 'k + t',
- 'objectGroup': 'console',
- 'includeCommandLineAPI': true,
- 'silent': false,
- 'returnByValue': false,
- 'generatePreview': true
- }
- }, setupExpectValue(1002)
- ],
- [
- {
- 'method': 'Runtime.evaluate', 'params': {
- 'expression': '5 * 5'
- }
- }, (message) => assert.strictEqual(25, message['result']['value'])
- ],
- ]);
-}
+ const response = await session.send({
+ 'method': 'Runtime.getProperties',
+ 'params': {
+ 'objectId': scopeId,
+ 'ownProperties': false,
+ 'accessorPropertiesOnly': false,
+ 'generatePreview': true
+ }
+ });
+ assertScopeValues(response, { t: 1001, k: 1 });
-function testNoUrlsWhenConnected(session) {
- session.testHttpResponse('/json/list', (err, response) => {
- assert.ifError(err);
- assert.strictEqual(1, response.length);
- assert.ok(!response[0].hasOwnProperty('devtoolsFrontendUrl'));
- assert.ok(!response[0].hasOwnProperty('webSocketDebuggerUrl'));
+ let { result } = await session.send({
+ 'method': 'Debugger.evaluateOnCallFrame', 'params': {
+ 'callFrameId': '{"ordinal":0,"injectedScriptId":1}',
+ 'expression': 'k + t',
+ 'objectGroup': 'console',
+ 'includeCommandLineAPI': true,
+ 'silent': false,
+ 'returnByValue': false,
+ 'generatePreview': true
+ }
});
+
+ assert.strictEqual(1002, result['value']);
+
+ result = (await session.send({
+ 'method': 'Runtime.evaluate', 'params': {
+ 'expression': '5 * 5'
+ }
+ })).result;
+ assert.strictEqual(25, result['value']);
}
-function testI18NCharacters(session) {
+async function testI18NCharacters(session) {
console.log('[test]', 'Verify sending and receiving UTF8 characters');
const chars = 'טֶ字и';
- session.sendInspectorCommands([
- {
- 'method': 'Debugger.evaluateOnCallFrame', 'params': {
- 'callFrameId': '{"ordinal":0,"injectedScriptId":1}',
- 'expression': `console.log("${chars}")`,
- 'objectGroup': 'console',
- 'includeCommandLineAPI': true,
- 'silent': false,
- 'returnByValue': false,
- 'generatePreview': true
- }
+ session.send({
+ 'method': 'Debugger.evaluateOnCallFrame', 'params': {
+ 'callFrameId': '{"ordinal":0,"injectedScriptId":1}',
+ 'expression': `console.log("${chars}")`,
+ 'objectGroup': 'console',
+ 'includeCommandLineAPI': true,
+ 'silent': false,
+ 'returnByValue': false,
+ 'generatePreview': true
}
- ]).expectMessages([
- setupExpectConsoleOutput('log', [chars]),
- ]);
+ });
+ await session.waitForConsoleOutput('log', [chars]);
}
-function testCommandLineAPI(session) {
+async function testCommandLineAPI(session) {
const testModulePath = require.resolve('../fixtures/empty.js');
const testModuleStr = JSON.stringify(testModulePath);
const printAModulePath = require.resolve('../fixtures/printA.js');
const printAModuleStr = JSON.stringify(printAModulePath);
const printBModulePath = require.resolve('../fixtures/printB.js');
const printBModuleStr = JSON.stringify(printBModulePath);
- session.sendInspectorCommands([
- [ // we can use `require` outside of a callframe with require in scope
- {
- 'method': 'Runtime.evaluate', 'params': {
- 'expression': 'typeof require("fs").readFile === "function"',
- 'includeCommandLineAPI': true
- }
- }, (message) => {
- checkException(message);
- assert.strictEqual(message['result']['value'], true);
+
+ // we can use `require` outside of a callframe with require in scope
+ let result = await session.send(
+ {
+ 'method': 'Runtime.evaluate', 'params': {
+ 'expression': 'typeof require("fs").readFile === "function"',
+ 'includeCommandLineAPI': true
}
- ],
- [ // the global require has the same properties as a normal `require`
- {
- 'method': 'Runtime.evaluate', 'params': {
- 'expression': [
- 'typeof require.resolve === "function"',
- 'typeof require.extensions === "object"',
- 'typeof require.cache === "object"'
- ].join(' && '),
- 'includeCommandLineAPI': true
- }
- }, (message) => {
- checkException(message);
- assert.strictEqual(message['result']['value'], true);
+ });
+ checkException(result);
+ assert.strictEqual(result['result']['value'], true);
+
+ // the global require has the same properties as a normal `require`
+ result = await session.send(
+ {
+ 'method': 'Runtime.evaluate', 'params': {
+ 'expression': [
+ 'typeof require.resolve === "function"',
+ 'typeof require.extensions === "object"',
+ 'typeof require.cache === "object"'
+ ].join(' && '),
+ 'includeCommandLineAPI': true
}
- ],
- [ // `require` twice returns the same value
- {
- 'method': 'Runtime.evaluate', 'params': {
- // 1. We require the same module twice
- // 2. We mutate the exports so we can compare it later on
- 'expression': `
- Object.assign(
- require(${testModuleStr}),
- { old: 'yes' }
- ) === require(${testModuleStr})`,
- 'includeCommandLineAPI': true
- }
- }, (message) => {
- checkException(message);
- assert.strictEqual(message['result']['value'], true);
+ });
+ checkException(result);
+ assert.strictEqual(result['result']['value'], true);
+ // `require` twice returns the same value
+ result = await session.send(
+ {
+ 'method': 'Runtime.evaluate', 'params': {
+ // 1. We require the same module twice
+ // 2. We mutate the exports so we can compare it later on
+ 'expression': `
+ Object.assign(
+ require(${testModuleStr}),
+ { old: 'yes' }
+ ) === require(${testModuleStr})`,
+ 'includeCommandLineAPI': true
}
- ],
- [ // after require the module appears in require.cache
- {
- 'method': 'Runtime.evaluate', 'params': {
- 'expression': `JSON.stringify(
- require.cache[${testModuleStr}].exports
- )`,
- 'includeCommandLineAPI': true
- }
- }, (message) => {
- checkException(message);
- assert.deepStrictEqual(JSON.parse(message['result']['value']),
- { old: 'yes' });
+ });
+ checkException(result);
+ assert.strictEqual(result['result']['value'], true);
+ // after require the module appears in require.cache
+ result = await session.send(
+ {
+ 'method': 'Runtime.evaluate', 'params': {
+ 'expression': `JSON.stringify(
+ require.cache[${testModuleStr}].exports
+ )`,
+ 'includeCommandLineAPI': true
}
- ],
- [ // remove module from require.cache
- {
- 'method': 'Runtime.evaluate', 'params': {
- 'expression': `delete require.cache[${testModuleStr}]`,
- 'includeCommandLineAPI': true
- }
- }, (message) => {
- checkException(message);
- assert.strictEqual(message['result']['value'], true);
+ });
+ checkException(result);
+ assert.deepStrictEqual(JSON.parse(result['result']['value']),
+ { old: 'yes' });
+ // remove module from require.cache
+ result = await session.send(
+ {
+ 'method': 'Runtime.evaluate', 'params': {
+ 'expression': `delete require.cache[${testModuleStr}]`,
+ 'includeCommandLineAPI': true
}
- ],
- [ // require again, should get fresh (empty) exports
- {
- 'method': 'Runtime.evaluate', 'params': {
- 'expression': `JSON.stringify(require(${testModuleStr}))`,
- 'includeCommandLineAPI': true
- }
- }, (message) => {
- checkException(message);
- assert.deepStrictEqual(JSON.parse(message['result']['value']), {});
+ });
+ checkException(result);
+ assert.strictEqual(result['result']['value'], true);
+ // require again, should get fresh (empty) exports
+ result = await session.send(
+ {
+ 'method': 'Runtime.evaluate', 'params': {
+ 'expression': `JSON.stringify(require(${testModuleStr}))`,
+ 'includeCommandLineAPI': true
}
- ],
- [ // require 2nd module, exports an empty object
- {
- 'method': 'Runtime.evaluate', 'params': {
- 'expression': `JSON.stringify(require(${printAModuleStr}))`,
- 'includeCommandLineAPI': true
- }
- }, (message) => {
- checkException(message);
- assert.deepStrictEqual(JSON.parse(message['result']['value']), {});
+ });
+ checkException(result);
+ assert.deepStrictEqual(JSON.parse(result['result']['value']), {});
+ // require 2nd module, exports an empty object
+ result = await session.send(
+ {
+ 'method': 'Runtime.evaluate', 'params': {
+ 'expression': `JSON.stringify(require(${printAModuleStr}))`,
+ 'includeCommandLineAPI': true
}
- ],
- [ // both modules end up with the same module.parent
- {
- 'method': 'Runtime.evaluate', 'params': {
- 'expression': `JSON.stringify({
- parentsEqual:
- require.cache[${testModuleStr}].parent ===
- require.cache[${printAModuleStr}].parent,
- parentId: require.cache[${testModuleStr}].parent.id,
- })`,
- 'includeCommandLineAPI': true
- }
- }, (message) => {
- checkException(message);
- assert.deepStrictEqual(JSON.parse(message['result']['value']), {
- parentsEqual: true,
- parentId: '<inspector console>'
- });
+ });
+ checkException(result);
+ assert.deepStrictEqual(JSON.parse(result['result']['value']), {});
+ // both modules end up with the same module.parent
+ result = await session.send(
+ {
+ 'method': 'Runtime.evaluate', 'params': {
+ 'expression': `JSON.stringify({
+ parentsEqual:
+ require.cache[${testModuleStr}].parent ===
+ require.cache[${printAModuleStr}].parent,
+ parentId: require.cache[${testModuleStr}].parent.id,
+ })`,
+ 'includeCommandLineAPI': true
}
- ],
- [ // the `require` in the module shadows the command line API's `require`
- {
- 'method': 'Debugger.evaluateOnCallFrame', 'params': {
- 'callFrameId': '{"ordinal":0,"injectedScriptId":1}',
- 'expression': `(
- require(${printBModuleStr}),
- require.cache[${printBModuleStr}].parent.id
- )`,
- 'includeCommandLineAPI': true
- }
- }, (message) => {
- checkException(message);
- assert.notStrictEqual(message['result']['value'],
- '<inspector console>');
+ });
+ checkException(result);
+ assert.deepStrictEqual(JSON.parse(result['result']['value']), {
+ parentsEqual: true,
+ parentId: '<inspector console>'
+ });
+ // the `require` in the module shadows the command line API's `require`
+ result = await session.send(
+ {
+ 'method': 'Debugger.evaluateOnCallFrame', 'params': {
+ 'callFrameId': '{"ordinal":0,"injectedScriptId":1}',
+ 'expression': `(
+ require(${printBModuleStr}),
+ require.cache[${printBModuleStr}].parent.id
+ )`,
+ 'includeCommandLineAPI': true
}
- ],
- ]);
+ });
+ checkException(result);
+ assert.notStrictEqual(result['result']['value'],
+ '<inspector console>');
}
-function testWaitsForFrontendDisconnect(session, harness) {
- console.log('[test]', 'Verify node waits for the frontend to disconnect');
- session.sendInspectorCommands({ 'method': 'Debugger.resume' })
- .expectMessages(setupExpectContextDestroyed(1))
- .expectStderrOutput('Waiting for the debugger to disconnect...')
- .disconnect(true);
-}
+async function runTest() {
+ const child = new NodeInstance();
+ checkListResponse(await child.httpGet(null, '/json'));
+ checkListResponse(await child.httpGet(null, '/json/list'));
+ checkVersion(await child.httpGet(null, '/json/version'));
+
+ await child.httpGet(null, '/json/activate').catch(checkBadPath);
+ await child.httpGet(null, '/json/activate/boom').catch(checkBadPath);
+ await child.httpGet(null, '/json/badpath').catch(checkBadPath);
-function runTests(harness) {
- harness
- .testHttpResponse(null, '/json', checkListResponse)
- .testHttpResponse(null, '/json/list', checkListResponse)
- .testHttpResponse(null, '/json/version', checkVersion)
- .testHttpResponse(null, '/json/activate', checkBadPath)
- .testHttpResponse(null, '/json/activate/boom', checkBadPath)
- .testHttpResponse(null, '/json/badpath', checkBadPath)
- .runFrontendSession([
- testNoUrlsWhenConnected,
- testBreakpointOnStart,
- testSetBreakpointAndResume,
- testInspectScope,
- testI18NCharacters,
- testCommandLineAPI,
- testWaitsForFrontendDisconnect
- ]).expectShutDown(55);
+ const session = await child.connectInspectorSession();
+ assertNoUrlsWhileConnected(await child.httpGet(null, '/json/list'));
+ await testBreakpointOnStart(session);
+ await testBreakpoint(session);
+ await testI18NCharacters(session);
+ await testCommandLineAPI(session);
+ await session.runToCompletion();
+ assert.strictEqual(55, (await child.expectShutdown()).exitCode);
}
-helper.startNodeForInspectorTest(runTests);
+common.crashOnUnhandledRejection();
+
+runTest();
diff --git a/test/inspector/test-ip-detection.js b/test/inspector/test-ip-detection.js
new file mode 100644
index 0000000000..5a6a116144
--- /dev/null
+++ b/test/inspector/test-ip-detection.js
@@ -0,0 +1,48 @@
+'use strict';
+const common = require('../common');
+
+common.skipIfInspectorDisabled();
+
+const assert = require('assert');
+const { NodeInstance } = require('./inspector-helper.js');
+const os = require('os');
+
+const ip = pickIPv4Address();
+
+if (!ip)
+ common.skip('No IP address found');
+
+function checkIpAddress(ip, response) {
+ const res = response[0];
+ const wsUrl = res['webSocketDebuggerUrl'];
+ assert.ok(wsUrl);
+ const match = wsUrl.match(/^ws:\/\/(.*):\d+\/(.*)/);
+ assert.strictEqual(ip, match[1]);
+ assert.strictEqual(res['id'], match[2]);
+ assert.strictEqual(ip, res['devtoolsFrontendUrl'].match(/.*ws=(.*):\d+/)[1]);
+}
+
+function pickIPv4Address() {
+ for (const i of [].concat(...Object.values(os.networkInterfaces()))) {
+ if (i.family === 'IPv4' && i.address !== '127.0.0.1')
+ return i.address;
+ }
+}
+
+async function test() {
+ const instance = new NodeInstance('--inspect-brk=0.0.0.0:0');
+ try {
+ checkIpAddress(ip, await instance.httpGet(ip, '/json/list'));
+ } catch (error) {
+ if (error.code === 'EHOSTUNREACH') {
+ common.printSkipMessage('Unable to connect to self');
+ } else {
+ throw error;
+ }
+ }
+ instance.kill();
+}
+
+common.crashOnUnhandledRejection();
+
+test();
diff --git a/test/inspector/test-not-blocked-on-idle.js b/test/inspector/test-not-blocked-on-idle.js
index 1573e875cd..8684d6f314 100644
--- a/test/inspector/test-not-blocked-on-idle.js
+++ b/test/inspector/test-not-blocked-on-idle.js
@@ -1,21 +1,21 @@
'use strict';
const common = require('../common');
common.skipIfInspectorDisabled();
-const helper = require('./inspector-helper.js');
+const { NodeInstance } = require('./inspector-helper.js');
-function shouldShutDown(session) {
- session
- .sendInspectorCommands([
- { 'method': 'Debugger.enable' },
- { 'method': 'Debugger.pause' },
- ])
- .disconnect(true);
-}
-
-function runTests(harness) {
+async function runTests() {
+ const script = 'setInterval(() => {debugger;}, 60000);';
+ const node = new NodeInstance('--inspect=0', script);
// 1 second wait to make sure the inferior began running the script
- setTimeout(() => harness.runFrontendSession([shouldShutDown]).kill(), 1000);
+ await new Promise((resolve) => setTimeout(() => resolve(), 1000));
+ const session = await node.connectInspectorSession();
+ await session.send([
+ { 'method': 'Debugger.enable' },
+ { 'method': 'Debugger.pause' }
+ ]);
+ session.disconnect();
+ node.kill();
}
-const script = 'setInterval(() => {debugger;}, 60000);';
-helper.startNodeForInspectorTest(runTests, '--inspect', script);
+common.crashOnUnhandledRejection();
+runTests();
diff --git a/test/inspector/test-off-no-session.js b/test/inspector/test-off-no-session.js
deleted file mode 100644
index 2ec54f3651..0000000000
--- a/test/inspector/test-off-no-session.js
+++ /dev/null
@@ -1,11 +0,0 @@
-'use strict';
-const common = require('../common');
-common.skipIfInspectorDisabled();
-const helper = require('./inspector-helper.js');
-
-function testStop(harness) {
- harness.expectShutDown(42);
-}
-
-helper.startNodeForInspectorTest(testStop, '--inspect',
- 'process._debugEnd();process.exit(42);');
diff --git a/test/inspector/test-off-with-session-then-on.js b/test/inspector/test-off-with-session-then-on.js
deleted file mode 100644
index bd6455699d..0000000000
--- a/test/inspector/test-off-with-session-then-on.js
+++ /dev/null
@@ -1,24 +0,0 @@
-'use strict';
-const common = require('../common');
-common.skipIfInspectorDisabled();
-const helper = require('./inspector-helper.js');
-
-function testResume(session) {
- session.sendCommandsAndExpectClose([
- { 'method': 'Runtime.runIfWaitingForDebugger' }
- ]);
-}
-
-function testDisconnectSession(harness) {
- harness
- .runFrontendSession([
- testResume,
- ]).expectShutDown(42);
-}
-
-const script = 'process._debugEnd();' +
- 'process._debugProcess(process.pid);' +
- 'setTimeout(() => {console.log("Done");process.exit(42)});';
-
-helper.startNodeForInspectorTest(testDisconnectSession, '--inspect-brk',
- script);
diff --git a/test/inspector/test-inspector-port-cluster.js b/test/inspector/test-port-cluster.js
index 3b2464d812..3b2464d812 100644
--- a/test/inspector/test-inspector-port-cluster.js
+++ b/test/inspector/test-port-cluster.js
diff --git a/test/inspector/test-inspector-port-zero-cluster.js b/test/inspector/test-port-zero-cluster.js
index f64e05f314..f64e05f314 100644
--- a/test/inspector/test-inspector-port-zero-cluster.js
+++ b/test/inspector/test-port-zero-cluster.js
diff --git a/test/inspector/test-inspector-port-zero.js b/test/inspector/test-port-zero.js
index a3eb08e5fb..a3eb08e5fb 100644
--- a/test/inspector/test-inspector-port-zero.js
+++ b/test/inspector/test-port-zero.js
diff --git a/test/inspector/test-stop-profile-after-done.js b/test/inspector/test-stop-profile-after-done.js
new file mode 100644
index 0000000000..314c429d46
--- /dev/null
+++ b/test/inspector/test-stop-profile-after-done.js
@@ -0,0 +1,30 @@
+'use strict';
+const common = require('../common');
+common.skipIfInspectorDisabled();
+const assert = require('assert');
+const { NodeInstance } = require('./inspector-helper.js');
+
+async function runTests() {
+ const child = new NodeInstance(['--inspect=0'],
+ `let c = 0;
+ const interval = setInterval(() => {
+ console.log(new Object());
+ if (c++ === 10)
+ clearInterval(interval);
+ }, 10);`);
+ const session = await child.connectInspectorSession();
+
+ session.send([
+ { 'method': 'Profiler.setSamplingInterval', 'params': { 'interval': 100 } },
+ { 'method': 'Profiler.enable' },
+ { 'method': 'Runtime.runIfWaitingForDebugger' },
+ { 'method': 'Profiler.start' }]);
+ while (await child.nextStderrString() !==
+ 'Waiting for the debugger to disconnect...');
+ await session.send({ 'method': 'Profiler.stop' });
+ session.disconnect();
+ assert.strictEqual(0, (await child.expectShutdown()).exitCode);
+}
+
+common.crashOnUnhandledRejection();
+runTests();
diff --git a/test/inspector/test-inspector-stops-no-file.js b/test/inspector/test-stops-no-file.js
index 772063b279..772063b279 100644
--- a/test/inspector/test-inspector-stops-no-file.js
+++ b/test/inspector/test-stops-no-file.js