diff options
author | LakshmiSwethaG <lakshmigopireddy@in.ibm.com> | 2018-09-05 10:39:08 -0400 |
---|---|---|
committer | Gireesh Punathil <gpunathi@in.ibm.com> | 2019-01-18 10:36:39 +0530 |
commit | 55e0ad9ae690f6d73b0caff7b813eb4d61195b94 (patch) | |
tree | d85d8d5bf3b8efb24b0d79886af706584a076721 /test | |
parent | 7f4053ed1381d7001dbdaa12f064847aaf4d0ecf (diff) | |
download | android-node-v8-55e0ad9ae690f6d73b0caff7b813eb4d61195b94.tar.gz android-node-v8-55e0ad9ae690f6d73b0caff7b813eb4d61195b94.tar.bz2 android-node-v8-55e0ad9ae690f6d73b0caff7b813eb4d61195b94.zip |
test: add node-report tests
One test per each API, so that additional tests in future are modular.
test/common/report.js contain common functions that tests leverage.
PR-URL: https://github.com/nodejs/node/pull/22712
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michael Dawson <Michael_Dawson@ca.ibm.com>
Reviewed-By: Vse Mozhet Byt <vsemozhetbyt@gmail.com>
Diffstat (limited to 'test')
-rw-r--r-- | test/common/index.js | 7 | ||||
-rw-r--r-- | test/common/report.js | 43 | ||||
-rw-r--r-- | test/node-report/test-api-getreport.js | 28 | ||||
-rw-r--r-- | test/node-report/test-api-nohooks.js | 30 | ||||
-rw-r--r-- | test/node-report/test-api-pass-error.js | 33 | ||||
-rw-r--r-- | test/node-report/test-api-uvhandles.js | 133 | ||||
-rw-r--r-- | test/node-report/test-api.js | 29 | ||||
-rw-r--r-- | test/node-report/test-exception.js | 44 | ||||
-rw-r--r-- | test/node-report/test-fatal-error.js | 44 | ||||
-rw-r--r-- | test/node-report/test-signal.js | 80 | ||||
-rw-r--r-- | test/node-report/testcfg.py | 6 |
11 files changed, 477 insertions, 0 deletions
diff --git a/test/common/index.js b/test/common/index.js index 72ad0c3b77..e7df290c3e 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -632,6 +632,12 @@ function skipIfInspectorDisabled() { } } +function skipIfReportDisabled() { + if (!process.config.variables.node_report) { + skip('Node Report is disabled'); + } +} + function skipIf32Bits() { if (bits < 64) { skip('The tested feature is not available in 32bit builds'); @@ -758,6 +764,7 @@ module.exports = { skipIf32Bits, skipIfEslintMissing, skipIfInspectorDisabled, + skipIfReportDisabled, skipIfWorker, get localhostIPv6() { return '::1'; }, diff --git a/test/common/report.js b/test/common/report.js new file mode 100644 index 0000000000..646660da49 --- /dev/null +++ b/test/common/report.js @@ -0,0 +1,43 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +const REPORT_SECTIONS = [ 'header', + 'javascriptStack', + 'nativeStack', + 'javascriptHeap', + 'libuv', + 'environmentVariables', + 'sharedObjects' ]; + +let tmppath = ''; + +exports.findReports = (pid, path) => { + // Default filenames are of the form + // report.<date>.<time>.<pid>.<seq>.json + tmppath = path; + const format = '^report\\.\\d+\\.\\d+\\.' + pid + '\\.\\d+\\.json$'; + const filePattern = new RegExp(format); + const files = fs.readdirSync(path); + return files.filter((file) => filePattern.test(file)); +}; + +exports.validate = (report, options) => { + const jtmp = path.join(tmppath, report); + fs.readFile(jtmp, (err, data) => { + this.validateContent(data, options); + }); +}; + + +exports.validateContent = function validateContent(data, options) { + const report = JSON.parse(data); + const comp = Object.keys(report); + + // Check all sections are present + REPORT_SECTIONS.forEach((section) => { + assert.ok(comp.includes(section)); + }); +}; diff --git a/test/node-report/test-api-getreport.js b/test/node-report/test-api-getreport.js new file mode 100644 index 0000000000..178469412d --- /dev/null +++ b/test/node-report/test-api-getreport.js @@ -0,0 +1,28 @@ +'use strict'; + +// Testcase for returning report as a string via API call +const common = require('../common'); +common.skipIfReportDisabled(); +const assert = require('assert'); +if (process.argv[2] === 'child') { + console.log(process.report.getReport()); +} else { + const helper = require('../common/report.js'); + const spawnSync = require('child_process').spawnSync; + const tmpdir = require('../common/tmpdir'); + tmpdir.refresh(); + + const args = ['--experimental-report', __filename, 'child']; + const child = spawnSync(process.execPath, args, { cwd: tmpdir.path }); + const report_msg = 'Found report files'; + const std_msg = 'Found messages on stderr'; + assert.ok(child.stderr.toString().includes( + `(node:${child.pid}) ExperimentalWarning: report is an` + + ' experimental feature. This feature could change at any time'), std_msg); + const reportFiles = helper.findReports(child.pid, tmpdir.path); + assert.deepStrictEqual(reportFiles, [], report_msg); + helper.validateContent(child.stdout, { pid: child.pid, + commandline: process.execPath + + ' ' + args.join(' ') + }); +} diff --git a/test/node-report/test-api-nohooks.js b/test/node-report/test-api-nohooks.js new file mode 100644 index 0000000000..8b497e0663 --- /dev/null +++ b/test/node-report/test-api-nohooks.js @@ -0,0 +1,30 @@ +'use strict'; + +// Testcase to produce report via API call, using the no-hooks/no-signal +// interface - i.e. require('node-report/api') +const common = require('../common'); +common.skipIfReportDisabled(); +if (process.argv[2] === 'child') { + process.report.triggerReport(); +} else { + const helper = require('../common/report.js'); + const spawn = require('child_process').spawn; + const assert = require('assert'); + const tmpdir = require('../common/tmpdir'); + tmpdir.refresh(); + + const child = spawn(process.execPath, ['--experimental-report', + __filename, 'child'], + { cwd: tmpdir.path }); + child.on('exit', common.mustCall((code) => { + const report_msg = 'No reports found'; + const process_msg = 'Process exited unexpectedly'; + assert.strictEqual(code, 0, process_msg + ':' + code); + const reports = helper.findReports(child.pid, tmpdir.path); + assert.strictEqual(reports.length, 1, report_msg); + const report = reports[0]; + helper.validate(report, { pid: child.pid, + commandline: child.spawnargs.join(' ') + }); + })); +} diff --git a/test/node-report/test-api-pass-error.js b/test/node-report/test-api-pass-error.js new file mode 100644 index 0000000000..caac6c7e86 --- /dev/null +++ b/test/node-report/test-api-pass-error.js @@ -0,0 +1,33 @@ +'use strict'; + +// Testcase for passing an error object to the API call. +const common = require('../common'); +common.skipIfReportDisabled(); +const assert = require('assert'); +if (process.argv[2] === 'child') { + try { + throw new Error('Testing error handling'); + } catch { + process.report.triggerReport(); + } +} else { + const helper = require('../common/report.js'); + const spawn = require('child_process').spawn; + const tmpdir = require('../common/tmpdir'); + tmpdir.refresh(); + const child = spawn(process.execPath, ['--experimental-report', + __filename, 'child'], + { cwd: tmpdir.path }); + child.on('exit', common.mustCall((code) => { + const report_msg = 'No reports found'; + const process_msg = 'Process exited unexpectedly'; + assert.strictEqual(code, 0, process_msg); + const reports = helper.findReports(child.pid, tmpdir.path); + assert.strictEqual(reports.length, 1, report_msg); + const report = reports[0]; + helper.validate(report, { pid: child.pid, + commandline: child.spawnargs.join(' '), + expectedException: 'Testing error handling', + }); + })); +} diff --git a/test/node-report/test-api-uvhandles.js b/test/node-report/test-api-uvhandles.js new file mode 100644 index 0000000000..52f58b6e13 --- /dev/null +++ b/test/node-report/test-api-uvhandles.js @@ -0,0 +1,133 @@ +'use strict'; + +// Testcase to check reporting of uv handles. +const common = require('../common'); +common.skipIfReportDisabled(); +if (process.argv[2] === 'child') { + // Exit on loss of parent process + const exit = () => process.exit(2); + process.on('disconnect', exit); + + const fs = require('fs'); + const http = require('http'); + const spawn = require('child_process').spawn; + + // Watching files should result in fs_event/fs_poll uv handles. + let watcher; + try { + watcher = fs.watch(__filename); + } catch { + // fs.watch() unavailable + } + fs.watchFile(__filename, () => {}); + + // Child should exist when this returns as child_process.pid must be set. + const child_process = spawn(process.execPath, + ['-e', "process.stdin.on('data', (x) => " + + 'console.log(x.toString()));']); + + const timeout = setInterval(() => {}, 1000); + // Make sure the timer doesn't keep the test alive and let + // us check we detect unref'd handles correctly. + timeout.unref(); + + // Datagram socket for udp uv handles. + const dgram = require('dgram'); + const udp_socket = dgram.createSocket('udp4'); + udp_socket.bind({}); + + // Simple server/connection to create tcp uv handles. + const server = http.createServer((req, res) => { + req.on('end', () => { + // Generate the report while the connection is active. + console.log(process.report.getReport()); + child_process.kill(); + + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end(); + + // Tidy up to allow process to exit cleanly. + server.close(() => { + if (watcher) watcher.close(); + fs.unwatchFile(__filename); + udp_socket.close(); + process.removeListener('disconnect', exit); + }); + }); + req.resume(); + }); + server.listen(() => { + const data = { pid: child_process.pid, + tcp_address: server.address(), + udp_address: udp_socket.address(), + skip_fs_watch: (watcher === undefined ? + 'fs.watch() unavailable' : + false) }; + process.send(data); + http.get({ port: server.address().port }); + }); +} else { + const helper = require('../common/report.js'); + const fork = require('child_process').fork; + const assert = require('assert'); + const tmpdir = require('../common/tmpdir'); + tmpdir.refresh(); + const options = { encoding: 'utf8', silent: true, cwd: tmpdir.path }; + const child = fork('--experimental-report', [__filename, 'child'], options); + let stderr = ''; + child.stderr.on('data', (chunk) => { stderr += chunk; }); + let stdout = ''; + const std_msg = 'Found messages in stderr unexpectedly: '; + const report_msg = 'Report files were written: unexpectedly'; + child.stdout.on('data', (chunk) => { stdout += chunk; }); + child.on('exit', common.mustCall((code, signal) => { + assert.deepStrictEqual(code, 0, 'Process exited unexpectedly with code' + + `${code}`); + assert.deepStrictEqual(signal, null, 'Process should have exited cleanly,' + + ` but did not: ${signal}`); + assert.ok(stderr.match( + '(node:.*) ExperimentalWarning: report is an experimental' + + ' feature. This feature could change at any time'), + std_msg); + + const reports = helper.findReports(child.pid, tmpdir.path); + assert.deepStrictEqual(reports, [], report_msg, reports); + + const report = JSON.parse(stdout); + let fs = 0; + let poll = 0; + let process = 0; + let timer = 0; + let pipe = 0; + let tcp = 0; + let udp = 0; + const fs_msg = 'fs_event not found'; + const poll_msg = 'poll_event not found'; + const process_msg = 'process event not found'; + const timer_msg = 'timer event not found'; + const pipe_msg = 'pipe event not found'; + const tcp_msg = 'tcp event not found'; + const udp_msg = 'udp event not found'; + for (const entry in report.libuv) { + if (report.libuv[entry].type === 'fs_event') fs = 1; + else if (report.libuv[entry].type === 'fs_poll') poll = 1; + else if (report.libuv[entry].type === 'process') process = 1; + else if (report.libuv[entry].type === 'timer') timer = 1; + else if (report.libuv[entry].type === 'pipe') pipe = 1; + else if (report.libuv[entry].type === 'tcp') tcp = 1; + else if (report.libuv[entry].type === 'udp') udp = 1; + } + assert.deepStrictEqual(fs, 1, fs_msg); + assert.deepStrictEqual(poll, 1, poll_msg); + assert.deepStrictEqual(process, 1, process_msg); + assert.deepStrictEqual(timer, 1, timer_msg); + assert.deepStrictEqual(pipe, 1, pipe_msg); + assert.deepStrictEqual(tcp, 1, tcp_msg); + assert.deepStrictEqual(udp, 1, udp_msg); + + // Common report tests. + helper.validateContent(stdout, { pid: child.pid, + commandline: child.spawnargs.join(' ') + }); + })); +} diff --git a/test/node-report/test-api.js b/test/node-report/test-api.js new file mode 100644 index 0000000000..9ffa66bb40 --- /dev/null +++ b/test/node-report/test-api.js @@ -0,0 +1,29 @@ +'use strict'; + +// Testcase to produce report via API call +const common = require('../common'); +common.skipIfReportDisabled(); +if (process.argv[2] === 'child') { + process.report.triggerReport(); +} else { + const helper = require('../common/report.js'); + const spawn = require('child_process').spawn; + const assert = require('assert'); + const tmpdir = require('../common/tmpdir'); + tmpdir.refresh(); + + const child = spawn(process.execPath, + ['--experimental-report', __filename, 'child'], + { cwd: tmpdir.path }); + child.on('exit', common.mustCall((code) => { + const report_msg = 'No reports found'; + const process_msg = 'Process exited unexpectedly'; + assert.strictEqual(code, 0, process_msg + ':' + code); + const reports = helper.findReports(child.pid, tmpdir.path); + assert.strictEqual(reports.length, 1, report_msg); + const report = reports[0]; + helper.validate(report, { pid: child.pid, + commandline: child.spawnargs.join(' ') + }); + })); +} diff --git a/test/node-report/test-exception.js b/test/node-report/test-exception.js new file mode 100644 index 0000000000..9e423ec29c --- /dev/null +++ b/test/node-report/test-exception.js @@ -0,0 +1,44 @@ +'use strict'; + +// Testcase to produce report on uncaught exception +const common = require('../common'); +common.skipIfReportDisabled(); +if (process.argv[2] === 'child') { + function myException(request, response) { + const m = '*** test-exception.js: throwing uncaught Error'; + throw new Error(m); + } + + myException(); + +} else { + const helper = require('../common/report.js'); + const tmpdir = require('../common/tmpdir'); + tmpdir.refresh(); + const spawn = require('child_process').spawn; + const assert = require('assert'); + + const child = spawn(process.execPath, + ['--experimental-report', + '--diagnostic-report-uncaught-exception', + __filename, 'child'], + { cwd: tmpdir.path }); + // Capture stderr output from the child process + let stderr = ''; + child.stderr.on('data', (chunk) => { + stderr += chunk; + }); + child.on('exit', common.mustCall((code) => { + const report_msg = 'No reports found'; + const process_msg = 'Process exited unexpectedly'; + assert.strictEqual(code, 1, process_msg + ':' + code); + assert.ok(new RegExp('myException').test(stderr), + 'Check for expected stack trace frame in stderr'); + const reports = helper.findReports(child.pid, tmpdir.path); + assert.strictEqual(reports.length, 1, report_msg); + const report = reports[0]; + helper.validate(report, { pid: child.pid, + commandline: child.spawnargs.join(' ') + }); + })); +} diff --git a/test/node-report/test-fatal-error.js b/test/node-report/test-fatal-error.js new file mode 100644 index 0000000000..3d830aa72f --- /dev/null +++ b/test/node-report/test-fatal-error.js @@ -0,0 +1,44 @@ +'use strict'; + +const common = require('../common'); +common.skipIfReportDisabled(); +const assert = require('assert'); +// Testcase to produce report on fatal error (javascript heap OOM) +if (process.argv[2] === 'child') { + + const list = []; + while (true) { + const record = new MyRecord(); + list.push(record); + } + + function MyRecord() { + this.name = 'foo'; + this.id = 128; + this.account = 98454324; + } +} else { + const helper = require('../common/report.js'); + const tmpdir = require('../common/tmpdir'); + tmpdir.refresh(); + const spawn = require('child_process').spawn; + const args = ['--experimental-report', + '--diagnostic-report-on-fatalerror', + '--max-old-space-size=20', + __filename, + 'child']; + const child = spawn(process.execPath, args, { cwd: tmpdir.path }); + child.on('exit', common.mustCall((code) => { + assert.notStrictEqual(code, 0, 'Process exited unexpectedly'); + const reports = helper.findReports(child.pid, tmpdir.path); + assert.strictEqual(reports.length, 1); + const report = reports[0]; + const options = { pid: child.pid }; + // Node.js currently overwrites the command line on AIX + // https://github.com/nodejs/node/issues/10607 + if (!(common.isAIX || common.isSunOS)) { + options.commandline = child.spawnargs.join(' '); + } + helper.validate(report, options); + })); +} diff --git a/test/node-report/test-signal.js b/test/node-report/test-signal.js new file mode 100644 index 0000000000..7368bb51c3 --- /dev/null +++ b/test/node-report/test-signal.js @@ -0,0 +1,80 @@ +'use strict'; +// Testcase to produce report on signal interrupting a js busy-loop, +// showing it is interruptible. +const common = require('../common'); +common.skipIfReportDisabled(); + +if (common.isWindows) return common.skip('Unsupported on Windows.'); + +if (process.argv[2] === 'child') { + // Exit on loss of parent process + const exit = () => process.exit(2); + process.on('disconnect', exit); + + function busyLoop() { + setInterval(() => { + const list = []; + for (let i = 0; i < 1e3; i++) { + for (let j = 0; j < 1000; j++) { + list.push(new MyRecord()); + } + for (let k = 0; k < 1000; k++) { + list[k].id += 1; + list[k].account += 2; + } + for (let l = 0; l < 1000; l++) { + list.pop(); + } + } + }, 1000); + } + + function MyRecord() { + this.name = 'foo'; + this.id = 128; + this.account = 98454324; + } + process.send('child started', busyLoop); + + +} else { + const helper = require('../common/report.js'); + const fork = require('child_process').fork; + const tmpdir = require('../common/tmpdir'); + tmpdir.refresh(); + const assert = require('assert'); + if (common.isWindows) { + assert.fail('Unsupported on Windows', { skip: true }); + return; + } + console.log(tmpdir.path); + const options = { stdio: 'pipe', encoding: 'utf8', cwd: tmpdir.path }; + const child = fork('--experimental-report', + ['--diagnostic-report-on-signal', __filename, 'child'], + options); + // Wait for child to indicate it is ready before sending signal + child.on('message', () => child.kill('SIGUSR2')); + let stderr = ''; + child.stderr.on('data', (chunk) => { + stderr += chunk; + // Terminate the child after the report has been written + if (stderr.includes('Node.js report completed')) { + child.kill('SIGTERM'); + } + }); + child.on('exit', common.mustCall((code, signal) => { + console.log('child exited'); + const report_msg = 'No reports found'; + const process_msg = 'Process exited unexpectedly'; + const signal_msg = 'Process exited with unexpected signal'; + assert.strictEqual(code, null, process_msg + ':' + code); + assert.deepStrictEqual(signal, 'SIGTERM', + signal_msg + ':' + signal); + const reports = helper.findReports(child.pid, tmpdir.path); + assert.deepStrictEqual(reports.length, 1, report_msg); + const report = reports[0]; + helper.validate(report, { pid: child.pid, + commandline: child.spawnargs.join(' ') + }); + })); +} diff --git a/test/node-report/testcfg.py b/test/node-report/testcfg.py new file mode 100644 index 0000000000..e5ea1f57a0 --- /dev/null +++ b/test/node-report/testcfg.py @@ -0,0 +1,6 @@ +import sys, os +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +import testpy + +def GetConfiguration(context, root): + return testpy.ParallelTestConfiguration(context, root, 'node-report') |