• 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
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