summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorLakshmiSwethaG <lakshmigopireddy@in.ibm.com>2018-09-05 10:39:08 -0400
committerGireesh Punathil <gpunathi@in.ibm.com>2019-01-18 10:36:39 +0530
commit55e0ad9ae690f6d73b0caff7b813eb4d61195b94 (patch)
treed85d8d5bf3b8efb24b0d79886af706584a076721 /test
parent7f4053ed1381d7001dbdaa12f064847aaf4d0ecf (diff)
downloadandroid-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.js7
-rw-r--r--test/common/report.js43
-rw-r--r--test/node-report/test-api-getreport.js28
-rw-r--r--test/node-report/test-api-nohooks.js30
-rw-r--r--test/node-report/test-api-pass-error.js33
-rw-r--r--test/node-report/test-api-uvhandles.js133
-rw-r--r--test/node-report/test-api.js29
-rw-r--r--test/node-report/test-exception.js44
-rw-r--r--test/node-report/test-fatal-error.js44
-rw-r--r--test/node-report/test-signal.js80
-rw-r--r--test/node-report/testcfg.py6
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')