• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
9 const common = require('../common');
10 const assert = require('assert');
11 const domain = require('domain');
12 const child_process = require('child_process');
13 
14 const tests = [];
15 
16 function 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 
28 tests.push({
29   fn: test1,
30   expectedMessages: ['uncaughtException']
31 });
32 
33 function 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 
42 tests.push({
43   fn: test2,
44   expectedMessages: ['uncaughtException']
45 });
46 
47 function 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 
65 tests.push({
66   fn: test3,
67   expectedMessages: ['errorHandledByDomain']
68 });
69 
70 function 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 
94 tests.push({
95   fn: test4,
96   expectedMessages: ['uncaughtException']
97 });
98 
99 function 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 }
116 tests.push({
117   fn: test5,
118   expectedMessages: ['errorHandledByDomain']
119 });
120 
121 function 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 
142 tests.push({
143   fn: test6,
144   expectedMessages: ['errorHandledByDomain']
145 });
146 
147 if (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