'use strict'; // The goal of this test is to make sure that errors thrown within domains // are handled correctly. It checks that the process' 'uncaughtException' event // is emitted when appropriate, and not emitted when it shouldn't. It also // checks that the proper domain error handlers are called when they should // be called, and not called when they shouldn't. const common = require('../common'); const assert = require('assert'); const domain = require('domain'); const child_process = require('child_process'); const tests = []; function test1() { // Throwing from an async callback from within a domain that doesn't have // an error handler must result in emitting the process' uncaughtException // event. const d = domain.create(); d.run(function() { setTimeout(function onTimeout() { throw new Error('boom!'); }, 1); }); } tests.push({ fn: test1, expectedMessages: ['uncaughtException'] }); function test2() { // Throwing from from within a domain that doesn't have an error handler must // result in emitting the process' uncaughtException event. const d2 = domain.create(); d2.run(function() { throw new Error('boom!'); }); } tests.push({ fn: test2, expectedMessages: ['uncaughtException'] }); function test3() { // This test creates two nested domains: d3 and d4. d4 doesn't register an // error handler, but d3 does. The error is handled by the d3 domain and thus // an 'uncaughtException' event should _not_ be emitted. const d3 = domain.create(); const d4 = domain.create(); d3.on('error', function onErrorInD3Domain() { process.send('errorHandledByDomain'); }); d3.run(function() { d4.run(function() { throw new Error('boom!'); }); }); } tests.push({ fn: test3, expectedMessages: ['errorHandledByDomain'] }); function test4() { // This test creates two nested domains: d5 and d6. d6 doesn't register an // error handler. When the timer's callback is called, because async // operations like timer callbacks are bound to the domain that was active // at the time of their creation, and because both d5 and d6 domains have // exited by the time the timer's callback is called, its callback runs with // only d6 on the domains stack. Since d6 doesn't register an error handler, // the process' uncaughtException event should be emitted. const d5 = domain.create(); const d6 = domain.create(); d5.on('error', function onErrorInD2Domain() { process.send('errorHandledByDomain'); }); d5.run(function() { d6.run(function() { setTimeout(function onTimeout() { throw new Error('boom!'); }, 1); }); }); } tests.push({ fn: test4, expectedMessages: ['uncaughtException'] }); function test5() { // This test creates two nested domains: d7 and d8. d8 _does_ register an // error handler, so throwing within that domain should not emit an uncaught // exception. const d7 = domain.create(); const d8 = domain.create(); d8.on('error', function onErrorInD3Domain() { process.send('errorHandledByDomain'); }); d7.run(function() { d8.run(function() { throw new Error('boom!'); }); }); } tests.push({ fn: test5, expectedMessages: ['errorHandledByDomain'] }); function test6() { // This test creates two nested domains: d9 and d10. d10 _does_ register an // error handler, so throwing within that domain in an async callback should // _not_ emit an uncaught exception. // const d9 = domain.create(); const d10 = domain.create(); d10.on('error', function onErrorInD2Domain() { process.send('errorHandledByDomain'); }); d9.run(function() { d10.run(function() { setTimeout(function onTimeout() { throw new Error('boom!'); }, 1); }); }); } tests.push({ fn: test6, expectedMessages: ['errorHandledByDomain'] }); if (process.argv[2] === 'child') { const testIndex = process.argv[3]; process.on('uncaughtException', function onUncaughtException() { process.send('uncaughtException'); }); tests[testIndex].fn(); } else { // Run each test's function in a child process. Listen on // messages sent by each child process and compare expected // messages defined for each test with the actual received messages. tests.forEach(function doTest(test, testIndex) { const testProcess = child_process.fork(__filename, ['child', testIndex]); testProcess.on('message', function onMsg(msg) { if (test.messagesReceived === undefined) test.messagesReceived = []; test.messagesReceived.push(msg); }); testProcess.on('disconnect', common.mustCall(function onExit() { // Make sure that all expected messages were sent from the // child process test.expectedMessages.forEach(function(expectedMessage) { const msgs = test.messagesReceived; if (msgs === undefined || !msgs.includes(expectedMessage)) { assert.fail(`test ${test.fn.name} should have sent message: ${ expectedMessage} but didn't`); } }); if (test.messagesReceived) { test.messagesReceived.forEach(function(receivedMessage) { if (!test.expectedMessages.includes(receivedMessage)) { assert.fail(`test ${test.fn.name} should not have sent message: ${ receivedMessage} but did`); } }); } })); }); }