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