nodejs/test/parallel/test-domain-with-abort-on-u...

170 lines
5.4 KiB
JavaScript

'use strict';
const common = require('../common');
const assert = require('assert');
const fs = require('fs');
/*
* The goal of this test is to make sure that:
*
* - Even if --abort_on_uncaught_exception is passed on the command line,
* setting up a top-level domain error handler and throwing an error
* within this domain does *not* make the process abort. The process exits
* gracefully.
*
* - When passing --abort_on_uncaught_exception on the command line and
* setting up a top-level domain error handler, an error thrown
* within this domain's error handler *does* make the process abort.
*
* - When *not* passing --abort_on_uncaught_exception on the command line and
* setting up a top-level domain error handler, an error thrown within this
* domain's error handler does *not* make the process abort, but makes it exit
* with the proper failure exit code.
*
* - When throwing an error within the top-level domain's error handler
* within a try/catch block, the process should exit gracefully, whether or
* not --abort_on_uncaught_exception is passed on the command line.
*/
const domainErrHandlerExMessage = 'exception from domain error handler';
if (process.argv[2] === 'child') {
const domain = require('domain');
const d = domain.create();
process.on('uncaughtException', function onUncaughtException() {
// The process' uncaughtException event must not be emitted when
// an error handler is setup on the top-level domain.
// Exiting with exit code of 42 here so that it would assert when
// the parent checks the child exit code.
process.exit(42);
});
d.on('error', function(err) {
// Swallowing the error on purpose if 'throwInDomainErrHandler' is not
// set
if (process.argv.includes('throwInDomainErrHandler')) {
// If useTryCatch is set, wrap the throw in a try/catch block.
// This is to make sure that a caught exception does not trigger
// an abort.
if (process.argv.includes('useTryCatch')) {
try {
throw new Error(domainErrHandlerExMessage);
} catch {
}
} else {
throw new Error(domainErrHandlerExMessage);
}
}
});
d.run(function doStuff() {
// Throwing from within different types of callbacks as each of them
// handles domains differently
process.nextTick(function() {
throw new Error('Error from nextTick callback');
});
fs.exists('/non/existing/file', function onExists(exists) {
throw new Error('Error from fs.exists callback');
});
setImmediate(function onSetImmediate() {
throw new Error('Error from setImmediate callback');
});
setTimeout(function onTimeout() {
throw new Error('Error from setTimeout callback');
}, 0);
throw new Error('Error from domain.run callback');
});
} else {
const exec = require('child_process').exec;
function testDomainExceptionHandling(cmdLineOption, options) {
if (typeof cmdLineOption === 'object') {
options = cmdLineOption;
cmdLineOption = undefined;
}
let throwInDomainErrHandlerOpt;
if (options.throwInDomainErrHandler)
throwInDomainErrHandlerOpt = 'throwInDomainErrHandler';
let cmdToExec = '';
if (!common.isWindows) {
// Do not create core files, as it can take a lot of disk space on
// continuous testing and developers' machines
cmdToExec += 'ulimit -c 0 && ';
}
let useTryCatchOpt;
if (options.useTryCatch)
useTryCatchOpt = 'useTryCatch';
cmdToExec += `"${process.argv[0]}" ${cmdLineOption ? cmdLineOption : ''} "${
process.argv[1]}" child ${throwInDomainErrHandlerOpt} ${useTryCatchOpt}`;
const child = exec(cmdToExec);
if (child) {
child.on('exit', function onChildExited(exitCode, signal) {
// When throwing errors from the top-level domain error handler
// outside of a try/catch block, the process should not exit gracefully
if (!options.useTryCatch && options.throwInDomainErrHandler) {
if (cmdLineOption === '--abort_on_uncaught_exception') {
assert(common.nodeProcessAborted(exitCode, signal),
'process should have aborted, but did not');
} else {
// By default, uncaught exceptions make node exit with an exit
// code of 7.
assert.strictEqual(exitCode, 7);
assert.strictEqual(signal, null);
}
} else {
// If the top-level domain's error handler does not throw,
// the process must exit gracefully, whether or not
// --abort_on_uncaught_exception was passed on the command line
assert.strictEqual(exitCode, 0);
assert.strictEqual(signal, null);
}
});
}
}
testDomainExceptionHandling('--abort_on_uncaught_exception', {
throwInDomainErrHandler: false,
useTryCatch: false
});
testDomainExceptionHandling('--abort_on_uncaught_exception', {
throwInDomainErrHandler: false,
useTryCatch: true
});
testDomainExceptionHandling('--abort_on_uncaught_exception', {
throwInDomainErrHandler: true,
useTryCatch: false
});
testDomainExceptionHandling('--abort_on_uncaught_exception', {
throwInDomainErrHandler: true,
useTryCatch: true
});
testDomainExceptionHandling({
throwInDomainErrHandler: false
});
testDomainExceptionHandling({
throwInDomainErrHandler: false,
useTryCatch: false
});
testDomainExceptionHandling({
throwInDomainErrHandler: true,
useTryCatch: true
});
}