1'use strict'; 2 3const common = require('../common'); 4const assert = require('assert'); 5const fs = require('fs'); 6 7// The goal of this test is to make sure that: 8// 9// - Even if --abort_on_uncaught_exception is passed on the command line, 10// setting up a top-level domain error handler and throwing an error 11// within this domain does *not* make the process abort. The process exits 12// gracefully. 13// 14// - When passing --abort_on_uncaught_exception on the command line and 15// setting up a top-level domain error handler, an error thrown 16// within this domain's error handler *does* make the process abort. 17// 18// - When *not* passing --abort_on_uncaught_exception on the command line and 19// setting up a top-level domain error handler, an error thrown within this 20// domain's error handler does *not* make the process abort, but makes it exit 21// with the proper failure exit code. 22// 23// - When throwing an error within the top-level domain's error handler 24// within a try/catch block, the process should exit gracefully, whether or 25// not --abort_on_uncaught_exception is passed on the command line. 26 27const domainErrHandlerExMessage = 'exception from domain error handler'; 28 29if (process.argv[2] === 'child') { 30 const domain = require('domain'); 31 const d = domain.create(); 32 33 process.on('uncaughtException', function onUncaughtException() { 34 // The process' uncaughtException event must not be emitted when 35 // an error handler is setup on the top-level domain. 36 // Exiting with exit code of 42 here so that it would assert when 37 // the parent checks the child exit code. 38 process.exit(42); 39 }); 40 41 d.on('error', function(err) { 42 // Swallowing the error on purpose if 'throwInDomainErrHandler' is not 43 // set 44 if (process.argv.includes('throwInDomainErrHandler')) { 45 // If useTryCatch is set, wrap the throw in a try/catch block. 46 // This is to make sure that a caught exception does not trigger 47 // an abort. 48 if (process.argv.includes('useTryCatch')) { 49 try { 50 throw new Error(domainErrHandlerExMessage); 51 } catch { 52 } 53 } else { 54 throw new Error(domainErrHandlerExMessage); 55 } 56 } 57 }); 58 59 d.run(function doStuff() { 60 // Throwing from within different types of callbacks as each of them 61 // handles domains differently 62 process.nextTick(function() { 63 throw new Error('Error from nextTick callback'); 64 }); 65 66 fs.exists('/non/existing/file', function onExists(exists) { 67 throw new Error('Error from fs.exists callback'); 68 }); 69 70 setImmediate(function onSetImmediate() { 71 throw new Error('Error from setImmediate callback'); 72 }); 73 74 setTimeout(function onTimeout() { 75 throw new Error('Error from setTimeout callback'); 76 }, 0); 77 78 throw new Error('Error from domain.run callback'); 79 }); 80} else { 81 const exec = require('child_process').exec; 82 83 function testDomainExceptionHandling(cmdLineOption, options) { 84 if (typeof cmdLineOption === 'object') { 85 options = cmdLineOption; 86 cmdLineOption = undefined; 87 } 88 89 let throwInDomainErrHandlerOpt; 90 if (options.throwInDomainErrHandler) 91 throwInDomainErrHandlerOpt = 'throwInDomainErrHandler'; 92 93 let cmdToExec = ''; 94 if (!common.isWindows) { 95 // Do not create core files, as it can take a lot of disk space on 96 // continuous testing and developers' machines 97 cmdToExec += 'ulimit -c 0 && '; 98 } 99 100 let useTryCatchOpt; 101 if (options.useTryCatch) 102 useTryCatchOpt = 'useTryCatch'; 103 104 cmdToExec += `"${process.argv[0]}" ${cmdLineOption ? cmdLineOption : ''} "${ 105 process.argv[1]}" child ${throwInDomainErrHandlerOpt} ${useTryCatchOpt}`; 106 107 const child = exec(cmdToExec); 108 109 if (child) { 110 child.on('exit', function onChildExited(exitCode, signal) { 111 // When throwing errors from the top-level domain error handler 112 // outside of a try/catch block, the process should not exit gracefully 113 if (!options.useTryCatch && options.throwInDomainErrHandler) { 114 if (cmdLineOption === '--abort_on_uncaught_exception') { 115 assert(common.nodeProcessAborted(exitCode, signal), 116 'process should have aborted, but did not'); 117 } else { 118 // By default, uncaught exceptions make node exit with an exit 119 // code of 7. 120 assert.strictEqual(exitCode, 7); 121 assert.strictEqual(signal, null); 122 } 123 } else { 124 // If the top-level domain's error handler does not throw, 125 // the process must exit gracefully, whether or not 126 // --abort_on_uncaught_exception was passed on the command line 127 assert.strictEqual(exitCode, 0); 128 assert.strictEqual(signal, null); 129 } 130 }); 131 } 132 } 133 134 testDomainExceptionHandling('--abort_on_uncaught_exception', { 135 throwInDomainErrHandler: false, 136 useTryCatch: false 137 }); 138 139 testDomainExceptionHandling('--abort_on_uncaught_exception', { 140 throwInDomainErrHandler: false, 141 useTryCatch: true 142 }); 143 144 testDomainExceptionHandling('--abort_on_uncaught_exception', { 145 throwInDomainErrHandler: true, 146 useTryCatch: false 147 }); 148 149 testDomainExceptionHandling('--abort_on_uncaught_exception', { 150 throwInDomainErrHandler: true, 151 useTryCatch: true 152 }); 153 154 testDomainExceptionHandling({ 155 throwInDomainErrHandler: false 156 }); 157 158 testDomainExceptionHandling({ 159 throwInDomainErrHandler: false, 160 useTryCatch: false 161 }); 162 163 testDomainExceptionHandling({ 164 throwInDomainErrHandler: true, 165 useTryCatch: true 166 }); 167} 168