diff options
author | Eugene Ostroukhov <eostroukhov@chromium.org> | 2017-05-31 15:14:52 -0700 |
---|---|---|
committer | Eugene Ostroukhov <eostroukhov@chromium.org> | 2017-06-05 08:47:43 -0700 |
commit | e6dcc3dfa9dbbf59cf6f67425e47c47bea70fe2c (patch) | |
tree | 1b34b15cf54eba71bf5e3f21aa16d6e5c4ada01a /test | |
parent | 5d9dc94509253845642e617f9d6d47ce0d09d7da (diff) | |
download | node-new-e6dcc3dfa9dbbf59cf6f67425e47c47bea70fe2c.tar.gz |
inspector: Allows reentry when paused
This change allows reentering the message dispatch loop when the Node is
paused. This is necessary when the pause happened as a result of the
message sent by a debug frontend, such as evaluating a function with a
breakpoint inside.
Fixes: https://github.com/nodejs/node/issues/13320
PR-URL: https://github.com/nodejs/node/pull/13350
Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'test')
-rw-r--r-- | test/inspector/global-function.js | 13 | ||||
-rw-r--r-- | test/inspector/inspector-helper.js | 28 | ||||
-rw-r--r-- | test/inspector/test-inspector-break-when-eval.js | 128 |
3 files changed, 160 insertions, 9 deletions
diff --git a/test/inspector/global-function.js b/test/inspector/global-function.js new file mode 100644 index 0000000000..d72bacd7ca --- /dev/null +++ b/test/inspector/global-function.js @@ -0,0 +1,13 @@ +'use strict'; // eslint-disable-line required-modules +let invocations = 0; +const interval = setInterval(() => {}, 1000); + +global.sum = function() { + const a = 1; + const b = 2; + const c = a + b; + clearInterval(interval); + console.log(invocations++, c); +}; + +console.log('Ready!'); diff --git a/test/inspector/inspector-helper.js b/test/inspector/inspector-helper.js index 755c357077..faf932f495 100644 --- a/test/inspector/inspector-helper.js +++ b/test/inspector/inspector-helper.js @@ -10,6 +10,7 @@ const url = require('url'); 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) { @@ -183,7 +184,6 @@ TestSession.prototype.processMessage_ = function(message) { this.messagefilter_ && this.messagefilter_(message); const id = message['id']; if (id) { - assert.strictEqual(id, this.expectedId_); this.expectedId_++; if (this.responseCheckers_[id]) { const messageJSON = JSON.stringify(message); @@ -207,16 +207,21 @@ TestSession.prototype.sendAll_ = function(commands, callback) { if (!commands.length) { callback(); } else { - this.lastId_++; + let id = ++this.lastId_; let command = commands[0]; if (command instanceof Array) { - this.responseCheckers_[this.lastId_] = command[1]; + this.responseCheckers_[id] = command[1]; command = command[0]; } if (command instanceof Function) command = command(); - this.messages_[this.lastId_] = command; - send(this.socket_, command, this.lastId_, + if (!command[DONT_EXPECT_RESPONSE_SYMBOL]) { + this.messages_[id] = command; + } else { + id += 100000; + this.lastId_--; + } + send(this.socket_, command, id, () => this.sendAll_(commands.slice(1), callback)); } }; @@ -497,12 +502,13 @@ Harness.prototype.kill = function() { exports.startNodeForInspectorTest = function(callback, inspectorFlags = ['--inspect-brk'], - opt_script_contents) { + scriptContents = '', + scriptFile = mainScript) { const args = [].concat(inspectorFlags); - if (opt_script_contents) { - args.push('-e', opt_script_contents); + if (scriptContents) { + args.push('-e', scriptContents); } else { - args.push(mainScript); + args.push(scriptFile); } const child = spawn(process.execPath, args); @@ -534,3 +540,7 @@ exports.startNodeForInspectorTest = function(callback, exports.mainScriptSource = function() { return fs.readFileSync(mainScript, 'utf8'); }; + +exports.markMessageNoResponse = function(message) { + message[DONT_EXPECT_RESPONSE_SYMBOL] = true; +}; diff --git a/test/inspector/test-inspector-break-when-eval.js b/test/inspector/test-inspector-break-when-eval.js new file mode 100644 index 0000000000..388edf6f5c --- /dev/null +++ b/test/inspector/test-inspector-break-when-eval.js @@ -0,0 +1,128 @@ +'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); |