diff --git a/test/parallel/test-domain-bind-timeout.js b/test/parallel/test-domain-bind-timeout.js new file mode 100644 index 00000000000000..f04f85bc58b22d --- /dev/null +++ b/test/parallel/test-domain-bind-timeout.js @@ -0,0 +1,17 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const domain = require('domain'); + +const d = new domain.Domain(); + +d.on('error', common.mustCall((err) => { + assert.strictEqual(err.message, 'foobar'); + assert.strictEqual(err.domain, d); + assert.strictEqual(err.domainEmitter, undefined); + assert.strictEqual(err.domainBound, undefined); + assert.strictEqual(err.domainThrown, true); +})); + +setTimeout(d.bind(() => { throw new Error('foobar'); }), 1); diff --git a/test/parallel/test-domain-ee-implicit.js b/test/parallel/test-domain-ee-implicit.js new file mode 100644 index 00000000000000..7634dd0bff86f1 --- /dev/null +++ b/test/parallel/test-domain-ee-implicit.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const domain = require('domain'); +const EventEmitter = require('events'); + +const d = new domain.Domain(); +let implicit; + +d.on('error', common.mustCall((err) => { + assert.strictEqual(err.message, 'foobar'); + assert.strictEqual(err.domain, d); + assert.strictEqual(err.domainEmitter, implicit); + assert.strictEqual(err.domainBound, undefined); + assert.strictEqual(err.domainThrown, false); +})); + +// Implicit addition of the EventEmitter by being created within a domain-bound +// context. +d.run(common.mustCall(() => { + implicit = new EventEmitter(); +})); + +setTimeout(common.mustCall(() => { + // escape from the domain, but implicit is still bound to it. + implicit.emit('error', new Error('foobar')); +}), 1); diff --git a/test/parallel/test-domain-ee.js b/test/parallel/test-domain-ee.js new file mode 100644 index 00000000000000..bc53f78445d7ac --- /dev/null +++ b/test/parallel/test-domain-ee.js @@ -0,0 +1,20 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const domain = require('domain'); +const EventEmitter = require('events'); + +const d = new domain.Domain(); +const e = new EventEmitter(); + +d.on('error', common.mustCall((err) => { + assert.strictEqual(err.message, 'foobar'); + assert.strictEqual(err.domain, d); + assert.strictEqual(err.domainEmitter, e); + assert.strictEqual(err.domainBound, undefined); + assert.strictEqual(err.domainThrown, false); +})); + +d.add(e); +e.emit('error', new Error('foobar')); diff --git a/test/parallel/test-domain-error-types.js b/test/parallel/test-domain-error-types.js new file mode 100644 index 00000000000000..6569c6de4bf1e1 --- /dev/null +++ b/test/parallel/test-domain-error-types.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const domain = require('domain'); + +// This test is similar to test-domain-multiple-errors, but uses a new domain +// for each errors. + +for (const something of [ + 42, null, undefined, false, () => {}, 'string', Symbol('foo') +]) { + const d = new domain.Domain(); + d.run(common.mustCall(() => { + process.nextTick(common.mustCall(() => { + throw something; + })); + })); + + d.on('error', common.mustCall((err) => { + assert.strictEqual(something, err); + })); +} diff --git a/test/parallel/test-domain-fs-enoent-stream.js b/test/parallel/test-domain-fs-enoent-stream.js new file mode 100644 index 00000000000000..9d28f3a1db4f83 --- /dev/null +++ b/test/parallel/test-domain-fs-enoent-stream.js @@ -0,0 +1,20 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const domain = require('domain'); +const fs = require('fs'); + +const d = new domain.Domain(); + +const fst = fs.createReadStream('stream for nonexistent file'); + +d.on('error', common.mustCall((err) => { + assert.ok(err.message.match(/^ENOENT: no such file or directory, open '/)); + assert.strictEqual(err.domain, d); + assert.strictEqual(err.domainEmitter, fst); + assert.strictEqual(err.domainBound, undefined); + assert.strictEqual(err.domainThrown, false); +})); + +d.add(fst); diff --git a/test/parallel/test-domain-implicit-binding.js b/test/parallel/test-domain-implicit-binding.js new file mode 100644 index 00000000000000..13c146e4d7b332 --- /dev/null +++ b/test/parallel/test-domain-implicit-binding.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const domain = require('domain'); +const fs = require('fs'); + +{ + const d = new domain.Domain(); + + d.on('error', common.mustCall((err) => { + assert.strictEqual(err.message, 'foobar'); + assert.strictEqual(err.domain, d); + assert.strictEqual(err.domainEmitter, undefined); + assert.strictEqual(err.domainBound, undefined); + assert.strictEqual(err.domainThrown, true); + })); + + d.run(common.mustCall(() => { + process.nextTick(common.mustCall(() => { + const i = setInterval(common.mustCall(() => { + clearInterval(i); + setTimeout(common.mustCall(() => { + fs.stat('this file does not exist', common.mustCall((er, stat) => { + throw new Error('foobar'); + })); + }), 1); + }), 1); + })); + })); +} diff --git a/test/parallel/test-domain-intercept.js b/test/parallel/test-domain-intercept.js new file mode 100644 index 00000000000000..41de22af202973 --- /dev/null +++ b/test/parallel/test-domain-intercept.js @@ -0,0 +1,43 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const domain = require('domain'); + +{ + const d = new domain.Domain(); + + const mustNotCall = common.mustNotCall(); + + d.on('error', common.mustCall((err) => { + assert.strictEqual(err.message, 'foobar'); + assert.strictEqual(err.domain, d); + assert.strictEqual(err.domainEmitter, undefined); + assert.strictEqual(err.domainBound, mustNotCall); + assert.strictEqual(err.domainThrown, false); + })); + + const bound = d.intercept(mustNotCall); + bound(new Error('foobar')); +} + +{ + const d = new domain.Domain(); + + const bound = d.intercept(common.mustCall((data) => { + assert.strictEqual(data, 'data'); + })); + + bound(null, 'data'); +} + +{ + const d = new domain.Domain(); + + const bound = d.intercept(common.mustCall((data, data2) => { + assert.strictEqual(data, 'data'); + assert.strictEqual(data2, 'data2'); + })); + + bound(null, 'data', 'data2'); +} diff --git a/test/parallel/test-domain-multiple-errors.js b/test/parallel/test-domain-multiple-errors.js new file mode 100644 index 00000000000000..5b031eb1b44bc4 --- /dev/null +++ b/test/parallel/test-domain-multiple-errors.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const domain = require('domain'); + +// This test is similar to test-domain-error-types, but uses a single domain +// to emit all errors. + +const d = new domain.Domain(); + +const values = [ + 42, null, undefined, false, () => {}, 'string', Symbol('foo') +]; + +d.on('error', common.mustCall((err) => { + assert(values.includes(err)); +}, values.length)); + +for (const something of values) { + d.run(common.mustCall(() => { + process.nextTick(common.mustCall(() => { + throw something; + })); + })); +} diff --git a/test/parallel/test-domain-nexttick.js b/test/parallel/test-domain-nexttick.js new file mode 100644 index 00000000000000..76cefd519d0bd3 --- /dev/null +++ b/test/parallel/test-domain-nexttick.js @@ -0,0 +1,21 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const domain = require('domain'); + +const d = new domain.Domain(); + +d.on('error', common.mustCall((err) => { + assert.strictEqual(err.message, 'foobar'); + assert.strictEqual(err.domain, d); + assert.strictEqual(err.domainEmitter, undefined); + assert.strictEqual(err.domainBound, undefined); + assert.strictEqual(err.domainThrown, true); +})); + +d.run(common.mustCall(() => { + process.nextTick(common.mustCall(() => { + throw new Error('foobar'); + })); +})); diff --git a/test/parallel/test-domain-run.js b/test/parallel/test-domain-run.js new file mode 100644 index 00000000000000..684d06204a2d9d --- /dev/null +++ b/test/parallel/test-domain-run.js @@ -0,0 +1,13 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const domain = require('domain'); + +const d = new domain.Domain(); + +assert.strictEqual(d.run(() => 'return value'), + 'return value'); + +assert.strictEqual(d.run((a, b) => `${a} ${b}`, 'return', 'value'), + 'return value'); diff --git a/test/parallel/test-domain-timer.js b/test/parallel/test-domain-timer.js new file mode 100644 index 00000000000000..67910e3d5b1934 --- /dev/null +++ b/test/parallel/test-domain-timer.js @@ -0,0 +1,21 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const domain = require('domain'); + +const d = new domain.Domain(); + +d.on('error', common.mustCall((err) => { + assert.strictEqual(err.message, 'foobar'); + assert.strictEqual(err.domain, d); + assert.strictEqual(err.domainEmitter, undefined); + assert.strictEqual(err.domainBound, undefined); + assert.strictEqual(err.domainThrown, true); +})); + +d.run(common.mustCall(() => { + setTimeout(common.mustCall(() => { + throw new Error('foobar'); + }), 1); +})); diff --git a/test/parallel/test-domain.js b/test/parallel/test-domain.js deleted file mode 100644 index 8c5bf08b14bc10..00000000000000 --- a/test/parallel/test-domain.js +++ /dev/null @@ -1,254 +0,0 @@ -'use strict'; -// Simple tests of most basic domain functionality. - -require('../common'); -const assert = require('assert'); -const domain = require('domain'); -const events = require('events'); -const fs = require('fs'); -let caught = 0; -let expectCaught = 0; - -const d = new domain.Domain(); -const e = new events.EventEmitter(); - -d.on('error', function(er) { - console.error('caught', er && (er.message || er)); - - let er_message = er.message; - let er_path = er.path; - - // On windows, error messages can contain full path names. If this is the - // case, remove the directory part. - if (typeof er_path === 'string') { - const slash = er_path.lastIndexOf('\\'); - if (slash !== -1) { - const dir = er_path.slice(0, slash + 1); - er_path = er_path.replace(dir, ''); - er_message = er_message.replace(dir, ''); - } - } - - switch (er_message) { - case 'emitted': - assert.strictEqual(er.domain, d); - assert.strictEqual(er.domainEmitter, e); - assert.strictEqual(er.domainThrown, false); - break; - - case 'bound': - assert.ok(!er.domainEmitter); - assert.strictEqual(er.domain, d); - assert.strictEqual(er.domainBound, fn); - assert.strictEqual(er.domainThrown, false); - break; - - case 'thrown': - assert.ok(!er.domainEmitter); - assert.strictEqual(er.domain, d); - assert.strictEqual(er.domainThrown, true); - break; - - case "ENOENT: no such file or directory, open 'this file does not exist'": - assert.strictEqual(er.domain, d); - assert.strictEqual(er.domainThrown, false); - assert.strictEqual(typeof er.domainBound, 'function'); - assert.ok(!er.domainEmitter); - assert.strictEqual(er.code, 'ENOENT'); - assert.strictEqual(er_path, 'this file does not exist'); - assert.strictEqual(typeof er.errno, 'number'); - break; - - case - "ENOENT: no such file or directory, open 'stream for nonexistent file'": - assert.strictEqual(typeof er.errno, 'number'); - assert.strictEqual(er.code, 'ENOENT'); - assert.strictEqual(er_path, 'stream for nonexistent file'); - assert.strictEqual(er.domain, d); - assert.strictEqual(er.domainEmitter, fst); - assert.ok(!er.domainBound); - assert.strictEqual(er.domainThrown, false); - break; - - case 'implicit': - assert.strictEqual(er.domainEmitter, implicit); - assert.strictEqual(er.domain, d); - assert.strictEqual(er.domainThrown, false); - assert.ok(!er.domainBound); - break; - - case 'implicit timer': - assert.strictEqual(er.domain, d); - assert.strictEqual(er.domainThrown, true); - assert.ok(!er.domainEmitter); - assert.ok(!er.domainBound); - break; - - case 'Cannot read property \'isDirectory\' of undefined': - assert.strictEqual(er.domain, d); - assert.ok(!er.domainEmitter); - assert.ok(!er.domainBound); - break; - - case 'nextTick execution loop': - assert.strictEqual(er.domain, d); - assert.ok(!er.domainEmitter); - assert.ok(!er.domainBound); - break; - - default: - console.error('unexpected error, throwing %j', er.message, er); - throw er; - } - - caught++; -}); - - -process.on('exit', function() { - console.error('exit', caught, expectCaught); - assert.strictEqual(caught, expectCaught, - 'caught the expected number of errors'); - console.log('ok'); -}); - - -// revert to using the domain when a callback is passed to nextTick in -// the middle of a tickCallback loop -d.run(function() { - process.nextTick(function() { - throw new Error('nextTick execution loop'); - }); -}); -expectCaught++; - - -// catch thrown errors no matter how many times we enter the event loop -// this only uses implicit binding, except for the first function -// passed to d.run(). The rest are implicitly bound by virtue of being -// set up while in the scope of the d domain. -d.run(function() { - process.nextTick(function() { - const i = setInterval(function() { - clearInterval(i); - setTimeout(function() { - fs.stat('this file does not exist', function(er, stat) { - // uh oh! stat isn't set! - // pretty common error. - console.log(stat.isDirectory()); - }); - }, 1); - }, 1); - }); -}); -expectCaught++; - - -// implicit addition of a timer created within a domain-bound context. -d.run(function() { - setTimeout(function() { - throw new Error('implicit timer'); - }, 1); -}); -expectCaught++; - - -// Event emitters added to the domain have their errors routed. -d.add(e); -e.emit('error', new Error('emitted')); -expectCaught++; - - -// get rid of the `if (er) return cb(er)` malarky, by intercepting -// the cb functions to the domain, and using the intercepted function -// as a callback instead. -function fn() { - throw new Error('This function should never be called!'); -} - -let bound = d.intercept(fn); -bound(new Error('bound')); -expectCaught++; - - -// intercepted should never pass first argument to callback -function fn2(data) { - assert.strictEqual(data, 'data', 'should not be null err argument'); -} - -bound = d.intercept(fn2); -bound(null, 'data'); - -// intercepted should never pass first argument to callback -// even if arguments length is more than 2. -function fn3(data, data2) { - assert.strictEqual(data, 'data', 'should not be null err argument'); - assert.strictEqual(data2, 'data2', 'should not be data argument'); -} - -bound = d.intercept(fn3); -bound(null, 'data', 'data2'); - -// throwing in a bound fn is also caught, -// even if it's asynchronous, by hitting the -// global uncaughtException handler. This doesn't -// require interception, since throws are always -// caught by the domain. -function thrower() { - throw new Error('thrown'); -} -setTimeout(d.bind(thrower), 100); -expectCaught++; - - -// Pass an intercepted function to an fs operation that fails. -fs.open('this file does not exist', 'r', d.intercept(function(er) { - console.error('should not get here!', er); - throw new Error('should not get here!'); -}, true)); -expectCaught++; - - -// implicit addition by being created within a domain-bound context. -let implicit; - -d.run(function() { - implicit = new events.EventEmitter(); -}); - -setTimeout(function() { - // escape from the domain, but implicit is still bound to it. - implicit.emit('error', new Error('implicit')); -}, 10); -expectCaught++; - - -let result = d.run(function() { - return 'return value'; -}); -assert.strictEqual(result, 'return value'); - - -// check if the executed function take in count the applied parameters -result = d.run((a, b) => `${a} ${b}`, 'return', 'value'); -assert.strictEqual(result, 'return value'); - - -let fst = fs.createReadStream('stream for nonexistent file'); -d.add(fst); -expectCaught++; - -[42, null, undefined, false, () => {}, 'string'].forEach(function(something) { - const d = new domain.Domain(); - d.run(function() { - process.nextTick(function() { - throw something; - }); - expectCaught++; - }); - - d.on('error', function(er) { - assert.strictEqual(something, er); - caught++; - }); -});