• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const common = require('../common');
4const assert = require('assert');
5const fs = require('fs');
6
7/*
8 * The goal of this test is to make sure that:
9 *
10 * - Even if --abort_on_uncaught_exception is passed on the command line,
11 * setting up a top-level domain error handler and throwing an error
12 * within this domain does *not* make the process abort. The process exits
13 * gracefully.
14 *
15 * - When passing --abort_on_uncaught_exception on the command line and
16 * setting up a top-level domain error handler, an error thrown
17 * within this domain's error handler *does* make the process abort.
18 *
19 * - When *not* passing --abort_on_uncaught_exception on the command line and
20 * setting up a top-level domain error handler, an error thrown within this
21 * domain's error handler does *not* make the process abort, but makes it exit
22 * with the proper failure exit code.
23 *
24 * - When throwing an error within the top-level domain's error handler
25 * within a try/catch block, the process should exit gracefully, whether or
26 * not --abort_on_uncaught_exception is passed on the command line.
27 */
28
29const domainErrHandlerExMessage = 'exception from domain error handler';
30
31if (process.argv[2] === 'child') {
32  const domain = require('domain');
33  const d = domain.create();
34
35  process.on('uncaughtException', function onUncaughtException() {
36    // The process' uncaughtException event must not be emitted when
37    // an error handler is setup on the top-level domain.
38    // Exiting with exit code of 42 here so that it would assert when
39    // the parent checks the child exit code.
40    process.exit(42);
41  });
42
43  d.on('error', function(err) {
44    // Swallowing the error on purpose if 'throwInDomainErrHandler' is not
45    // set
46    if (process.argv.includes('throwInDomainErrHandler')) {
47      // If useTryCatch is set, wrap the throw in a try/catch block.
48      // This is to make sure that a caught exception does not trigger
49      // an abort.
50      if (process.argv.includes('useTryCatch')) {
51        try {
52          throw new Error(domainErrHandlerExMessage);
53        } catch {
54        }
55      } else {
56        throw new Error(domainErrHandlerExMessage);
57      }
58    }
59  });
60
61  d.run(function doStuff() {
62    // Throwing from within different types of callbacks as each of them
63    // handles domains differently
64    process.nextTick(function() {
65      throw new Error('Error from nextTick callback');
66    });
67
68    fs.exists('/non/existing/file', function onExists(exists) {
69      throw new Error('Error from fs.exists callback');
70    });
71
72    setImmediate(function onSetImmediate() {
73      throw new Error('Error from setImmediate callback');
74    });
75
76    setTimeout(function onTimeout() {
77      throw new Error('Error from setTimeout callback');
78    }, 0);
79
80    throw new Error('Error from domain.run callback');
81  });
82} else {
83  const exec = require('child_process').exec;
84
85  function testDomainExceptionHandling(cmdLineOption, options) {
86    if (typeof cmdLineOption === 'object') {
87      options = cmdLineOption;
88      cmdLineOption = undefined;
89    }
90
91    let throwInDomainErrHandlerOpt;
92    if (options.throwInDomainErrHandler)
93      throwInDomainErrHandlerOpt = 'throwInDomainErrHandler';
94
95    let cmdToExec = '';
96    if (!common.isWindows) {
97      // Do not create core files, as it can take a lot of disk space on
98      // continuous testing and developers' machines
99      cmdToExec += 'ulimit -c 0 && ';
100    }
101
102    let useTryCatchOpt;
103    if (options.useTryCatch)
104      useTryCatchOpt = 'useTryCatch';
105
106    cmdToExec += `"${process.argv[0]}" ${cmdLineOption ? cmdLineOption : ''} "${
107      process.argv[1]}" child ${throwInDomainErrHandlerOpt} ${useTryCatchOpt}`;
108
109    const child = exec(cmdToExec);
110
111    if (child) {
112      child.on('exit', function onChildExited(exitCode, signal) {
113        // When throwing errors from the top-level domain error handler
114        // outside of a try/catch block, the process should not exit gracefully
115        if (!options.useTryCatch && options.throwInDomainErrHandler) {
116          if (cmdLineOption === '--abort_on_uncaught_exception') {
117            assert(common.nodeProcessAborted(exitCode, signal),
118                   'process should have aborted, but did not');
119          } else {
120            // By default, uncaught exceptions make node exit with an exit
121            // code of 7.
122            assert.strictEqual(exitCode, 7);
123            assert.strictEqual(signal, null);
124          }
125        } else {
126          // If the top-level domain's error handler does not throw,
127          // the process must exit gracefully, whether or not
128          // --abort_on_uncaught_exception was passed on the command line
129          assert.strictEqual(exitCode, 0);
130          assert.strictEqual(signal, null);
131        }
132      });
133    }
134  }
135
136  testDomainExceptionHandling('--abort_on_uncaught_exception', {
137    throwInDomainErrHandler: false,
138    useTryCatch: false
139  });
140
141  testDomainExceptionHandling('--abort_on_uncaught_exception', {
142    throwInDomainErrHandler: false,
143    useTryCatch: true
144  });
145
146  testDomainExceptionHandling('--abort_on_uncaught_exception', {
147    throwInDomainErrHandler: true,
148    useTryCatch: false
149  });
150
151  testDomainExceptionHandling('--abort_on_uncaught_exception', {
152    throwInDomainErrHandler: true,
153    useTryCatch: true
154  });
155
156  testDomainExceptionHandling({
157    throwInDomainErrHandler: false
158  });
159
160  testDomainExceptionHandling({
161    throwInDomainErrHandler: false,
162    useTryCatch: false
163  });
164
165  testDomainExceptionHandling({
166    throwInDomainErrHandler: true,
167    useTryCatch: true
168  });
169}
170