1'use strict';
2
3// This test makes sure that when throwing an error from a domain, and then
4// handling that error in an uncaughtException handler by throwing an error
5// again, the exit code, signal and error messages are the ones we expect with
6// and without using --abort-on-uncaught-exception.
7
8const common = require('../common');
9const assert = require('assert');
10const child_process = require('child_process');
11const domain = require('domain');
12
13const uncaughtExceptionHandlerErrMsg = 'boom from uncaughtException handler';
14const domainErrMsg = 'boom from domain';
15
16const RAN_UNCAUGHT_EXCEPTION_HANDLER_EXIT_CODE = 42;
17
18if (process.argv[2] === 'child') {
19  process.on('uncaughtException', common.mustCall(function onUncaught() {
20    if (process.execArgv.includes('--abort-on-uncaught-exception')) {
21      // When passing --abort-on-uncaught-exception to the child process,
22      // we want to make sure that this handler (the process' uncaughtException
23      // event handler) wasn't called. Unfortunately we can't parse the child
24      // process' output to do that, since on Windows the standard error output
25      // is not properly flushed in V8's Isolate::Throw right before the
26      // process aborts due to an uncaught exception, and thus the error
27      // message representing the error that was thrown cannot be read by the
28      // parent process. So instead of parsing the child process' standard
29      // error, the parent process will check that in the case
30      // --abort-on-uncaught-exception was passed, the process did not exit
31      // with exit code RAN_UNCAUGHT_EXCEPTION_HANDLER_EXIT_CODE.
32      process.exit(RAN_UNCAUGHT_EXCEPTION_HANDLER_EXIT_CODE);
33    } else {
34      // On the other hand, when not passing --abort-on-uncaught-exception to
35      // the node process, we want to throw in this event handler to make sure
36      // that the proper error message, exit code and signal are the ones we
37      // expect.
38      throw new Error(uncaughtExceptionHandlerErrMsg);
39    }
40  }));
41
42  const d = domain.create();
43  d.run(common.mustCall(function() {
44    throw new Error(domainErrMsg);
45  }));
46} else {
47  runTestWithoutAbortOnUncaughtException();
48  runTestWithAbortOnUncaughtException();
49}
50
51function runTestWithoutAbortOnUncaughtException() {
52  child_process.exec(
53    createTestCmdLine(),
54    function onTestDone(err, stdout, stderr) {
55      // When _not_ passing --abort-on-uncaught-exception, the process'
56      // uncaughtException handler _must_ be called, and thus the error
57      // message must include only the message of the error thrown from the
58      // process' uncaughtException handler.
59      assert(stderr.includes(uncaughtExceptionHandlerErrMsg),
60             'stderr output must include proper uncaughtException ' +
61             'handler\'s error\'s message');
62      assert(!stderr.includes(domainErrMsg),
63             'stderr output must not include domain\'s error\'s message');
64
65      assert.notStrictEqual(err.code, 0,
66                            'child process should have exited with a ' +
67                            'non-zero exit code, but did not');
68    }
69  );
70}
71
72function runTestWithAbortOnUncaughtException() {
73  child_process.exec(createTestCmdLine({
74    withAbortOnUncaughtException: true
75  }), function onTestDone(err, stdout, stderr) {
76    assert.notStrictEqual(err.code, RAN_UNCAUGHT_EXCEPTION_HANDLER_EXIT_CODE,
77                          'child process should not have run its ' +
78                          'uncaughtException event handler');
79    assert(common.nodeProcessAborted(err.code, err.signal),
80           'process should have aborted, but did not');
81  });
82}
83
84function createTestCmdLine(options) {
85  let testCmd = '';
86
87  if (!common.isWindows) {
88    // Do not create core files, as it can take a lot of disk space on
89    // continuous testing and developers' machines
90    testCmd += 'ulimit -c 0 && ';
91  }
92
93  testCmd += `"${process.argv[0]}"`;
94
95  if (options && options.withAbortOnUncaughtException) {
96    testCmd += ' --abort-on-uncaught-exception';
97  }
98
99  testCmd += ` "${process.argv[1]}" child`;
100
101  return testCmd;
102}
103