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