'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.indexOf(receivedMessage) === -1) { assert.fail(`test ${test.fn.name} should not have sent message: ${ receivedMessage} but did`); } }); } })); }); }