mirror of
https://github.com/nodejs/node.git
synced 2024-12-01 16:10:02 +01:00
28e9a022df
Currently there are many instances where assert.fail is directly passed to a callback for error handling. Unfortunately this will swallow the error as it is the third argument of assert.fail that sets the message not the first. This commit adds a new function to test/common.js that simply wraps assert.fail and calls it with the provided message. Tip of the hat to @trott for pointing me in the direction of this. PR-URL: https://github.com/nodejs/node/pull/3453 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
680 lines
20 KiB
JavaScript
680 lines
20 KiB
JavaScript
'use strict';
|
||
var common = require('../common');
|
||
var assert = require('assert');
|
||
var domain = require('domain');
|
||
|
||
var asyncTest = (function() {
|
||
var asyncTestsEnabled = false;
|
||
var asyncTestLastCheck;
|
||
var asyncTestQueue = [];
|
||
var asyncTestHandle;
|
||
var currentTest = null;
|
||
|
||
function fail(error) {
|
||
var stack = currentTest
|
||
? error.stack + '\nFrom previous event:\n' + currentTest.stack
|
||
: error.stack;
|
||
|
||
if (currentTest)
|
||
process.stderr.write('\'' + currentTest.description + '\' failed\n\n');
|
||
|
||
process.stderr.write(stack);
|
||
process.exit(2);
|
||
}
|
||
|
||
function nextAsyncTest() {
|
||
var called = false;
|
||
function done(err) {
|
||
if (called) return fail(new Error('done called twice'));
|
||
called = true;
|
||
asyncTestLastCheck = Date.now();
|
||
if (arguments.length > 0) return fail(err);
|
||
setTimeout(nextAsyncTest, 10);
|
||
}
|
||
|
||
if (asyncTestQueue.length) {
|
||
var test = asyncTestQueue.shift();
|
||
currentTest = test;
|
||
test.action(done);
|
||
} else {
|
||
clearInterval(asyncTestHandle);
|
||
}
|
||
}
|
||
|
||
return function asyncTest(description, fn) {
|
||
var stack = new Error().stack.split('\n').slice(1).join('\n');
|
||
asyncTestQueue.push({
|
||
action: fn,
|
||
stack: stack,
|
||
description: description
|
||
});
|
||
if (!asyncTestsEnabled) {
|
||
asyncTestsEnabled = true;
|
||
asyncTestLastCheck = Date.now();
|
||
process.on('uncaughtException', fail);
|
||
asyncTestHandle = setInterval(function() {
|
||
var now = Date.now();
|
||
if (now - asyncTestLastCheck > 10000) {
|
||
return fail(new Error('Async test timeout exceeded'));
|
||
}
|
||
}, 10);
|
||
setTimeout(nextAsyncTest, 10);
|
||
}
|
||
};
|
||
|
||
})();
|
||
|
||
function setupException(fn) {
|
||
var listeners = process.listeners('uncaughtException');
|
||
process.removeAllListeners('uncaughtException');
|
||
process.on('uncaughtException', fn);
|
||
return function clean() {
|
||
process.removeListener('uncaughtException', fn);
|
||
listeners.forEach(function(listener) {
|
||
process.on('uncaughtException', listener);
|
||
});
|
||
};
|
||
}
|
||
|
||
function clean() {
|
||
process.removeAllListeners('unhandledRejection');
|
||
process.removeAllListeners('rejectionHandled');
|
||
}
|
||
|
||
function onUnhandledSucceed(done, predicate) {
|
||
clean();
|
||
process.on('unhandledRejection', function(reason, promise) {
|
||
try {
|
||
predicate(reason, promise);
|
||
} catch (e) {
|
||
return done(e);
|
||
}
|
||
done();
|
||
});
|
||
}
|
||
|
||
function onUnhandledFail(done) {
|
||
clean();
|
||
process.on('unhandledRejection', function(reason, promise) {
|
||
done(new Error('unhandledRejection not supposed to be triggered'));
|
||
});
|
||
process.on('rejectionHandled', function() {
|
||
done(new Error('rejectionHandled not supposed to be triggered'));
|
||
});
|
||
setTimeout(function() {
|
||
done();
|
||
}, 10);
|
||
}
|
||
|
||
asyncTest('synchronously rejected promise should trigger' +
|
||
' unhandledRejection', function(done) {
|
||
var e = new Error();
|
||
onUnhandledSucceed(done, function(reason, promise) {
|
||
assert.strictEqual(e, reason);
|
||
});
|
||
Promise.reject(e);
|
||
});
|
||
|
||
asyncTest('synchronously rejected promise should trigger' +
|
||
' unhandledRejection', function(done) {
|
||
var e = new Error();
|
||
onUnhandledSucceed(done, function(reason, promise) {
|
||
assert.strictEqual(e, reason);
|
||
});
|
||
new Promise(function(_, reject) {
|
||
reject(e);
|
||
});
|
||
});
|
||
|
||
asyncTest('Promise rejected after setImmediate should trigger' +
|
||
' unhandledRejection', function(done) {
|
||
var e = new Error();
|
||
onUnhandledSucceed(done, function(reason, promise) {
|
||
assert.strictEqual(e, reason);
|
||
});
|
||
new Promise(function(_, reject) {
|
||
setImmediate(function() {
|
||
reject(e);
|
||
});
|
||
});
|
||
});
|
||
|
||
asyncTest('Promise rejected after setTimeout(,1) should trigger' +
|
||
' unhandled rejection', function(done) {
|
||
var e = new Error();
|
||
onUnhandledSucceed(done, function(reason, promise) {
|
||
assert.strictEqual(e, reason);
|
||
});
|
||
new Promise(function(_, reject) {
|
||
setTimeout(function() {
|
||
reject(e);
|
||
}, 1);
|
||
});
|
||
});
|
||
|
||
asyncTest('Catching a promise rejection after setImmediate is not' +
|
||
' soon enough to stop unhandledRejection', function(done) {
|
||
var e = new Error();
|
||
onUnhandledSucceed(done, function(reason, promise) {
|
||
assert.strictEqual(e, reason);
|
||
});
|
||
var _reject;
|
||
var promise = new Promise(function(_, reject) {
|
||
_reject = reject;
|
||
});
|
||
_reject(e);
|
||
setImmediate(function() {
|
||
promise.then(common.fail, function() {});
|
||
});
|
||
});
|
||
|
||
asyncTest('When re-throwing new errors in a promise catch, only the' +
|
||
' re-thrown error should hit unhandledRejection', function(done) {
|
||
var e = new Error();
|
||
var e2 = new Error();
|
||
onUnhandledSucceed(done, function(reason, promise) {
|
||
assert.strictEqual(e2, reason);
|
||
assert.strictEqual(promise2, promise);
|
||
});
|
||
var promise2 = Promise.reject(e).then(common.fail, function(reason) {
|
||
assert.strictEqual(e, reason);
|
||
throw e2;
|
||
});
|
||
});
|
||
|
||
asyncTest('Test params of unhandledRejection for a synchronously-rejected' +
|
||
'promise', function(done) {
|
||
var e = new Error();
|
||
var e2 = new Error();
|
||
onUnhandledSucceed(done, function(reason, promise) {
|
||
assert.strictEqual(e, reason);
|
||
assert.strictEqual(promise, promise);
|
||
});
|
||
var promise = Promise.reject(e);
|
||
});
|
||
|
||
asyncTest('When re-throwing new errors in a promise catch, only the ' +
|
||
're-thrown error should hit unhandledRejection: original promise' +
|
||
' rejected async with setTimeout(,1)', function(done) {
|
||
var e = new Error();
|
||
var e2 = new Error();
|
||
onUnhandledSucceed(done, function(reason, promise) {
|
||
assert.strictEqual(e2, reason);
|
||
assert.strictEqual(promise2, promise);
|
||
});
|
||
var promise2 = new Promise(function(_, reject) {
|
||
setTimeout(function() {
|
||
reject(e);
|
||
}, 1);
|
||
}).then(common.fail, function(reason) {
|
||
assert.strictEqual(e, reason);
|
||
throw e2;
|
||
});
|
||
});
|
||
|
||
asyncTest('When re-throwing new errors in a promise catch, only the re-thrown' +
|
||
' error should hit unhandledRejection: promise catch attached a' +
|
||
' process.nextTick after rejection', function(done) {
|
||
var e = new Error();
|
||
var e2 = new Error();
|
||
onUnhandledSucceed(done, function(reason, promise) {
|
||
assert.strictEqual(e2, reason);
|
||
assert.strictEqual(promise2, promise);
|
||
});
|
||
var promise = new Promise(function(_, reject) {
|
||
setTimeout(function() {
|
||
reject(e);
|
||
process.nextTick(function() {
|
||
promise2 = promise.then(common.fail, function(reason) {
|
||
assert.strictEqual(e, reason);
|
||
throw e2;
|
||
});
|
||
});
|
||
}, 1);
|
||
});
|
||
var promise2;
|
||
});
|
||
|
||
asyncTest('unhandledRejection should not be triggered if a promise catch is' +
|
||
' attached synchronously upon the promise\'s creation',
|
||
function(done) {
|
||
var e = new Error();
|
||
onUnhandledFail(done);
|
||
Promise.reject(e).then(common.fail, function() {});
|
||
});
|
||
|
||
asyncTest('unhandledRejection should not be triggered if a promise catch is' +
|
||
' attached synchronously upon the promise\'s creation',
|
||
function(done) {
|
||
var e = new Error();
|
||
onUnhandledFail(done);
|
||
new Promise(function(_, reject) {
|
||
reject(e);
|
||
}).then(common.fail, function() {});
|
||
});
|
||
|
||
asyncTest('Attaching a promise catch in a process.nextTick is soon enough to' +
|
||
' prevent unhandledRejection', function(done) {
|
||
var e = new Error();
|
||
onUnhandledFail(done);
|
||
var promise = Promise.reject(e);
|
||
process.nextTick(function() {
|
||
promise.then(common.fail, function() {});
|
||
});
|
||
});
|
||
|
||
asyncTest('Attaching a promise catch in a process.nextTick is soon enough to' +
|
||
' prevent unhandledRejection', function(done) {
|
||
var e = new Error();
|
||
onUnhandledFail(done);
|
||
var promise = new Promise(function(_, reject) {
|
||
reject(e);
|
||
});
|
||
process.nextTick(function() {
|
||
promise.then(common.fail, function() {});
|
||
});
|
||
});
|
||
|
||
asyncTest('While inside setImmediate, catching a rejected promise derived ' +
|
||
'from returning a rejected promise in a fulfillment handler ' +
|
||
'prevents unhandledRejection', function(done) {
|
||
onUnhandledFail(done);
|
||
|
||
setImmediate(function() {
|
||
// reproduces on first tick and inside of setImmediate
|
||
Promise
|
||
.resolve('resolve')
|
||
.then(function() {
|
||
return Promise.reject('reject');
|
||
}).catch(function(e) {});
|
||
});
|
||
});
|
||
|
||
// State adapation tests
|
||
asyncTest('catching a promise which is asynchronously rejected (via' +
|
||
'resolution to an asynchronously-rejected promise) prevents' +
|
||
' unhandledRejection', function(done) {
|
||
var e = new Error();
|
||
onUnhandledFail(done);
|
||
Promise.resolve().then(function() {
|
||
return new Promise(function(_, reject) {
|
||
setTimeout(function() {
|
||
reject(e);
|
||
}, 1);
|
||
});
|
||
}).then(common.fail, function(reason) {
|
||
assert.strictEqual(e, reason);
|
||
});
|
||
});
|
||
|
||
asyncTest('Catching a rejected promise derived from throwing in a' +
|
||
' fulfillment handler prevents unhandledRejection', function(done) {
|
||
var e = new Error();
|
||
onUnhandledFail(done);
|
||
Promise.resolve().then(function() {
|
||
throw e;
|
||
}).then(common.fail, function(reason) {
|
||
assert.strictEqual(e, reason);
|
||
});
|
||
});
|
||
|
||
asyncTest('Catching a rejected promise derived from returning a' +
|
||
' synchronously-rejected promise in a fulfillment handler' +
|
||
' prevents unhandledRejection', function(done) {
|
||
var e = new Error();
|
||
onUnhandledFail(done);
|
||
Promise.resolve().then(function() {
|
||
return Promise.reject(e);
|
||
}).then(common.fail, function(reason) {
|
||
assert.strictEqual(e, reason);
|
||
});
|
||
});
|
||
|
||
asyncTest('A rejected promise derived from returning an' +
|
||
' asynchronously-rejected promise in a fulfillment handler' +
|
||
' does trigger unhandledRejection', function(done) {
|
||
var e = new Error();
|
||
var _promise;
|
||
onUnhandledSucceed(done, function(reason, promise) {
|
||
assert.strictEqual(e, reason);
|
||
assert.strictEqual(_promise, promise);
|
||
});
|
||
_promise = Promise.resolve().then(function() {
|
||
return new Promise(function(_, reject) {
|
||
setTimeout(function() {
|
||
reject(e);
|
||
}, 1);
|
||
});
|
||
});
|
||
});
|
||
|
||
asyncTest('A rejected promise derived from throwing in a fulfillment handler' +
|
||
' does trigger unhandledRejection', function(done) {
|
||
var e = new Error();
|
||
var _promise;
|
||
onUnhandledSucceed(done, function(reason, promise) {
|
||
assert.strictEqual(e, reason);
|
||
assert.strictEqual(_promise, promise);
|
||
});
|
||
_promise = Promise.resolve().then(function() {
|
||
throw e;
|
||
});
|
||
});
|
||
|
||
asyncTest('A rejected promise derived from returning a synchronously-rejected' +
|
||
' promise in a fulfillment handler does trigger unhandledRejection',
|
||
function(done) {
|
||
var e = new Error();
|
||
var _promise;
|
||
onUnhandledSucceed(done, function(reason, promise) {
|
||
assert.strictEqual(e, reason);
|
||
assert.strictEqual(_promise, promise);
|
||
});
|
||
_promise = Promise.resolve().then(function() {
|
||
return Promise.reject(e);
|
||
});
|
||
});
|
||
|
||
// Combinations with Promise.all
|
||
asyncTest('Catching the Promise.all() of a collection that includes a' +
|
||
'rejected promise prevents unhandledRejection', function(done) {
|
||
var e = new Error();
|
||
onUnhandledFail(done);
|
||
Promise.all([Promise.reject(e)]).then(common.fail, function() {});
|
||
});
|
||
|
||
asyncTest('Catching the Promise.all() of a collection that includes a ' +
|
||
'nextTick-async rejected promise prevents unhandledRejection',
|
||
function(done) {
|
||
var e = new Error();
|
||
onUnhandledFail(done);
|
||
var p = new Promise(function(_, reject) {
|
||
process.nextTick(function() {
|
||
reject(e);
|
||
});
|
||
});
|
||
p = Promise.all([p]);
|
||
process.nextTick(function() {
|
||
p.then(common.fail, function() {});
|
||
});
|
||
});
|
||
|
||
asyncTest('Failing to catch the Promise.all() of a collection that includes' +
|
||
' a rejected promise triggers unhandledRejection for the returned' +
|
||
' promise, not the passed promise', function(done) {
|
||
var e = new Error();
|
||
onUnhandledSucceed(done, function(reason, promise) {
|
||
assert.strictEqual(e, reason);
|
||
assert.strictEqual(p, promise);
|
||
});
|
||
var p = Promise.all([Promise.reject(e)]);
|
||
});
|
||
|
||
asyncTest('Waiting setTimeout(, 10) to catch a promise causes an' +
|
||
' unhandledRejection + rejectionHandled pair', function(done) {
|
||
clean();
|
||
var unhandledPromises = [];
|
||
var e = new Error();
|
||
process.on('unhandledRejection', function(reason, promise) {
|
||
assert.strictEqual(e, reason);
|
||
unhandledPromises.push(promise);
|
||
});
|
||
process.on('rejectionHandled', function(promise) {
|
||
assert.strictEqual(1, unhandledPromises.length);
|
||
assert.strictEqual(unhandledPromises[0], promise);
|
||
assert.strictEqual(thePromise, promise);
|
||
done();
|
||
});
|
||
|
||
var thePromise = new Promise(function() {
|
||
throw e;
|
||
});
|
||
setTimeout(function() {
|
||
thePromise.then(common.fail, function(reason) {
|
||
assert.strictEqual(e, reason);
|
||
});
|
||
}, 10);
|
||
});
|
||
|
||
asyncTest('Waiting for some combination of process.nextTick + promise' +
|
||
' microtasks to attach a catch handler is still soon enough to' +
|
||
' prevent unhandledRejection', function(done) {
|
||
var e = new Error();
|
||
onUnhandledFail(done);
|
||
|
||
|
||
var a = Promise.reject(e);
|
||
process.nextTick(function() {
|
||
Promise.resolve().then(function() {
|
||
process.nextTick(function() {
|
||
Promise.resolve().then(function() {
|
||
a.catch(function() {});
|
||
});
|
||
});
|
||
});
|
||
});
|
||
});
|
||
|
||
asyncTest('Waiting for some combination of process.nextTick + promise' +
|
||
' microtasks to attach a catch handler is still soon enough to ' +
|
||
'prevent unhandledRejection: inside setImmediate', function(done) {
|
||
var e = new Error();
|
||
onUnhandledFail(done);
|
||
|
||
setImmediate(function() {
|
||
var a = Promise.reject(e);
|
||
process.nextTick(function() {
|
||
Promise.resolve().then(function() {
|
||
process.nextTick(function() {
|
||
Promise.resolve().then(function() {
|
||
a.catch(function() {});
|
||
});
|
||
});
|
||
});
|
||
});
|
||
});
|
||
});
|
||
|
||
asyncTest('Waiting for some combination of process.nextTick + promise ' +
|
||
'microtasks to attach a catch handler is still soon enough to ' +
|
||
'prevent unhandledRejection: inside setTimeout', function(done) {
|
||
var e = new Error();
|
||
onUnhandledFail(done);
|
||
|
||
setTimeout(function() {
|
||
var a = Promise.reject(e);
|
||
process.nextTick(function() {
|
||
Promise.resolve().then(function() {
|
||
process.nextTick(function() {
|
||
Promise.resolve().then(function() {
|
||
a.catch(function() {});
|
||
});
|
||
});
|
||
});
|
||
});
|
||
}, 0);
|
||
});
|
||
|
||
asyncTest('Waiting for some combination of promise microtasks + ' +
|
||
'process.nextTick to attach a catch handler is still soon enough' +
|
||
' to prevent unhandledRejection', function(done) {
|
||
var e = new Error();
|
||
onUnhandledFail(done);
|
||
|
||
|
||
var a = Promise.reject(e);
|
||
Promise.resolve().then(function() {
|
||
process.nextTick(function() {
|
||
Promise.resolve().then(function() {
|
||
process.nextTick(function() {
|
||
a.catch(function() {});
|
||
});
|
||
});
|
||
});
|
||
});
|
||
});
|
||
|
||
asyncTest('Waiting for some combination of promise microtasks +' +
|
||
' process.nextTick to attach a catch handler is still soon enough' +
|
||
' to prevent unhandledRejection: inside setImmediate',
|
||
function(done) {
|
||
var e = new Error();
|
||
onUnhandledFail(done);
|
||
|
||
setImmediate(function() {
|
||
var a = Promise.reject(e);
|
||
Promise.resolve().then(function() {
|
||
process.nextTick(function() {
|
||
Promise.resolve().then(function() {
|
||
process.nextTick(function() {
|
||
a.catch(function() {});
|
||
});
|
||
});
|
||
});
|
||
});
|
||
});
|
||
});
|
||
|
||
asyncTest('Waiting for some combination of promise microtasks +' +
|
||
' process.nextTick to attach a catch handler is still soon enough' +
|
||
' to prevent unhandledRejection: inside setTimeout', function(done) {
|
||
var e = new Error();
|
||
onUnhandledFail(done);
|
||
|
||
setTimeout(function() {
|
||
var a = Promise.reject(e);
|
||
Promise.resolve().then(function() {
|
||
process.nextTick(function() {
|
||
Promise.resolve().then(function() {
|
||
process.nextTick(function() {
|
||
a.catch(function() {});
|
||
});
|
||
});
|
||
});
|
||
});
|
||
}, 0);
|
||
});
|
||
|
||
asyncTest('setImmediate + promise microtasks is too late to attach a catch' +
|
||
' handler; unhandledRejection will be triggered in that case.' +
|
||
' (setImmediate before promise creation/rejection)', function(done) {
|
||
var e = new Error();
|
||
onUnhandledSucceed(done, function(reason, promise) {
|
||
assert.strictEqual(e, reason);
|
||
assert.strictEqual(p, promise);
|
||
});
|
||
var p = Promise.reject(e);
|
||
setImmediate(function() {
|
||
Promise.resolve().then(function() {
|
||
p.catch(function() {});
|
||
});
|
||
});
|
||
});
|
||
|
||
asyncTest('setImmediate + promise microtasks is too late to attach a catch' +
|
||
' handler; unhandledRejection will be triggered in that case' +
|
||
' (setImmediate before promise creation/rejection)', function(done) {
|
||
onUnhandledSucceed(done, function(reason, promise) {
|
||
assert.strictEqual(undefined, reason);
|
||
assert.strictEqual(p, promise);
|
||
});
|
||
setImmediate(function() {
|
||
Promise.resolve().then(function() {
|
||
Promise.resolve().then(function() {
|
||
Promise.resolve().then(function() {
|
||
Promise.resolve().then(function() {
|
||
p.catch(function() {});
|
||
});
|
||
});
|
||
});
|
||
});
|
||
});
|
||
var p = Promise.reject();
|
||
});
|
||
|
||
asyncTest('setImmediate + promise microtasks is too late to attach a catch' +
|
||
' handler; unhandledRejection will be triggered in that case' +
|
||
' (setImmediate after promise creation/rejection)', function(done) {
|
||
onUnhandledSucceed(done, function(reason, promise) {
|
||
assert.strictEqual(undefined, reason);
|
||
assert.strictEqual(p, promise);
|
||
});
|
||
var p = Promise.reject();
|
||
setImmediate(function() {
|
||
Promise.resolve().then(function() {
|
||
Promise.resolve().then(function() {
|
||
Promise.resolve().then(function() {
|
||
Promise.resolve().then(function() {
|
||
p.catch(function() {});
|
||
});
|
||
});
|
||
});
|
||
});
|
||
});
|
||
});
|
||
|
||
asyncTest('Promise unhandledRejection handler does not interfere with domain' +
|
||
' error handlers being given exceptions thrown from nextTick.',
|
||
function(done) {
|
||
var d = domain.create();
|
||
var domainReceivedError;
|
||
d.on('error', function(e) {
|
||
domainReceivedError = e;
|
||
});
|
||
d.run(function() {
|
||
var e = new Error('error');
|
||
var domainError = new Error('domain error');
|
||
onUnhandledSucceed(done, function(reason, promise) {
|
||
assert.strictEqual(reason, e);
|
||
assert.strictEqual(domainReceivedError, domainError);
|
||
d.dispose();
|
||
});
|
||
var a = Promise.reject(e);
|
||
process.nextTick(function() {
|
||
throw domainError;
|
||
});
|
||
});
|
||
});
|
||
|
||
asyncTest('nextTick is immediately scheduled when called inside an event' +
|
||
' handler', function(done) {
|
||
clean();
|
||
var e = new Error('error');
|
||
process.on('unhandledRejection', function(reason, promise) {
|
||
var order = [];
|
||
process.nextTick(function() {
|
||
order.push(1);
|
||
});
|
||
setTimeout(function() {
|
||
order.push(2);
|
||
assert.deepEqual([1, 2], order);
|
||
done();
|
||
}, 1);
|
||
});
|
||
Promise.reject(e);
|
||
});
|
||
|
||
asyncTest('Throwing an error inside a rejectionHandled handler goes to' +
|
||
' unhandledException, and does not cause .catch() to throw an' +
|
||
'exception', function(done) {
|
||
clean();
|
||
var e = new Error();
|
||
var e2 = new Error();
|
||
var tearDownException = setupException(function(err) {
|
||
assert.equal(e2, err);
|
||
tearDownException();
|
||
done();
|
||
});
|
||
process.on('rejectionHandled', function() {
|
||
throw e2;
|
||
});
|
||
var p = Promise.reject(e);
|
||
setTimeout(function() {
|
||
try {
|
||
p.catch(function() {});
|
||
} catch (e) {
|
||
done(new Error('fail'));
|
||
}
|
||
}, 1);
|
||
});
|