1'use strict'; 2 3/* 4 * The goal of this test is to make sure that errors thrown within domains 5 * are handled correctly. It checks that the process' 'uncaughtException' event 6 * is emitted when appropriate, and not emitted when it shouldn't. It also 7 * checks that the proper domain error handlers are called when they should 8 * be called, and not called when they shouldn't. 9 */ 10 11const common = require('../common'); 12const assert = require('assert'); 13const domain = require('domain'); 14const child_process = require('child_process'); 15 16const tests = []; 17 18function test1() { 19 /* 20 * Throwing from an async callback from within a domain that doesn't have 21 * an error handler must result in emitting the process' uncaughtException 22 * event. 23 */ 24 const d = domain.create(); 25 d.run(function() { 26 setTimeout(function onTimeout() { 27 throw new Error('boom!'); 28 }, 1); 29 }); 30} 31 32tests.push({ 33 fn: test1, 34 expectedMessages: ['uncaughtException'] 35}); 36 37function test2() { 38 /* 39 * Throwing from from within a domain that doesn't have an error handler must 40 * result in emitting the process' uncaughtException event. 41 */ 42 const d2 = domain.create(); 43 d2.run(function() { 44 throw new Error('boom!'); 45 }); 46} 47 48tests.push({ 49 fn: test2, 50 expectedMessages: ['uncaughtException'] 51}); 52 53function test3() { 54 /* 55 * This test creates two nested domains: d3 and d4. d4 doesn't register an 56 * error handler, but d3 does. The error is handled by the d3 domain and thus 57 * an 'uncaughtException' event should _not_ be emitted. 58 */ 59 const d3 = domain.create(); 60 const d4 = domain.create(); 61 62 d3.on('error', function onErrorInD3Domain() { 63 process.send('errorHandledByDomain'); 64 }); 65 66 d3.run(function() { 67 d4.run(function() { 68 throw new Error('boom!'); 69 }); 70 }); 71} 72 73tests.push({ 74 fn: test3, 75 expectedMessages: ['errorHandledByDomain'] 76}); 77 78function test4() { 79 /* 80 * This test creates two nested domains: d5 and d6. d6 doesn't register an 81 * error handler. When the timer's callback is called, because async 82 * operations like timer callbacks are bound to the domain that was active 83 * at the time of their creation, and because both d5 and d6 domains have 84 * exited by the time the timer's callback is called, its callback runs with 85 * only d6 on the domains stack. Since d6 doesn't register an error handler, 86 * the process' uncaughtException event should be emitted. 87 */ 88 const d5 = domain.create(); 89 const d6 = domain.create(); 90 91 d5.on('error', function onErrorInD2Domain() { 92 process.send('errorHandledByDomain'); 93 }); 94 95 d5.run(function() { 96 d6.run(function() { 97 setTimeout(function onTimeout() { 98 throw new Error('boom!'); 99 }, 1); 100 }); 101 }); 102} 103 104tests.push({ 105 fn: test4, 106 expectedMessages: ['uncaughtException'] 107}); 108 109function test5() { 110 /* 111 * This test creates two nested domains: d7 and d8. d8 _does_ register an 112 * error handler, so throwing within that domain should not emit an uncaught 113 * exception. 114 */ 115 const d7 = domain.create(); 116 const d8 = domain.create(); 117 118 d8.on('error', function onErrorInD3Domain() { 119 process.send('errorHandledByDomain'); 120 }); 121 122 d7.run(function() { 123 d8.run(function() { 124 throw new Error('boom!'); 125 }); 126 }); 127} 128tests.push({ 129 fn: test5, 130 expectedMessages: ['errorHandledByDomain'] 131}); 132 133function test6() { 134 /* 135 * This test creates two nested domains: d9 and d10. d10 _does_ register an 136 * error handler, so throwing within that domain in an async callback should 137 * _not_ emit an uncaught exception. 138 */ 139 const d9 = domain.create(); 140 const d10 = domain.create(); 141 142 d10.on('error', function onErrorInD2Domain() { 143 process.send('errorHandledByDomain'); 144 }); 145 146 d9.run(function() { 147 d10.run(function() { 148 setTimeout(function onTimeout() { 149 throw new Error('boom!'); 150 }, 1); 151 }); 152 }); 153} 154 155tests.push({ 156 fn: test6, 157 expectedMessages: ['errorHandledByDomain'] 158}); 159 160if (process.argv[2] === 'child') { 161 const testIndex = process.argv[3]; 162 process.on('uncaughtException', function onUncaughtException() { 163 process.send('uncaughtException'); 164 }); 165 166 tests[testIndex].fn(); 167} else { 168 // Run each test's function in a child process. Listen on 169 // messages sent by each child process and compare expected 170 // messages defined for each test with the actual received messages. 171 tests.forEach(function doTest(test, testIndex) { 172 const testProcess = child_process.fork(__filename, ['child', testIndex]); 173 174 testProcess.on('message', function onMsg(msg) { 175 if (test.messagesReceived === undefined) 176 test.messagesReceived = []; 177 178 test.messagesReceived.push(msg); 179 }); 180 181 testProcess.on('disconnect', common.mustCall(function onExit() { 182 // Make sure that all expected messages were sent from the 183 // child process 184 test.expectedMessages.forEach(function(expectedMessage) { 185 const msgs = test.messagesReceived; 186 if (msgs === undefined || !msgs.includes(expectedMessage)) { 187 assert.fail(`test ${test.fn.name} should have sent message: ${ 188 expectedMessage} but didn't`); 189 } 190 }); 191 192 if (test.messagesReceived) { 193 test.messagesReceived.forEach(function(receivedMessage) { 194 if (!test.expectedMessages.includes(receivedMessage)) { 195 assert.fail(`test ${test.fn.name} should not have sent message: ${ 196 receivedMessage} but did`); 197 } 198 }); 199 } 200 })); 201 }); 202} 203