| 'use strict'; |
| |
| // This test makes sure that when throwing an error from a domain, and then |
| // handling that error in an uncaughtException handler by throwing an error |
| // again, the exit code, signal and error messages are the ones we expect with |
| // and without using --abort-on-uncaught-exception. |
| |
| const common = require('../common'); |
| const assert = require('assert'); |
| const child_process = require('child_process'); |
| const domain = require('domain'); |
| |
| const uncaughtExceptionHandlerErrMsg = 'boom from uncaughtException handler'; |
| const domainErrMsg = 'boom from domain'; |
| |
| const RAN_UNCAUGHT_EXCEPTION_HANDLER_EXIT_CODE = 42; |
| |
| if (process.argv[2] === 'child') { |
| process.on('uncaughtException', common.mustCall(function onUncaught() { |
| if (process.execArgv.includes('--abort-on-uncaught-exception')) { |
| // When passing --abort-on-uncaught-exception to the child process, |
| // we want to make sure that this handler (the process' uncaughtException |
| // event handler) wasn't called. Unfortunately we can't parse the child |
| // process' output to do that, since on Windows the standard error output |
| // is not properly flushed in V8's Isolate::Throw right before the |
| // process aborts due to an uncaught exception, and thus the error |
| // message representing the error that was thrown cannot be read by the |
| // parent process. So instead of parsing the child process' standard |
| // error, the parent process will check that in the case |
| // --abort-on-uncaught-exception was passed, the process did not exit |
| // with exit code RAN_UNCAUGHT_EXCEPTION_HANDLER_EXIT_CODE. |
| process.exit(RAN_UNCAUGHT_EXCEPTION_HANDLER_EXIT_CODE); |
| } else { |
| // On the other hand, when not passing --abort-on-uncaught-exception to |
| // the node process, we want to throw in this event handler to make sure |
| // that the proper error message, exit code and signal are the ones we |
| // expect. |
| throw new Error(uncaughtExceptionHandlerErrMsg); |
| } |
| })); |
| |
| const d = domain.create(); |
| d.run(common.mustCall(function() { |
| throw new Error(domainErrMsg); |
| })); |
| } else { |
| runTestWithoutAbortOnUncaughtException(); |
| runTestWithAbortOnUncaughtException(); |
| } |
| |
| function runTestWithoutAbortOnUncaughtException() { |
| child_process.exec( |
| createTestCmdLine(), |
| function onTestDone(err, stdout, stderr) { |
| // When _not_ passing --abort-on-uncaught-exception, the process' |
| // uncaughtException handler _must_ be called, and thus the error |
| // message must include only the message of the error thrown from the |
| // process' uncaughtException handler. |
| assert(stderr.includes(uncaughtExceptionHandlerErrMsg), |
| 'stderr output must include proper uncaughtException ' + |
| 'handler\'s error\'s message'); |
| assert(!stderr.includes(domainErrMsg), |
| 'stderr output must not include domain\'s error\'s message'); |
| |
| assert.notStrictEqual(err.code, 0, |
| 'child process should have exited with a ' + |
| 'non-zero exit code, but did not'); |
| } |
| ); |
| } |
| |
| function runTestWithAbortOnUncaughtException() { |
| child_process.exec(createTestCmdLine({ |
| withAbortOnUncaughtException: true |
| }), function onTestDone(err, stdout, stderr) { |
| assert.notStrictEqual(err.code, RAN_UNCAUGHT_EXCEPTION_HANDLER_EXIT_CODE, |
| 'child process should not have run its ' + |
| 'uncaughtException event handler'); |
| assert(common.nodeProcessAborted(err.code, err.signal), |
| 'process should have aborted, but did not'); |
| }); |
| } |
| |
| function createTestCmdLine(options) { |
| let testCmd = ''; |
| |
| if (!common.isWindows) { |
| // Do not create core files, as it can take a lot of disk space on |
| // continuous testing and developers' machines |
| testCmd += 'ulimit -c 0 && '; |
| } |
| |
| testCmd += `"${process.argv[0]}"`; |
| |
| if (options && options.withAbortOnUncaughtException) { |
| testCmd += ' --abort-on-uncaught-exception'; |
| } |
| |
| testCmd += ` "${process.argv[1]}" child`; |
| |
| return testCmd; |
| } |