summaryrefslogtreecommitdiff
path: root/test/report
diff options
context:
space:
mode:
authorcjihrig <cjihrig@gmail.com>2019-02-28 20:54:35 -0500
committercjihrig <cjihrig@gmail.com>2019-03-02 21:17:19 -0500
commitfe292fac55c2b41432db2c326fe40ae154a1ab21 (patch)
tree659b2ae6022f0077b5acf9d83bf6657fabea79e3 /test/report
parent060af324ae7093a390edd1524855d7679ce6837b (diff)
downloadandroid-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.js38
-rw-r--r--test/report/test-report-getreport.js29
-rw-r--r--test/report/test-report-signal.js78
-rw-r--r--test/report/test-report-triggerreport.js83
-rw-r--r--test/report/test-report-uncaught-exception.js24
-rw-r--r--test/report/test-report-uv-handles.js156
-rw-r--r--test/report/testcfg.py6
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')