diff options
author | cjihrig <cjihrig@gmail.com> | 2019-02-28 20:54:35 -0500 |
---|---|---|
committer | cjihrig <cjihrig@gmail.com> | 2019-03-02 21:17:19 -0500 |
commit | fe292fac55c2b41432db2c326fe40ae154a1ab21 (patch) | |
tree | 659b2ae6022f0077b5acf9d83bf6657fabea79e3 /test/report | |
parent | 060af324ae7093a390edd1524855d7679ce6837b (diff) | |
download | android-node-v8-fe292fac55c2b41432db2c326fe40ae154a1ab21.tar.gz android-node-v8-fe292fac55c2b41432db2c326fe40ae154a1ab21.tar.bz2 android-node-v8-fe292fac55c2b41432db2c326fe40ae154a1ab21.zip |
test: rename node-report suite to report
This commit renames the "node-report" test suite to "report"
in order to begin differentiating core's diagnostic reporting
from the original node-report module on npm
PR-URL: https://github.com/nodejs/node/pull/26371
Reviewed-By: Richard Lau <riclau@uk.ibm.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Minwoo Jung <minwoo@nodesource.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'test/report')
-rw-r--r-- | test/report/test-report-fatal-error.js | 38 | ||||
-rw-r--r-- | test/report/test-report-getreport.js | 29 | ||||
-rw-r--r-- | test/report/test-report-signal.js | 78 | ||||
-rw-r--r-- | test/report/test-report-triggerreport.js | 83 | ||||
-rw-r--r-- | test/report/test-report-uncaught-exception.js | 24 | ||||
-rw-r--r-- | test/report/test-report-uv-handles.js | 156 | ||||
-rw-r--r-- | test/report/testcfg.py | 6 |
7 files changed, 414 insertions, 0 deletions
diff --git a/test/report/test-report-fatal-error.js b/test/report/test-report-fatal-error.js new file mode 100644 index 0000000000..b6cf792725 --- /dev/null +++ b/test/report/test-report-fatal-error.js @@ -0,0 +1,38 @@ +'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]; + helper.validate(report); + })); +} diff --git a/test/report/test-report-getreport.js b/test/report/test-report-getreport.js new file mode 100644 index 0000000000..9f40e61c2e --- /dev/null +++ b/test/report/test-report-getreport.js @@ -0,0 +1,29 @@ +// Flags: --experimental-report +'use strict'; +const common = require('../common'); +common.skipIfReportDisabled(); +const assert = require('assert'); +const helper = require('../common/report'); + +common.expectWarning('ExperimentalWarning', + 'report is an experimental feature. This feature could ' + + 'change at any time'); + +{ + // Test with no arguments. + helper.validateContent(process.report.getReport()); + assert.deepStrictEqual(helper.findReports(process.pid, process.cwd()), []); +} + +{ + // Test with an error argument. + helper.validateContent(process.report.getReport(new Error('test error'))); + assert.deepStrictEqual(helper.findReports(process.pid, process.cwd()), []); +} + +// Test with an invalid error argument. +[null, 1, Symbol(), function() {}, 'foo'].forEach((error) => { + common.expectsError(() => { + process.report.getReport(error); + }, { code: 'ERR_INVALID_ARG_TYPE' }); +}); diff --git a/test/report/test-report-signal.js b/test/report/test-report-signal.js new file mode 100644 index 0000000000..129933b546 --- /dev/null +++ b/test/report/test-report-signal.js @@ -0,0 +1,78 @@ +'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); + })); +} diff --git a/test/report/test-report-triggerreport.js b/test/report/test-report-triggerreport.js new file mode 100644 index 0000000000..4ac8f1a9a5 --- /dev/null +++ b/test/report/test-report-triggerreport.js @@ -0,0 +1,83 @@ +// Flags: --experimental-report +'use strict'; + +// Test producing a report via API call, using the no-hooks/no-signal interface. +const common = require('../common'); +common.skipIfReportDisabled(); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); +const helper = require('../common/report'); +const tmpdir = require('../common/tmpdir'); + +common.expectWarning('ExperimentalWarning', + 'report is an experimental feature. This feature could ' + + 'change at any time'); +tmpdir.refresh(); +process.report.setOptions({ path: tmpdir.path }); + +function validate() { + const reports = helper.findReports(process.pid, tmpdir.path); + assert.strictEqual(reports.length, 1); + helper.validate(reports[0]); + fs.unlinkSync(reports[0]); + return reports[0]; +} + +{ + // Test with no arguments. + process.report.triggerReport(); + validate(); +} + +{ + // Test with an error argument. + process.report.triggerReport(new Error('test error')); + validate(); +} + +{ + // Test with a file argument. + const file = process.report.triggerReport('custom-name-1.json'); + const absolutePath = path.join(tmpdir.path, file); + assert.strictEqual(helper.findReports(process.pid, tmpdir.path).length, 0); + assert.strictEqual(file, 'custom-name-1.json'); + helper.validate(absolutePath); + fs.unlinkSync(absolutePath); +} + +{ + // Test with file and error arguments. + const file = process.report.triggerReport('custom-name-2.json', + new Error('test error')); + const absolutePath = path.join(tmpdir.path, file); + assert.strictEqual(helper.findReports(process.pid, tmpdir.path).length, 0); + assert.strictEqual(file, 'custom-name-2.json'); + helper.validate(absolutePath); + fs.unlinkSync(absolutePath); +} + +{ + // Test with a filename option. + const filename = path.join(tmpdir.path, 'custom-name-3.json'); + process.report.setOptions({ filename }); + const file = process.report.triggerReport(); + assert.strictEqual(helper.findReports(process.pid, tmpdir.path).length, 0); + assert.strictEqual(file, filename); + helper.validate(filename); + fs.unlinkSync(filename); +} + +// Test with an invalid file argument. +[null, 1, Symbol(), function() {}].forEach((file) => { + common.expectsError(() => { + process.report.triggerReport(file); + }, { code: 'ERR_INVALID_ARG_TYPE' }); +}); + +// Test with an invalid error argument. +[null, 1, Symbol(), function() {}, 'foo'].forEach((error) => { + common.expectsError(() => { + process.report.triggerReport('file', error); + }, { code: 'ERR_INVALID_ARG_TYPE' }); +}); diff --git a/test/report/test-report-uncaught-exception.js b/test/report/test-report-uncaught-exception.js new file mode 100644 index 0000000000..b3da7c4244 --- /dev/null +++ b/test/report/test-report-uncaught-exception.js @@ -0,0 +1,24 @@ +// Flags: --experimental-report --diagnostic-report-uncaught-exception +'use strict'; +// Test producing a report on uncaught exception. +const common = require('../common'); +common.skipIfReportDisabled(); +const assert = require('assert'); +const helper = require('../common/report'); +const tmpdir = require('../common/tmpdir'); +const error = new Error('test error'); + +common.expectWarning('ExperimentalWarning', + 'report is an experimental feature. This feature could ' + + 'change at any time'); +tmpdir.refresh(); +process.report.setOptions({ path: tmpdir.path }); + +process.on('uncaughtException', common.mustCall((err) => { + assert.strictEqual(err, error); + const reports = helper.findReports(process.pid, tmpdir.path); + assert.strictEqual(reports.length, 1); + helper.validate(reports[0]); +})); + +throw error; diff --git a/test/report/test-report-uv-handles.js b/test/report/test-report-uv-handles.js new file mode 100644 index 0000000000..6473b630f5 --- /dev/null +++ b/test/report/test-report-uv-handles.js @@ -0,0 +1,156 @@ +'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) }; + 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 child_data; + child.on('message', (data) => { child_data = data; }); + 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); + const prefix = common.isWindows ? '\\\\?\\' : ''; + const expected_filename = `${prefix}${__filename}`; + const found_tcp = []; + // Functions are named to aid debugging when they are not called. + const validators = { + fs_event: common.mustCall(function fs_event_validator(handle) { + if (!child_data.skip_fs_watch) { + assert.strictEqual(handle.filename, expected_filename); + assert(handle.is_referenced); + } + }), + fs_poll: common.mustCall(function fs_poll_validator(handle) { + assert.strictEqual(handle.filename, expected_filename); + assert(handle.is_referenced); + }), + pipe: common.mustCallAtLeast(function pipe_validator(handle) { + assert(handle.is_referenced); + }), + process: common.mustCall(function process_validator(handle) { + assert.strictEqual(handle.pid, child_data.pid); + assert(handle.is_referenced); + }), + tcp: common.mustCall(function tcp_validator(handle) { + // TCP handles. The report should contain three sockets: + // 1. The server's listening socket. + // 2. The inbound socket making the request. + // 3. The outbound socket sending the response. + const port = child_data.tcp_address.port; + if (handle.localEndpoint.port === port) { + if (handle.remoteEndpoint === null) { + found_tcp.push('listening'); + } else { + found_tcp.push('inbound'); + } + } else if (handle.remoteEndpoint.port === port) { + found_tcp.push('outbound'); + } + assert(handle.is_referenced); + }, 3), + timer: common.mustCall(function timer_validator(handle) { + assert(!handle.is_referenced); + assert.strictEqual(handle.repeat, 0); + }), + udp: common.mustCall(function udp_validator(handle) { + assert.strictEqual(handle.localEndpoint.port, + child_data.udp_address.port); + assert(handle.is_referenced); + }), + }; + for (const entry of report.libuv) { + if (validators[entry.type]) validators[entry.type](entry); + } + for (const socket of ['listening', 'inbound', 'outbound']) { + assert(found_tcp.includes(socket), `${socket} TCP socket was not found`); + } + + // Common report tests. + helper.validateContent(stdout); + })); +} diff --git a/test/report/testcfg.py b/test/report/testcfg.py new file mode 100644 index 0000000000..c06b75ce5c --- /dev/null +++ b/test/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, 'report') |