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 // Continue regardless of error. 53 } 54 } else { 55 throw new Error(domainErrHandlerExMessage); 56 } 57 } 58 }); 59 60 d.run(function doStuff() { 61 // Throwing from within different types of callbacks as each of them 62 // handles domains differently 63 process.nextTick(function() { 64 throw new Error('Error from nextTick callback'); 65 }); 66 67 fs.exists('/non/existing/file', function onExists(exists) { 68 throw new Error('Error from fs.exists callback'); 69 }); 70 71 setImmediate(function onSetImmediate() { 72 throw new Error('Error from setImmediate callback'); 73 }); 74 75 setTimeout(function onTimeout() { 76 throw new Error('Error from setTimeout callback'); 77 }, 0); 78 79 throw new Error('Error from domain.run callback'); 80 }); 81} else { 82 const exec = require('child_process').exec; 83 84 function testDomainExceptionHandling(cmdLineOption, options) { 85 if (typeof cmdLineOption === 'object') { 86 options = cmdLineOption; 87 cmdLineOption = undefined; 88 } 89 90 let throwInDomainErrHandlerOpt; 91 if (options.throwInDomainErrHandler) 92 throwInDomainErrHandlerOpt = 'throwInDomainErrHandler'; 93 94 let cmdToExec = ''; 95 if (!common.isWindows) { 96 // Do not create core files, as it can take a lot of disk space on 97 // continuous testing and developers' machines 98 cmdToExec += 'ulimit -c 0 && '; 99 } 100 101 let useTryCatchOpt; 102 if (options.useTryCatch) 103 useTryCatchOpt = 'useTryCatch'; 104 105 cmdToExec += `"${process.argv[0]}" ${cmdLineOption ? cmdLineOption : ''} "${ 106 process.argv[1]}" child ${throwInDomainErrHandlerOpt} ${useTryCatchOpt}`; 107 108 const child = exec(cmdToExec); 109 110 if (child) { 111 child.on('exit', function onChildExited(exitCode, signal) { 112 // When throwing errors from the top-level domain error handler 113 // outside of a try/catch block, the process should not exit gracefully 114 if (!options.useTryCatch && options.throwInDomainErrHandler) { 115 if (cmdLineOption === '--abort_on_uncaught_exception') { 116 assert(common.nodeProcessAborted(exitCode, signal), 117 'process should have aborted, but did not'); 118 } else { 119 // By default, uncaught exceptions make node exit with an exit 120 // code of 7. 121 assert.strictEqual(exitCode, 7); 122 assert.strictEqual(signal, null); 123 } 124 } else { 125 // If the top-level domain's error handler does not throw, 126 // the process must exit gracefully, whether or not 127 // --abort_on_uncaught_exception was passed on the command line 128 assert.strictEqual(exitCode, 0); 129 assert.strictEqual(signal, null); 130 } 131 }); 132 } 133 } 134 135 testDomainExceptionHandling('--abort_on_uncaught_exception', { 136 throwInDomainErrHandler: false, 137 useTryCatch: false 138 }); 139 140 testDomainExceptionHandling('--abort_on_uncaught_exception', { 141 throwInDomainErrHandler: false, 142 useTryCatch: true 143 }); 144 145 testDomainExceptionHandling('--abort_on_uncaught_exception', { 146 throwInDomainErrHandler: true, 147 useTryCatch: false 148 }); 149 150 testDomainExceptionHandling('--abort_on_uncaught_exception', { 151 throwInDomainErrHandler: true, 152 useTryCatch: true 153 }); 154 155 testDomainExceptionHandling({ 156 throwInDomainErrHandler: false 157 }); 158 159 testDomainExceptionHandling({ 160 throwInDomainErrHandler: false, 161 useTryCatch: false 162 }); 163 164 testDomainExceptionHandling({ 165 throwInDomainErrHandler: true, 166 useTryCatch: true 167 }); 168} 169