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