• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const common = require('../../common');
4const assert = require('assert');
5const domain = require('domain');
6const binding = require(`./build/${common.buildType}/binding`);
7const makeCallback = binding.makeCallback;
8
9// Make sure this is run in the future.
10const mustCallCheckDomains = common.mustCall(checkDomains);
11
12
13// Make sure that using MakeCallback allows the error to propagate.
14assert.throws(function() {
15  makeCallback({}, function() {
16    throw new Error('hi from domain error');
17  });
18}, /^Error: hi from domain error$/);
19
20
21// Check the execution order of the nextTickQueue and MicrotaskQueue in
22// relation to running multiple MakeCallback's from bootstrap,
23// node::MakeCallback() and node::AsyncWrap::MakeCallback().
24// TODO(trevnorris): Is there a way to verify this is being run during
25// bootstrap?
26(function verifyExecutionOrder(arg) {
27  const results = [];
28
29  // Processing of the MicrotaskQueue is manually handled by node. They are not
30  // processed until after the nextTickQueue has been processed.
31  Promise.resolve(1).then(common.mustCall(function() {
32    results.push(7);
33  }));
34
35  // The nextTick should run after all immediately invoked calls.
36  process.nextTick(common.mustCall(function() {
37    results.push(3);
38
39    // Run same test again but while processing the nextTickQueue to make sure
40    // the following MakeCallback call breaks in the middle of processing the
41    // queue and allows the script to run normally.
42    process.nextTick(common.mustCall(function() {
43      results.push(6);
44    }));
45
46    makeCallback({}, common.mustCall(function() {
47      results.push(4);
48    }));
49
50    results.push(5);
51  }));
52
53  results.push(0);
54
55  // MakeCallback is calling the function immediately, but should then detect
56  // that a script is already in the middle of execution and return before
57  // either the nextTickQueue or MicrotaskQueue are processed.
58  makeCallback({}, common.mustCall(function() {
59    results.push(1);
60  }));
61
62  // This should run before either the nextTickQueue or MicrotaskQueue are
63  // processed. Previously MakeCallback would not detect this circumstance
64  // and process them immediately.
65  results.push(2);
66
67  setImmediate(common.mustCall(function() {
68    for (let i = 0; i < results.length; i++) {
69      assert.strictEqual(results[i], i,
70                         `verifyExecutionOrder(${arg}) results: ${results}`);
71    }
72    if (arg === 1) {
73      // The tests are first run on bootstrap during LoadEnvironment() in
74      // src/node.cc. Now run the tests through node::MakeCallback().
75      setImmediate(function() {
76        makeCallback({}, common.mustCall(function() {
77          verifyExecutionOrder(2);
78        }));
79      });
80    } else if (arg === 2) {
81      // Make sure there are no conflicts using node::MakeCallback()
82      // within timers.
83      setTimeout(common.mustCall(function() {
84        verifyExecutionOrder(3);
85      }), 10);
86    } else if (arg === 3) {
87      mustCallCheckDomains();
88    } else {
89      throw new Error('UNREACHABLE');
90    }
91  }));
92}(1));
93
94
95function checkDomains() {
96  // Check that domains are properly entered/exited when called in multiple
97  // levels from both node::MakeCallback() and AsyncWrap::MakeCallback
98  setImmediate(common.mustCall(function() {
99    const d1 = domain.create();
100    const d2 = domain.create();
101    const d3 = domain.create();
102
103    makeCallback({ domain: d1 }, common.mustCall(function() {
104      assert.strictEqual(d1, process.domain);
105      makeCallback({ domain: d2 }, common.mustCall(function() {
106        assert.strictEqual(d2, process.domain);
107        makeCallback({ domain: d3 }, common.mustCall(function() {
108          assert.strictEqual(d3, process.domain);
109        }));
110        assert.strictEqual(d2, process.domain);
111      }));
112      assert.strictEqual(d1, process.domain);
113    }));
114  }));
115
116  setTimeout(common.mustCall(function() {
117    const d1 = domain.create();
118    const d2 = domain.create();
119    const d3 = domain.create();
120
121    makeCallback({ domain: d1 }, common.mustCall(function() {
122      assert.strictEqual(d1, process.domain);
123      makeCallback({ domain: d2 }, common.mustCall(function() {
124        assert.strictEqual(d2, process.domain);
125        makeCallback({ domain: d3 }, common.mustCall(function() {
126          assert.strictEqual(d3, process.domain);
127        }));
128        assert.strictEqual(d2, process.domain);
129      }));
130      assert.strictEqual(d1, process.domain);
131    }));
132  }), 1);
133
134  function testTimer(id) {
135    // Make sure nextTick, setImmediate and setTimeout can all recover properly
136    // after a thrown makeCallback call.
137    const d = domain.create();
138    d.on('error', common.mustCall(function(e) {
139      assert.strictEqual(e.message, `throw from domain ${id}`);
140    }));
141    makeCallback({ domain: d }, function() {
142      throw new Error(`throw from domain ${id}`);
143    });
144    throw new Error('UNREACHABLE');
145  }
146
147  process.nextTick(common.mustCall(testTimer), 3);
148  setImmediate(common.mustCall(testTimer), 2);
149  setTimeout(common.mustCall(testTimer), 1, 1);
150}
151