text: 'use strict';
  
  /*
   * The goal of this test is to make sure that errors thrown within domains
   * are handled correctly. It checks that the process' 'uncaughtException' event
   * is emitted when appropriate, and not emitted when it shouldn't. It also
   * checks that the proper domain error handlers are called when they should
   * be called, and not called when they shouldn't.
   */
  
  const common = require('../common');
  const assert = require('assert');
  const domain = require('domain');
  const child_process = require('child_process');
  
  const tests = [];
  
  function test1() {
    /*
     * Throwing from an async callback from within a domain that doesn't have
     * an error handler must result in emitting the process' uncaughtException
     * event.
     */
    const d = domain.create();
    d.run(function() {
      setTimeout(function onTimeout() {
        throw new Error('boom!');
      }, 1);
    });
  }
  
  tests.push({
    fn: test1,
    expectedMessages: ['uncaughtException']
  });
  
  function test2() {
    /*
     * Throwing from from within a domain that doesn't have an error handler must
     * result in emitting the process' uncaughtException event.
     */
    const d2 = domain.create();
    d2.run(function() {
      throw new Error('boom!');
    });
  }
  
  tests.push({
    fn: test2,
    expectedMessages: ['uncaughtException']
  });
  
  function test3() {
    /*
     * This test creates two nested domains: d3 and d4. d4 doesn't register an
     * error handler, but d3 does. The error is handled by the d3 domain and thus
     * an 'uncaughtException' event should _not_ be emitted.
     */
    const d3 = domain.create();
    const d4 = domain.create();
  
    d3.on('error', function onErrorInD3Domain() {
      process.send('errorHandledByDomain');
    });
  
    d3.run(function() {
      d4.run(function() {
        throw new Error('boom!');
      });
    });
  }
  
  tests.push({
    fn: test3,
    expectedMessages: ['errorHandledByDomain']
  });
  
  function test4() {
    /*
     * This test creates two nested domains: d5 and d6. d6 doesn't register an
     * error handler. When the timer's callback is called, because async
     * operations like timer callbacks are bound to the domain that was active
     * at the time of their creation, and because both d5 and d6 domains have
     * exited by the time the timer's callback is called, its callback runs with
     * only d6 on the domains stack. Since d6 doesn't register an error handler,
     * the process' uncaughtException event should be emitted.
     */
    const d5 = domain.create();
    const d6 = domain.create();
  
    d5.on('error', function onErrorInD2Domain() {
      process.send('errorHandledByDomain');
    });
  
    d5.run(function() {
      d6.run(function() {
        setTimeout(function onTimeout() {
          throw new Error('boom!');
        }, 1);
      });
    });
  }
  
  tests.push({
    fn: test4,
    expectedMessages: ['uncaughtException']
  });
  
  function test5() {
    /*
     * This test creates two nested domains: d7 and d8. d8 _does_ register an
     * error handler, so throwing within that domain should not emit an uncaught
     * exception.
     */
    const d7 = domain.create();
    const d8 = domain.create();
  
    d8.on('error', function onErrorInD3Domain() {
      process.send('errorHandledByDomain');
    });
  
    d7.run(function() {
      d8.run(function() {
        throw new Error('boom!');
      });
    });
  }
  tests.push({
    fn: test5,
    expectedMessages: ['errorHandledByDomain']
  });
  
  function test6() {
    /*
     * This test creates two nested domains: d9 and d10. d10 _does_ register an
     * error handler, so throwing within that domain in an async callback should
     * _not_ emit an uncaught exception.
     */
    const d9 = domain.create();
    const d10 = domain.create();
  
    d10.on('error', function onErrorInD2Domain() {
      process.send('errorHandledByDomain');
    });
  
    d9.run(function() {
      d10.run(function() {
        setTimeout(function onTimeout() {
          throw new Error('boom!');
        }, 1);
      });
    });
  }
  
  tests.push({
    fn: test6,
    expectedMessages: ['errorHandledByDomain']
  });
  
  if (process.argv[2] === 'child') {
    const testIndex = process.argv[3];
    process.on('uncaughtException', function onUncaughtException() {
      process.send('uncaughtException');
    });
  
    tests[testIndex].fn();
  } else {
    // Run each test's function in a child process. Listen on
    // messages sent by each child process and compare expected
    // messages defined for each test with the actual received messages.
    tests.forEach(function doTest(test, testIndex) {
      const testProcess = child_process.fork(__filename, ['child', testIndex]);
  
      testProcess.on('message', function onMsg(msg) {
        if (test.messagesReceived === undefined)
          test.messagesReceived = [];
  
        test.messagesReceived.push(msg);
      });
  
      testProcess.on('disconnect', common.mustCall(function onExit() {
        // Make sure that all expected messages were sent from the
        // child process
        test.expectedMessages.forEach(function(expectedMessage) {
          if (test.messagesReceived === undefined ||
            test.messagesReceived.indexOf(expectedMessage) === -1)
            assert.fail(`test ${test.fn.name} should have sent message: ${
                        expectedMessage} but didn't`);
        });
  
        if (test.messagesReceived) {
          test.messagesReceived.forEach(function(receivedMessage) {
            if (test.expectedMessages.indexOf(receivedMessage) === -1) {
              assert.fail(`test ${test.fn.name} should not have sent message: ${
                          receivedMessage} but did`);
            }
          });
        }
      }));
    });
  }
  