diff options
author | Florian Dold <florian.dold@gmail.com> | 2019-04-03 15:43:32 +0200 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2019-04-03 15:45:57 +0200 |
commit | 71e285b94c7edaa43aa8115965cf5a36b8e0f80a (patch) | |
tree | 7d4aa9d0d5aff686b106cd5da72ba77960c4af43 /deps/node/deps/node-inspect/test/cli | |
parent | 7dadf9356b4f3f4137ce982ea5bb960283116e9a (diff) | |
download | akono-71e285b94c7edaa43aa8115965cf5a36b8e0f80a.tar.gz akono-71e285b94c7edaa43aa8115965cf5a36b8e0f80a.tar.bz2 akono-71e285b94c7edaa43aa8115965cf5a36b8e0f80a.zip |
Node.js v11.13.0
Diffstat (limited to 'deps/node/deps/node-inspect/test/cli')
16 files changed, 1106 insertions, 0 deletions
diff --git a/deps/node/deps/node-inspect/test/cli/backtrace.test.js b/deps/node/deps/node-inspect/test/cli/backtrace.test.js new file mode 100644 index 00000000..127ea56b --- /dev/null +++ b/deps/node/deps/node-inspect/test/cli/backtrace.test.js @@ -0,0 +1,30 @@ +'use strict'; +const Path = require('path'); + +const { test } = require('tap'); + +const startCLI = require('./start-cli'); + +test('display and navigate backtrace', (t) => { + const script = Path.join('examples', 'backtrace.js'); + const cli = startCLI([script]); + + function onFatal(error) { + cli.quit(); + throw error; + } + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => cli.stepCommand('c')) + .then(() => cli.command('bt')) + .then(() => { + t.match(cli.output, `#0 topFn ${script}:7:2`); + }) + .then(() => cli.command('backtrace')) + .then(() => { + t.match(cli.output, `#0 topFn ${script}:7:2`); + }) + .then(() => cli.quit()) + .then(null, onFatal); +}); diff --git a/deps/node/deps/node-inspect/test/cli/break.test.js b/deps/node/deps/node-inspect/test/cli/break.test.js new file mode 100644 index 00000000..ce8c8d6d --- /dev/null +++ b/deps/node/deps/node-inspect/test/cli/break.test.js @@ -0,0 +1,195 @@ +'use strict'; +const Path = require('path'); + +const { test } = require('tap'); + +const startCLI = require('./start-cli'); + +test('stepping through breakpoints', (t) => { + const script = Path.join('examples', 'break.js'); + const cli = startCLI([script]); + + function onFatal(error) { + cli.quit(); + throw error; + } + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => { + t.match( + cli.output, + `break in ${script}:1`, + 'pauses in the first line of the script'); + t.match( + cli.output, + /> 1 \(function \([^)]+\) \{ const x = 10;/, + 'shows the source and marks the current line'); + }) + .then(() => cli.stepCommand('n')) + .then(() => { + t.match( + cli.output, + `break in ${script}:2`, + 'pauses in next line of the script'); + t.match( + cli.output, + '> 2 let name = \'World\';', + 'marks the 2nd line'); + }) + .then(() => cli.stepCommand('next')) + .then(() => { + t.match( + cli.output, + `break in ${script}:3`, + 'pauses in next line of the script'); + t.match( + cli.output, + '> 3 name = \'Robin\';', + 'marks the 3nd line'); + }) + .then(() => cli.stepCommand('cont')) + .then(() => { + t.match( + cli.output, + `break in ${script}:10`, + 'pauses on the next breakpoint'); + t.match( + cli.output, + '>10 debugger;', + 'marks the debugger line'); + }) + + // Prepare additional breakpoints + .then(() => cli.command('sb("break.js", 6)')) + .then(() => t.notMatch(cli.output, 'Could not resolve breakpoint')) + .then(() => cli.command('sb("otherFunction()")')) + .then(() => cli.command('sb(16)')) + .then(() => t.notMatch(cli.output, 'Could not resolve breakpoint')) + .then(() => cli.command('breakpoints')) + .then(() => { + t.match(cli.output, `#0 ${script}:6`); + t.match(cli.output, `#1 ${script}:16`); + }) + + .then(() => cli.command('list()')) + .then(() => { + t.match(cli.output, '>10 debugger;', 'prints and marks current line'); + t.strictDeepEqual( + cli.parseSourceLines(), + [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + 'prints 5 lines before and after'); + }) + .then(() => cli.command('list(2)')) + .then(() => { + t.match(cli.output, '>10 debugger;', 'prints and marks current line'); + t.strictDeepEqual( + cli.parseSourceLines(), + [8, 9, 10, 11, 12], + 'prints 2 lines before and after'); + }) + + .then(() => cli.stepCommand('s')) + .then(() => cli.stepCommand('')) + .then(() => { + t.match( + cli.output, + 'break in timers.js', + 'entered timers.js'); + }) + .then(() => cli.stepCommand('cont')) + .then(() => { + t.match( + cli.output, + `break in ${script}:16`, + 'found breakpoint we set above w/ line number only'); + }) + .then(() => cli.stepCommand('cont')) + .then(() => { + t.match( + cli.output, + `break in ${script}:6`, + 'found breakpoint we set above w/ line number & script'); + }) + .then(() => cli.stepCommand('')) + .then(() => { + t.match( + cli.output, + `debugCommand in ${script}:14`, + 'found function breakpoint we set above'); + }) + .then(() => cli.quit()) + .then(null, onFatal); +}); + +test('sb before loading file', (t) => { + const script = Path.join('examples', 'cjs', 'index.js'); + const otherScript = Path.join('examples', 'cjs', 'other.js'); + const cli = startCLI([script]); + + function onFatal(error) { + cli.quit(); + throw error; + } + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => cli.command('sb("other.js", 2)')) + .then(() => { + t.match( + cli.output, + 'not loaded yet', + 'warns that the script was not loaded yet'); + }) + .then(() => cli.stepCommand('cont')) + .then(() => { + t.match( + cli.output, + `break in ${otherScript}:2`, + 'found breakpoint in file that was not loaded yet'); + }) + .then(() => cli.quit()) + .then(null, onFatal); +}); + +test('clearBreakpoint', (t) => { + const script = Path.join('examples', 'break.js'); + const cli = startCLI([script]); + + function onFatal(error) { + cli.quit(); + throw error; + } + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => cli.command('sb("break.js", 3)')) + .then(() => cli.command('sb("break.js", 9)')) + .then(() => cli.command('breakpoints')) + .then(() => { + t.match(cli.output, `#0 ${script}:3`); + t.match(cli.output, `#1 ${script}:9`); + }) + .then(() => cli.command('clearBreakpoint("break.js", 4)')) + .then(() => { + t.match(cli.output, 'Could not find breakpoint'); + }) + .then(() => cli.command('clearBreakpoint("not-such-script.js", 3)')) + .then(() => { + t.match(cli.output, 'Could not find breakpoint'); + }) + .then(() => cli.command('clearBreakpoint("break.js", 3)')) + .then(() => cli.command('breakpoints')) + .then(() => { + t.match(cli.output, `#0 ${script}:9`); + }) + .then(() => cli.stepCommand('cont')) + .then(() => { + t.match( + cli.output, + `break in ${script}:9`, + 'hits the 2nd breakpoint because the 1st was cleared'); + }) + .then(() => cli.quit()) + .then(null, onFatal); +}); diff --git a/deps/node/deps/node-inspect/test/cli/exceptions.test.js b/deps/node/deps/node-inspect/test/cli/exceptions.test.js new file mode 100644 index 00000000..18b7f185 --- /dev/null +++ b/deps/node/deps/node-inspect/test/cli/exceptions.test.js @@ -0,0 +1,68 @@ +'use strict'; +const Path = require('path'); + +const { test } = require('tap'); + +const startCLI = require('./start-cli'); + +test('break on (uncaught) exceptions', (t) => { + const script = Path.join('examples', 'exceptions.js'); + const cli = startCLI([script]); + + function onFatal(error) { + cli.quit(); + throw error; + } + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => { + t.match(cli.output, `break in ${script}:1`); + }) + // making sure it will die by default: + .then(() => cli.command('c')) + // TODO: Remove FATAL ERROR once node doesn't show a FATAL ERROR anymore + .then(() => cli.waitFor(/disconnect|FATAL ERROR/)) + + // Next run: With `breakOnException` it pauses in both places + .then(() => cli.stepCommand('r')) + .then(() => cli.waitForInitialBreak()) + .then(() => { + t.match(cli.output, `break in ${script}:1`); + }) + .then(() => cli.command('breakOnException')) + .then(() => cli.stepCommand('c')) + .then(() => { + t.match(cli.output, `exception in ${script}:3`); + }) + .then(() => cli.stepCommand('c')) + .then(() => { + t.match(cli.output, `exception in ${script}:9`); + }) + + // Next run: With `breakOnUncaught` it only pauses on the 2nd exception + .then(() => cli.command('breakOnUncaught')) + .then(() => cli.stepCommand('r')) // also, the setting survives the restart + .then(() => cli.waitForInitialBreak()) + .then(() => { + t.match(cli.output, `break in ${script}:1`); + }) + .then(() => cli.stepCommand('c')) + .then(() => { + t.match(cli.output, `exception in ${script}:9`); + }) + + // Next run: Back to the initial state! It should die again. + .then(() => cli.command('breakOnNone')) + .then(() => cli.stepCommand('r')) + .then(() => cli.waitForInitialBreak()) + .then(() => { + t.match(cli.output, `break in ${script}:1`); + }) + .then(() => cli.command('c')) + // TODO: Remove FATAL ERROR once node doesn't show a FATAL ERROR anymore + .then(() => cli.waitFor(/disconnect|FATAL ERROR/)) + + .then(() => cli.quit()) + .then(null, onFatal); +}); diff --git a/deps/node/deps/node-inspect/test/cli/exec.test.js b/deps/node/deps/node-inspect/test/cli/exec.test.js new file mode 100644 index 00000000..acfd6e34 --- /dev/null +++ b/deps/node/deps/node-inspect/test/cli/exec.test.js @@ -0,0 +1,77 @@ +'use strict'; +const { test } = require('tap'); + +const startCLI = require('./start-cli'); + +test('examples/alive.js', (t) => { + const cli = startCLI(['examples/alive.js']); + + function onFatal(error) { + cli.quit(); + throw error; + } + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => cli.command('exec [typeof heartbeat, typeof process.exit]')) + .then(() => { + t.match(cli.output, '[ \'function\', \'function\' ]', 'works w/o paren'); + }) + .then(() => cli.command('repl')) + .then(() => { + t.match( + cli.output, + 'Press Ctrl + C to leave debug repl\n> ', + 'shows hint for how to leave repl'); + t.notMatch(cli.output, 'debug>', 'changes the repl style'); + }) + .then(() => cli.command('[typeof heartbeat, typeof process.exit]')) + .then(() => cli.waitFor(/function/)) + .then(() => cli.waitForPrompt()) + .then(() => { + t.match( + cli.output, + '[ \'function\', \'function\' ]', 'can evaluate in the repl'); + t.match(cli.output, /> $/); + }) + .then(() => cli.ctrlC()) + .then(() => cli.waitFor(/debug> $/)) + .then(() => cli.command('exec("[typeof heartbeat, typeof process.exit]")')) + .then(() => { + t.match(cli.output, '[ \'function\', \'function\' ]', 'works w/ paren'); + }) + .then(() => cli.command('cont')) + .then(() => cli.command('exec [typeof heartbeat, typeof process.exit]')) + .then(() => { + t.match( + cli.output, + '[ \'undefined\', \'function\' ]', + 'non-paused exec can see global but not module-scope values'); + }) + .then(() => cli.quit()) + .then(null, onFatal); +}); + +test('exec .scope', (t) => { + const cli = startCLI(['examples/backtrace.js']); + + function onFatal(error) { + cli.quit(); + throw error; + } + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => cli.stepCommand('c')) + .then(() => cli.command('exec .scope')) + .then(() => { + t.match( + cli.output, + '\'moduleScoped\'', 'displays closure from module body'); + t.match(cli.output, '\'a\'', 'displays local / function arg'); + t.match(cli.output, '\'l1\'', 'displays local scope'); + t.notMatch(cli.output, '\'encodeURIComponent\'', 'omits global scope'); + }) + .then(() => cli.quit()) + .then(null, onFatal); +}); diff --git a/deps/node/deps/node-inspect/test/cli/heap-profiler.test.js b/deps/node/deps/node-inspect/test/cli/heap-profiler.test.js new file mode 100644 index 00000000..ebd734e0 --- /dev/null +++ b/deps/node/deps/node-inspect/test/cli/heap-profiler.test.js @@ -0,0 +1,34 @@ +'use strict'; +const { test } = require('tap'); +const { readFileSync, unlinkSync } = require('fs'); + +const startCLI = require('./start-cli'); +const filename = 'node.heapsnapshot'; + +function cleanup() { + try { + unlinkSync(filename); + } catch (_) { + // Ignore. + } +} + +cleanup(); + +test('Heap profiler take snapshot', (t) => { + const cli = startCLI(['examples/empty.js']); + + function onFatal(error) { + cli.quit(); + throw error; + } + + // Check that the snapshot is valid JSON. + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => cli.command('takeHeapSnapshot()')) + .then(() => JSON.parse(readFileSync(filename, 'utf8'))) + .then(() => cleanup()) + .then(() => cli.quit()) + .then(null, onFatal); +}); diff --git a/deps/node/deps/node-inspect/test/cli/help.test.js b/deps/node/deps/node-inspect/test/cli/help.test.js new file mode 100644 index 00000000..9f0c081b --- /dev/null +++ b/deps/node/deps/node-inspect/test/cli/help.test.js @@ -0,0 +1,22 @@ +'use strict'; +const { test } = require('tap'); + +const startCLI = require('./start-cli'); + +test('examples/empty.js', (t) => { + const cli = startCLI(['examples/empty.js']); + + function onFatal(error) { + cli.quit(); + throw error; + } + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => cli.command('help')) + .then(() => { + t.match(cli.output, /run, restart, r\s+/m); + }) + .then(() => cli.quit()) + .then(null, onFatal); +}); diff --git a/deps/node/deps/node-inspect/test/cli/invalid-args.test.js b/deps/node/deps/node-inspect/test/cli/invalid-args.test.js new file mode 100644 index 00000000..c1aaeb6a --- /dev/null +++ b/deps/node/deps/node-inspect/test/cli/invalid-args.test.js @@ -0,0 +1,54 @@ +'use strict'; +const Path = require('path'); +const { createServer } = require('net'); + +const { test } = require('tap'); + +const startCLI = require('./start-cli'); + +test('launch CLI w/o args', (t) => { + const cli = startCLI([]); + return cli.quit() + .then((code) => { + t.equal(code, 1, 'exits with non-zero exit code'); + t.match(cli.output, /^Usage:/, 'Prints usage info'); + }); +}); + +test('launch w/ invalid host:port', (t) => { + const cli = startCLI(['localhost:914']); + return cli.quit() + .then((code) => { + t.match( + cli.output, + 'failed to connect', + 'Tells the user that the connection failed'); + t.equal(code, 1, 'exits with non-zero exit code'); + }); +}); + +test('launch w/ unavailable port', async (t) => { + const blocker = createServer((socket) => socket.end()); + const port = await new Promise((resolve, reject) => { + blocker.on('error', reject); + blocker.listen(0, '127.0.0.1', () => resolve(blocker.address().port)); + }); + + try { + const script = Path.join('examples', 'three-lines.js'); + const cli = startCLI([`--port=${port}`, script]); + const code = await cli.quit(); + + t.notMatch( + cli.output, + 'report this bug', + 'Omits message about reporting this as a bug'); + t.match( + cli.output, + `waiting for 127.0.0.1:${port} to be free`, + 'Tells the user that the port wasn\'t available'); + t.equal(code, 1, 'exits with non-zero exit code'); + } finally { + blocker.close(); + } +}); diff --git a/deps/node/deps/node-inspect/test/cli/launch.test.js b/deps/node/deps/node-inspect/test/cli/launch.test.js new file mode 100644 index 00000000..8808d47a --- /dev/null +++ b/deps/node/deps/node-inspect/test/cli/launch.test.js @@ -0,0 +1,176 @@ +'use strict'; +const Path = require('path'); + +const { test } = require('tap'); + +const startCLI = require('./start-cli'); + +test('custom port', (t) => { + const CUSTOM_PORT = '9230'; + const script = Path.join('examples', 'three-lines.js'); + + const cli = startCLI([`--port=${CUSTOM_PORT}`, script]); + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => { + t.match(cli.output, 'debug>', 'prints a prompt'); + t.match( + cli.output, + new RegExp(`< Debugger listening on [^\n]*${CUSTOM_PORT}`), + 'forwards child output'); + }) + .then(() => cli.quit()) + .then((code) => { + t.equal(code, 0, 'exits with success'); + }); +}); + +test('random port', (t) => { + const script = Path.join('examples', 'three-lines.js'); + + const cli = startCLI(['--port=0', script]); + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => { + t.match(cli.output, 'debug>', 'prints a prompt'); + t.match( + cli.output, + /< Debugger listening on /, + 'forwards child output'); + }) + .then(() => cli.quit()) + .then((code) => { + t.equal(code, 0, 'exits with success'); + }); +}); + +test('random port with --inspect-port=0', (t) => { + const script = Path.join('examples', 'three-lines.js'); + + const cli = startCLI([script], ['--inspect-port=0']); + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => { + t.match(cli.output, 'debug>', 'prints a prompt'); + t.match( + cli.output, + /< Debugger listening on /, + 'forwards child output'); + }) + .then(() => cli.quit()) + .then((code) => { + t.equal(code, 0, 'exits with success'); + }); +}); + +test('examples/three-lines.js', (t) => { + const script = Path.join('examples', 'three-lines.js'); + const cli = startCLI([script]); + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => { + t.match(cli.output, 'debug>', 'prints a prompt'); + t.match( + cli.output, + /< Debugger listening on [^\n]*9229/, + 'forwards child output'); + }) + .then(() => cli.command('["hello", "world"].join(" ")')) + .then(() => { + t.match(cli.output, 'hello world', 'prints the result'); + }) + .then(() => cli.command('')) + .then(() => { + t.match(cli.output, 'hello world', 'repeats the last command on <enter>'); + }) + .then(() => cli.command('version')) + .then(() => { + t.match(cli.output, process.versions.v8, 'version prints the v8 version'); + }) + .then(() => cli.quit()) + .then((code) => { + t.equal(code, 0, 'exits with success'); + }); +}); + +test('run after quit / restart', (t) => { + const script = Path.join('examples', 'three-lines.js'); + const cli = startCLI([script]); + + function onFatal(error) { + cli.quit(); + throw error; + } + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => cli.stepCommand('n')) + .then(() => { + t.match( + cli.output, + `break in ${script}:2`, + 'steps to the 2nd line'); + }) + .then(() => cli.command('cont')) + .then(() => cli.waitFor(/disconnect/)) + .then(() => { + t.match( + cli.output, + 'Waiting for the debugger to disconnect', + 'the child was done'); + }) + .then(() => { + // On windows the socket won't close by itself + return cli.command('kill'); + }) + .then(() => cli.command('cont')) + .then(() => cli.waitFor(/start the app/)) + .then(() => { + t.match(cli.output, 'Use `run` to start the app again'); + }) + .then(() => cli.stepCommand('run')) + .then(() => cli.waitForInitialBreak()) + .then(() => cli.waitForPrompt()) + .then(() => { + t.match( + cli.output, + `break in ${script}:1`, + 'is back at the beginning'); + }) + .then(() => cli.stepCommand('n')) + .then(() => { + t.match( + cli.output, + `break in ${script}:2`, + 'steps to the 2nd line'); + }) + .then(() => cli.stepCommand('restart')) + .then(() => cli.waitForInitialBreak()) + .then(() => { + t.match( + cli.output, + `break in ${script}:1`, + 'is back at the beginning'); + }) + .then(() => cli.command('kill')) + .then(() => cli.command('cont')) + .then(() => cli.waitFor(/start the app/)) + .then(() => { + t.match(cli.output, 'Use `run` to start the app again'); + }) + .then(() => cli.stepCommand('run')) + .then(() => cli.waitForInitialBreak()) + .then(() => cli.waitForPrompt()) + .then(() => { + t.match( + cli.output, + `break in ${script}:1`, + 'is back at the beginning'); + }) + .then(() => cli.quit()) + .then(null, onFatal); +}); diff --git a/deps/node/deps/node-inspect/test/cli/low-level.test.js b/deps/node/deps/node-inspect/test/cli/low-level.test.js new file mode 100644 index 00000000..77e3fc2b --- /dev/null +++ b/deps/node/deps/node-inspect/test/cli/low-level.test.js @@ -0,0 +1,34 @@ +'use strict'; +const { test } = require('tap'); + +const startCLI = require('./start-cli'); + +test('Debugger agent direct access', (t) => { + const cli = startCLI(['examples/three-lines.js']); + const scriptPattern = /^\* (\d+): examples(?:\/|\\)three-lines.js/; + + function onFatal(error) { + cli.quit(); + throw error; + } + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => cli.command('scripts')) + .then(() => { + const [, scriptId] = cli.output.match(scriptPattern); + return cli.command( + `Debugger.getScriptSource({ scriptId: '${scriptId}' })` + ); + }) + .then(() => { + t.match( + cli.output, + /scriptSource: '\(function \(/); + t.match( + cli.output, + /let x = 1;/); + }) + .then(() => cli.quit()) + .then(null, onFatal); +}); diff --git a/deps/node/deps/node-inspect/test/cli/pid.test.js b/deps/node/deps/node-inspect/test/cli/pid.test.js new file mode 100644 index 00000000..15d7fdea --- /dev/null +++ b/deps/node/deps/node-inspect/test/cli/pid.test.js @@ -0,0 +1,52 @@ +'use strict'; +const { spawn } = require('child_process'); +const Path = require('path'); + +const { test } = require('tap'); + +const startCLI = require('./start-cli'); + +function launchTarget(...args) { + const childProc = spawn(process.execPath, args); + return Promise.resolve(childProc); +} + +// process.debugPort is our proxy for "the version of node used to run this +// test suite doesn't support SIGUSR1 for enabling --inspect for a process". +const defaultsToOldProtocol = process.debugPort === 5858; + +test('examples/alive.js', { skip: defaultsToOldProtocol }, (t) => { + const script = Path.join('examples', 'alive.js'); + let cli = null; + let target = null; + + function cleanup(error) { + if (cli) { + cli.quit(); + cli = null; + } + if (target) { + target.kill(); + target = null; + } + if (error) throw error; + } + + return launchTarget(script) + .then((childProc) => { + target = childProc; + cli = startCLI(['-p', `${target.pid}`]); + return cli.waitForPrompt(); + }) + .then(() => cli.command('sb("alive.js", 3)')) + .then(() => cli.waitFor(/break/)) + .then(() => cli.waitForPrompt()) + .then(() => { + t.match( + cli.output, + '> 3 ++x;', + 'marks the 3rd line'); + }) + .then(() => cleanup()) + .then(null, cleanup); +}); diff --git a/deps/node/deps/node-inspect/test/cli/preserve-breaks.test.js b/deps/node/deps/node-inspect/test/cli/preserve-breaks.test.js new file mode 100644 index 00000000..94f61408 --- /dev/null +++ b/deps/node/deps/node-inspect/test/cli/preserve-breaks.test.js @@ -0,0 +1,64 @@ +'use strict'; +const Path = require('path'); + +const { test } = require('tap'); + +const startCLI = require('./start-cli'); + +test('run after quit / restart', (t) => { + const script = Path.join('examples', 'three-lines.js'); + const cli = startCLI([script]); + + function onFatal(error) { + cli.quit(); + throw error; + } + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => cli.command('breakpoints')) + .then(() => { + t.match(cli.output, 'No breakpoints yet'); + }) + .then(() => cli.command('sb(2)')) + .then(() => cli.command('sb(3)')) + .then(() => cli.command('breakpoints')) + .then(() => { + t.match(cli.output, `#0 ${script}:2`); + t.match(cli.output, `#1 ${script}:3`); + }) + .then(() => cli.stepCommand('c')) // hit line 2 + .then(() => cli.stepCommand('c')) // hit line 3 + .then(() => { + t.match(cli.output, `break in ${script}:3`); + }) + .then(() => cli.command('restart')) + .then(() => cli.waitForInitialBreak()) + .then(() => { + t.match(cli.output, `break in ${script}:1`); + }) + .then(() => cli.stepCommand('c')) + .then(() => { + t.match(cli.output, `break in ${script}:2`); + }) + .then(() => cli.stepCommand('c')) + .then(() => { + t.match(cli.output, `break in ${script}:3`); + }) + .then(() => cli.command('breakpoints')) + .then(() => { + if (process.platform === 'aix') { + // TODO: There is a known issue on AIX where the breakpoints aren't + // properly resolved yet when we reach this point. + // Eventually that should be figured out but for now we don't want + // to fail builds because of it. + t.match(cli.output, /#0 [^\n]+three-lines\.js\$?:2/); + t.match(cli.output, /#1 [^\n]+three-lines\.js\$?:3/); + } else { + t.match(cli.output, `#0 ${script}:2`); + t.match(cli.output, `#1 ${script}:3`); + } + }) + .then(() => cli.quit()) + .then(null, onFatal); +}); diff --git a/deps/node/deps/node-inspect/test/cli/profile.test.js b/deps/node/deps/node-inspect/test/cli/profile.test.js new file mode 100644 index 00000000..0f900c5a --- /dev/null +++ b/deps/node/deps/node-inspect/test/cli/profile.test.js @@ -0,0 +1,32 @@ +'use strict'; +const { test } = require('tap'); + +const startCLI = require('./start-cli'); + +function delay(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +test('profiles', (t) => { + const cli = startCLI(['examples/empty.js']); + + function onFatal(error) { + cli.quit(); + throw error; + } + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => cli.command('exec console.profile()')) + .then(() => { + t.match(cli.output, 'undefined'); + }) + .then(() => cli.command('exec console.profileEnd()')) + .then(() => delay(250)) + .then(() => { + t.match(cli.output, 'undefined'); + t.match(cli.output, 'Captured new CPU profile.'); + }) + .then(() => cli.quit()) + .then(null, onFatal); +}); diff --git a/deps/node/deps/node-inspect/test/cli/scripts.test.js b/deps/node/deps/node-inspect/test/cli/scripts.test.js new file mode 100644 index 00000000..1546b804 --- /dev/null +++ b/deps/node/deps/node-inspect/test/cli/scripts.test.js @@ -0,0 +1,43 @@ +'use strict'; +const Path = require('path'); + +const { test } = require('tap'); + +const startCLI = require('./start-cli'); + +test('list scripts', (t) => { + const script = Path.join('examples', 'three-lines.js'); + const cli = startCLI([script]); + + function onFatal(error) { + cli.quit(); + throw error; + } + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => cli.command('scripts')) + .then(() => { + t.match( + cli.output, + /^\* \d+: examples(?:\/|\\)three-lines\.js/, + 'lists the user script'); + t.notMatch( + cli.output, + /\d+: module\.js <native>/, + 'omits node-internal scripts'); + }) + .then(() => cli.command('scripts(true)')) + .then(() => { + t.match( + cli.output, + /\* \d+: examples(?:\/|\\)three-lines\.js/, + 'lists the user script'); + t.match( + cli.output, + /\d+: module\.js <native>/, + 'includes node-internal scripts'); + }) + .then(() => cli.quit()) + .then(null, onFatal); +}); diff --git a/deps/node/deps/node-inspect/test/cli/start-cli.js b/deps/node/deps/node-inspect/test/cli/start-cli.js new file mode 100644 index 00000000..b086dcd8 --- /dev/null +++ b/deps/node/deps/node-inspect/test/cli/start-cli.js @@ -0,0 +1,156 @@ +'use strict'; +const spawn = require('child_process').spawn; + +// This allows us to keep the helper inside of `test/` without tap warning +// about "pending" test files. +const tap = require('tap'); +tap.test('startCLI', (t) => t.end()); + +const CLI = + process.env.USE_EMBEDDED_NODE_INSPECT === '1' ? + 'inspect' : + require.resolve('../../cli.js'); + +const BREAK_MESSAGE = new RegExp('(?:' + [ + 'assert', 'break', 'break on start', 'debugCommand', + 'exception', 'other', 'promiseRejection', +].join('|') + ') in', 'i'); + +function startCLI(args, flags = []) { + const child = spawn(process.execPath, [...flags, CLI, ...args]); + let isFirstStdoutChunk = true; + + const outputBuffer = []; + function bufferOutput(chunk) { + if (isFirstStdoutChunk) { + isFirstStdoutChunk = false; + outputBuffer.push(chunk.replace(/^debug>\s*/, '')); + } else { + outputBuffer.push(chunk); + } + } + + function getOutput() { + return outputBuffer.join('').toString() + .replace(/^[^\n]*?[\b]/mg, ''); + } + + child.stdout.setEncoding('utf8'); + child.stdout.on('data', bufferOutput); + child.stderr.setEncoding('utf8'); + child.stderr.on('data', bufferOutput); + + if (process.env.VERBOSE === '1') { + child.stdout.pipe(process.stderr); + child.stderr.pipe(process.stderr); + } + + return { + flushOutput() { + const output = this.output; + outputBuffer.length = 0; + return output; + }, + + waitFor(pattern, timeout = 2000) { + function checkPattern(str) { + if (Array.isArray(pattern)) { + return pattern.every((p) => p.test(str)); + } + return pattern.test(str); + } + + return new Promise((resolve, reject) => { + function checkOutput() { + if (checkPattern(getOutput())) { + tearDown(); // eslint-disable-line no-use-before-define + resolve(); + } + } + + function onChildExit() { + tearDown(); // eslint-disable-line no-use-before-define + reject(new Error( + `Child quit while waiting for ${pattern}; found: ${this.output}`)); + } + + const timer = setTimeout(() => { + tearDown(); // eslint-disable-line no-use-before-define + reject(new Error([ + `Timeout (${timeout}) while waiting for ${pattern}`, + `found: ${this.output}`, + ].join('; '))); + }, timeout); + + function tearDown() { + clearTimeout(timer); + child.stdout.removeListener('data', checkOutput); + child.removeListener('exit', onChildExit); + } + + child.on('exit', onChildExit); + child.stdout.on('data', checkOutput); + checkOutput(); + }); + }, + + waitForPrompt(timeout = 2000) { + return this.waitFor(/>\s+$/, timeout); + }, + + waitForInitialBreak(timeout = 2000) { + return this.waitFor(/break (?:on start )?in/i, timeout) + .then(() => { + if (/Break on start/.test(this.output)) { + return this.command('next', false) + .then(() => this.waitFor(/break in/, timeout)); + } + }); + }, + + ctrlC() { + return this.command('.interrupt'); + }, + + get output() { + return getOutput(); + }, + + get rawOutput() { + return outputBuffer.join('').toString(); + }, + + parseSourceLines() { + return getOutput().split('\n') + .map((line) => line.match(/(?:\*|>)?\s*(\d+)/)) + .filter((match) => match !== null) + .map((match) => +match[1]); + }, + + command(input, flush = true) { + if (flush) { + this.flushOutput(); + } + child.stdin.write(input); + child.stdin.write('\n'); + return this.waitForPrompt(); + }, + + stepCommand(input) { + this.flushOutput(); + child.stdin.write(input); + child.stdin.write('\n'); + return this + .waitFor(BREAK_MESSAGE) + .then(() => this.waitForPrompt()); + }, + + quit() { + return new Promise((resolve) => { + child.stdin.end(); + child.on('exit', resolve); + }); + }, + }; +} +module.exports = startCLI; diff --git a/deps/node/deps/node-inspect/test/cli/use-strict.test.js b/deps/node/deps/node-inspect/test/cli/use-strict.test.js new file mode 100644 index 00000000..780802a5 --- /dev/null +++ b/deps/node/deps/node-inspect/test/cli/use-strict.test.js @@ -0,0 +1,27 @@ +'use strict'; +const Path = require('path'); + +const { test } = require('tap'); + +const startCLI = require('./start-cli'); + +test('for whiles that starts with strict directive', (t) => { + const script = Path.join('examples', 'use-strict.js'); + const cli = startCLI([script]); + + function onFatal(error) { + cli.quit(); + throw error; + } + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => { + t.match( + cli.output, + /break in [^:]+:(?:1|2)[^\d]/, + 'pauses either on strict directive or first "real" line'); + }) + .then(() => cli.quit()) + .then(null, onFatal); +}); diff --git a/deps/node/deps/node-inspect/test/cli/watchers.test.js b/deps/node/deps/node-inspect/test/cli/watchers.test.js new file mode 100644 index 00000000..46bcde19 --- /dev/null +++ b/deps/node/deps/node-inspect/test/cli/watchers.test.js @@ -0,0 +1,42 @@ +'use strict'; +const { test } = require('tap'); + +const startCLI = require('./start-cli'); + +test('stepping through breakpoints', (t) => { + const cli = startCLI(['examples/break.js']); + + function onFatal(error) { + cli.quit(); + throw error; + } + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => cli.command('watch("x")')) + .then(() => cli.command('watch("\\"Hello\\"")')) + .then(() => cli.command('watch("42")')) + .then(() => cli.command('watch("NaN")')) + .then(() => cli.command('watch("true")')) + .then(() => cli.command('watch("[1, 2]")')) + .then(() => cli.command('watch("process.env")')) + .then(() => cli.command('watchers')) + .then(() => { + t.match(cli.output, 'x is not defined'); + }) + .then(() => cli.command('unwatch("42")')) + .then(() => cli.stepCommand('n')) + .then(() => { + t.match(cli.output, '0: x = 10'); + t.match(cli.output, '1: "Hello" = \'Hello\''); + t.match(cli.output, '2: NaN = NaN'); + t.match(cli.output, '3: true = true'); + t.match(cli.output, '4: [1, 2] = [ 1, 2 ]'); + t.match( + cli.output, + /5: process\.env =\n\s+\{[\s\S]+,\n\s+\.\.\. \}/, + 'shows "..." for process.env'); + }) + .then(() => cli.quit()) + .then(null, onFatal); +}); |