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