Skip to content
This repository has been archived by the owner on Jun 18, 2021. It is now read-only.

Convert testcases to use tap #15

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@
"Richard Chamberlain <richard_chamberlain@uk.ibm.com> (https://github.com/rnchamberlain)"
],
"scripts": {
"test": "node test/autorun.js"
"test": "tap test/test*.js"
},
"bugs": {
"url": "https://github.com/nodejs/nodereport/issues"
},
"devDependencies": {
"tap": "^8.0.0"
}
}
129 changes: 0 additions & 129 deletions test/autorun.js

This file was deleted.

36 changes: 36 additions & 0 deletions test/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use strict';

const fs = require('fs');

const REPORT_SECTIONS = [
'NodeReport',
'JavaScript Stack Trace',
'JavaScript Heap',
'System Information'
];

exports.findReports = (pid) => {
// Default filenames are of the form NodeReport.<date>.<time>.<pid>.<seq>.txt
const format = '^NodeReport\\.\\d+\\.\\d+\\.' + pid + '\\.\\d+\\.txt$';
const filePattern = new RegExp(format);
const files = fs.readdirSync('.');
return files.filter((file) => filePattern.test(file));
};

exports.validate = (t, report, pid) => {
t.test('Validating ' + report, (t) => {
fs.readFile(report, (err, data) => {
const reportContents = data.toString();
const plan = REPORT_SECTIONS.length + (pid ? 1 : 0);
t.plan(plan);
if (pid) {
t.match(reportContents, new RegExp('Process ID: ' + pid),
'Checking report contains expected process ID ' + pid);
}
REPORT_SECTIONS.forEach((section) => {
t.match(reportContents, new RegExp('==== ' + section),
'Checking report contains ' + section + ' section');
});
});
});
};
21 changes: 21 additions & 0 deletions test/test-api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';

// Testcase to produce NodeReport via API call
if (process.argv[2] === 'child') {
const nodereport = require('../');
nodereport.triggerReport();
} else {
const common = require('./common.js');
const spawn = require('child_process').spawn;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its typical to lexically sort require lines by the name of the var

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do so for all of the tests.

const tap = require('tap');

const child = spawn(process.execPath, [__filename, 'child']);
child.on('exit', (code) => {
tap.plan(3);
tap.equal(code, 0, 'Process exited cleanly');
const reports = common.findReports(child.pid);
tap.equal(reports.length, 1, 'Found reports ' + reports);
const report = reports[0];
common.validate(tap, report, child.pid);
});
}
36 changes: 36 additions & 0 deletions test/test-exception.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use strict';

// Testcase to produce NodeReport on uncaught exception
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could do a variant of this that emits 'error' on an EventEmitter, too

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would prefer for this PR to concentrate on converting the existing tests to use tap. We can then add more variants and new tests once this is merged.

if (process.argv[2] === 'child') {
require('../').setEvents('exception');

function myException(request, response) {
const m = '*** exception.js: testcase exception thrown from myException()';
throw new UserException(m);
}

function UserException(message) {
this.message = message;
this.name = 'UserException';
}

myException();
} else {
const common = require('./common.js');
const spawn = require('child_process').spawn;
const tap = require('tap');

const child = spawn(process.execPath, [__filename, 'child']);
child.on('exit', (code, signal) => {
const expectedExitCode = process.platform === 'win32' ? 0xC0000005 : null;
const expectedSignal = process.platform === 'win32' ? null : 'SIGILL';
tap.plan(4);
tap.equal(code, expectedExitCode, 'Process should not exit cleanly');
tap.equal(signal, expectedSignal,
'Process should exit with expected signal ');
const reports = common.findReports(child.pid);
tap.equal(reports.length, 1, 'Found reports ' + reports);
const report = reports[0];
common.validate(tap, report, child.pid);
});
}
33 changes: 33 additions & 0 deletions test/test-fatal-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use strict';

// Testcase to produce NodeReport on fatal error (javascript heap OOM)
if (process.argv[2] === 'child') {
require('../').setEvents('fatalerror');

const list = [];
while (true) {
const record = new MyRecord();
list.push(record);
}

function MyRecord() {
this.name = 'foo';
this.id = 128;
this.account = 98454324;
}
} else {
const common = require('./common.js');
const spawn = require('child_process').spawn;
const tap = require('tap');

const args = ['--max-old-space-size=20', __filename, 'child'];
const child = spawn(process.execPath, args);
child.on('exit', (code) => {
tap.plan(3);
tap.notEqual(code, 0, 'Process should not exit cleanly');
const reports = common.findReports(child.pid);
tap.equal(reports.length, 1, 'Found reports ' + reports);
const report = reports[0];
common.validate(tap, report, child.pid);
});
}
64 changes: 64 additions & 0 deletions test/test-signal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
'use strict';

// Testcase to produce NodeReport on signal interrupting a js busy-loop,
// showing it is interruptible.
if (process.argv[2] === 'child') {
require('../').setEvents('signal');

// Exit on loss of parent process
process.on('disconnect', () => process.exit(2));

function busyLoop() {
var list = [];
for (var i = 0; i < 1e10; i++) {
for (var j = 0; j < 1000; j++) {
list.push(new MyRecord());
}
for (var k = 0; k < 1000; k++) {
list[k].id += 1;
list[k].account += 2;
}
for (var l = 0; l < 1000; l++) {
list.pop();
}
}
}

function MyRecord() {
this.name = 'foo';
this.id = 128;
this.account = 98454324;
}

process.send('child started', busyLoop);
} else {
const common = require('./common.js');
const fork = require('child_process').fork;
const tap = require('tap');

if (process.platform === 'win32') {
tap.fail('Unsupported on Windows', { skip: true });
return;
}

const child = fork(__filename, ['child'], { silent: true });
// Wait for child to indicate it is ready before sending signal
child.on('message', () => child.kill('SIGUSR2'));
var 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', (code, signal) => {
tap.plan(4);
tap.equal(code, null, 'Process should not exit cleanly');
tap.equal(signal, 'SIGTERM', 'Process should exit with expected signal');
const reports = common.findReports(child.pid);
tap.equal(reports.length, 1, 'Found reports ' + reports);
const report = reports[0];
common.validate(tap, report, child.pid);
});
}
10 changes: 0 additions & 10 deletions test/test_1.js

This file was deleted.

15 changes: 0 additions & 15 deletions test/test_2.js

This file was deleted.

16 changes: 0 additions & 16 deletions test/test_3.js

This file was deleted.

Loading