summaryrefslogtreecommitdiff
path: root/test/parallel/test-domain-with-abort-on-uncaught-exception.js
blob: 0707f123b8c3a1255bfc83c21898ef0d00025e92 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
'use strict';

const common = require('../common');
const assert = require('assert');
const fs = require('fs');

/*
 * The goal of this test is to make sure that:
 *
 * - Even if --abort_on_uncaught_exception is passed on the command line,
 * setting up a top-level domain error handler and throwing an error
 * within this domain does *not* make the process abort. The process exits
 * gracefully.
 *
 * - When passing --abort_on_uncaught_exception on the command line and
 * setting up a top-level domain error handler, an error thrown
 * within this domain's error handler *does* make the process abort.
 *
 * - When *not* passing --abort_on_uncaught_exception on the command line and
 * setting up a top-level domain error handler, an error thrown within this
 * domain's error handler does *not* make the process abort, but makes it exit
 * with the proper failure exit code.
 *
 * - When throwing an error within the top-level domain's error handler
 * within a try/catch block, the process should exit gracefully, whether or
 * not --abort_on_uncaught_exception is passed on the command line.
 */

const domainErrHandlerExMessage = 'exception from domain error handler';

if (process.argv[2] === 'child') {
  const domain = require('domain');
  const d = domain.create();

  process.on('uncaughtException', function onUncaughtException() {
    // The process' uncaughtException event must not be emitted when
    // an error handler is setup on the top-level domain.
    // Exiting with exit code of 42 here so that it would assert when
    // the parent checks the child exit code.
    process.exit(42);
  });

  d.on('error', function(err) {
    // Swallowing the error on purpose if 'throwInDomainErrHandler' is not
    // set
    if (process.argv.includes('throwInDomainErrHandler')) {
      // If useTryCatch is set, wrap the throw in a try/catch block.
      // This is to make sure that a caught exception does not trigger
      // an abort.
      if (process.argv.includes('useTryCatch')) {
        try {
          throw new Error(domainErrHandlerExMessage);
        } catch {
        }
      } else {
        throw new Error(domainErrHandlerExMessage);
      }
    }
  });

  d.run(function doStuff() {
    // Throwing from within different types of callbacks as each of them
    // handles domains differently
    process.nextTick(function() {
      throw new Error('Error from nextTick callback');
    });

    fs.exists('/non/existing/file', function onExists(exists) {
      throw new Error('Error from fs.exists callback');
    });

    setImmediate(function onSetImmediate() {
      throw new Error('Error from setImmediate callback');
    });

    setTimeout(function onTimeout() {
      throw new Error('Error from setTimeout callback');
    }, 0);

    throw new Error('Error from domain.run callback');
  });
} else {
  const exec = require('child_process').exec;

  function testDomainExceptionHandling(cmdLineOption, options) {
    if (typeof cmdLineOption === 'object') {
      options = cmdLineOption;
      cmdLineOption = undefined;
    }

    let throwInDomainErrHandlerOpt;
    if (options.throwInDomainErrHandler)
      throwInDomainErrHandlerOpt = 'throwInDomainErrHandler';

    let cmdToExec = '';
    if (!common.isWindows) {
      // Do not create core files, as it can take a lot of disk space on
      // continuous testing and developers' machines
      cmdToExec += 'ulimit -c 0 && ';
    }

    let useTryCatchOpt;
    if (options.useTryCatch)
      useTryCatchOpt = 'useTryCatch';

    cmdToExec += `"${process.argv[0]}" ${cmdLineOption ? cmdLineOption : ''} "${
      process.argv[1]}" child ${throwInDomainErrHandlerOpt} ${useTryCatchOpt}`;

    const child = exec(cmdToExec);

    if (child) {
      child.on('exit', function onChildExited(exitCode, signal) {
        // When throwing errors from the top-level domain error handler
        // outside of a try/catch block, the process should not exit gracefully
        if (!options.useTryCatch && options.throwInDomainErrHandler) {
          if (cmdLineOption === '--abort_on_uncaught_exception') {
            assert(common.nodeProcessAborted(exitCode, signal),
                   'process should have aborted, but did not');
          } else {
            // By default, uncaught exceptions make node exit with an exit
            // code of 7.
            assert.strictEqual(exitCode, 7);
            assert.strictEqual(signal, null);
          }
        } else {
          // If the top-level domain's error handler does not throw,
          // the process must exit gracefully, whether or not
          // --abort_on_uncaught_exception was passed on the command line
          assert.strictEqual(exitCode, 0);
          assert.strictEqual(signal, null);
        }
      });
    }
  }

  testDomainExceptionHandling('--abort_on_uncaught_exception', {
    throwInDomainErrHandler: false,
    useTryCatch: false
  });

  testDomainExceptionHandling('--abort_on_uncaught_exception', {
    throwInDomainErrHandler: false,
    useTryCatch: true
  });

  testDomainExceptionHandling('--abort_on_uncaught_exception', {
    throwInDomainErrHandler: true,
    useTryCatch: false
  });

  testDomainExceptionHandling('--abort_on_uncaught_exception', {
    throwInDomainErrHandler: true,
    useTryCatch: true
  });

  testDomainExceptionHandling({
    throwInDomainErrHandler: false
  });

  testDomainExceptionHandling({
    throwInDomainErrHandler: false,
    useTryCatch: false
  });

  testDomainExceptionHandling({
    throwInDomainErrHandler: true,
    useTryCatch: true
  });
}