1 var common = require('../common');
2 var assert = require('assert');
3 var domain = require('domain');
5 var asyncTest = (function() {
6 var asyncTestsEnabled = false;
7 var asyncTestLastCheck;
8 var asyncTestQueue = [];
10 var currentTest = null;
12 function fail(error) {
13 var stack = currentTest
14 ? error.stack + '\nFrom previous event:\n' + currentTest.stack
18 process.stderr.write('\'' + currentTest.description + '\' failed\n\n');
20 process.stderr.write(stack);
24 function nextAsyncTest() {
27 if (called) return fail(new Error('done called twice'));
29 asyncTestLastCheck = Date.now();
30 if (arguments.length > 0) return fail(err);
31 setTimeout(nextAsyncTest, 10);
34 if (asyncTestQueue.length) {
35 var test = asyncTestQueue.shift();
39 clearInterval(asyncTestHandle);
43 return function asyncTest(description, fn) {
44 var stack = new Error().stack.split('\n').slice(1).join('\n');
48 description: description
50 if (!asyncTestsEnabled) {
51 asyncTestsEnabled = true;
52 asyncTestLastCheck = Date.now();
53 process.on('uncaughtException', fail);
54 asyncTestHandle = setInterval(function() {
56 if (now - asyncTestLastCheck > 10000) {
57 return fail(new Error('Async test timeout exceeded'));
60 setTimeout(nextAsyncTest, 10);
66 function setupException(fn) {
67 var listeners = process.listeners('uncaughtException');
68 process.removeAllListeners('uncaughtException');
69 process.on('uncaughtException', fn);
70 return function clean() {
71 process.removeListener('uncaughtException', fn);
72 listeners.forEach(function(listener) {
73 process.on('uncaughtException', listener);
79 process.removeAllListeners('unhandledRejection');
80 process.removeAllListeners('rejectionHandled');
83 function onUnhandledSucceed(done, predicate) {
85 process.on('unhandledRejection', function(reason, promise) {
87 predicate(reason, promise);
95 function onUnhandledFail(done) {
97 process.on('unhandledRejection', function(reason, promise) {
98 done(new Error('unhandledRejection not supposed to be triggered'));
100 process.on('rejectionHandled', function() {
101 done(new Error('rejectionHandled not supposed to be triggered'));
103 setTimeout(function() {
108 asyncTest('synchronously rejected promise should trigger unhandledRejection', function(done) {
110 onUnhandledSucceed(done, function(reason, promise) {
111 assert.strictEqual(e, reason);
116 asyncTest('synchronously rejected promise should trigger unhandledRejection', function(done) {
118 onUnhandledSucceed(done, function(reason, promise) {
119 assert.strictEqual(e, reason);
121 new Promise(function(_, reject) {
126 asyncTest('Promise rejected after setImmediate should trigger unhandledRejection', function(done) {
128 onUnhandledSucceed(done, function(reason, promise) {
129 assert.strictEqual(e, reason);
131 new Promise(function(_, reject) {
132 setImmediate(function() {
138 asyncTest('Promise rejected after setTimeout(,1) should trigger unhandled rejection', function(done) {
140 onUnhandledSucceed(done, function(reason, promise) {
141 assert.strictEqual(e, reason);
143 new Promise(function(_, reject) {
144 setTimeout(function() {
150 asyncTest('Catching a promise rejection after setImmediate is not soon enough to stop unhandledRejection', function(done) {
152 onUnhandledSucceed(done, function(reason, promise) {
153 assert.strictEqual(e, reason);
156 var promise = new Promise(function(_, reject) {
160 setImmediate(function() {
161 promise.then(assert.fail, function(){});
165 asyncTest('When re-throwing new errors in a promise catch, only the re-thrown error should hit unhandledRejection', function(done) {
167 var e2 = new Error();
168 onUnhandledSucceed(done, function(reason, promise) {
169 assert.strictEqual(e2, reason);
170 assert.strictEqual(promise2, promise);
172 var promise2 = Promise.reject(e).then(assert.fail, function(reason) {
173 assert.strictEqual(e, reason);
178 asyncTest('Test params of unhandledRejection for a synchronously-rejected promise', function(done) {
180 var e2 = new Error();
181 onUnhandledSucceed(done, function(reason, promise) {
182 assert.strictEqual(e, reason);
183 assert.strictEqual(promise, promise);
185 var promise = Promise.reject(e);
188 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) {
190 var e2 = new Error();
191 onUnhandledSucceed(done, function(reason, promise) {
192 assert.strictEqual(e2, reason);
193 assert.strictEqual(promise2, promise);
195 var promise2 = new Promise(function(_, reject) {
196 setTimeout(function() {
199 }).then(assert.fail, function(reason) {
200 assert.strictEqual(e, reason);
205 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) {
207 var e2 = new Error();
208 onUnhandledSucceed(done, function(reason, promise) {
209 assert.strictEqual(e2, reason);
210 assert.strictEqual(promise2, promise);
212 var promise = new Promise(function(_, reject) {
213 setTimeout(function() {
215 process.nextTick(function() {
216 promise2 = promise.then(assert.fail, function(reason) {
217 assert.strictEqual(e, reason);
226 asyncTest('unhandledRejection should not be triggered if a promise catch is attached synchronously upon the promise\'s creation', function(done) {
228 onUnhandledFail(done);
229 Promise.reject(e).then(assert.fail, function(){});
232 asyncTest('unhandledRejection should not be triggered if a promise catch is attached synchronously upon the promise\'s creation', function(done) {
234 onUnhandledFail(done);
235 new Promise(function(_, reject) {
237 }).then(assert.fail, function(){});
240 asyncTest('Attaching a promise catch in a process.nextTick is soon enough to prevent unhandledRejection', function(done) {
242 onUnhandledFail(done);
243 var promise = Promise.reject(e);
244 process.nextTick(function() {
245 promise.then(assert.fail, function(){});
249 asyncTest('Attaching a promise catch in a process.nextTick is soon enough to prevent unhandledRejection', function(done) {
251 onUnhandledFail(done);
252 var promise = new Promise(function(_, reject) {
255 process.nextTick(function() {
256 promise.then(assert.fail, function(){});
260 // State adapation tests
261 asyncTest('catching a promise which is asynchronously rejected (via resolution to an asynchronously-rejected promise) prevents unhandledRejection', function(done) {
263 onUnhandledFail(done);
264 Promise.resolve().then(function() {
265 return new Promise(function(_, reject) {
266 setTimeout(function() {
270 }).then(assert.fail, function(reason) {
271 assert.strictEqual(e, reason);
275 asyncTest('Catching a rejected promise derived from throwing in a fulfillment handler prevents unhandledRejection', function(done) {
277 onUnhandledFail(done);
278 Promise.resolve().then(function() {
280 }).then(assert.fail, function(reason) {
281 assert.strictEqual(e, reason);
285 asyncTest('Catching a rejected promise derived from returning a synchronously-rejected promise in a fulfillment handler prevents unhandledRejection', function(done) {
287 onUnhandledFail(done);
288 Promise.resolve().then(function() {
289 return Promise.reject(e);
290 }).then(assert.fail, function(reason) {
291 assert.strictEqual(e, reason);
295 asyncTest('A rejected promise derived from returning an asynchronously-rejected promise in a fulfillment handler does trigger unhandledRejection', function(done) {
298 onUnhandledSucceed(done, function(reason, promise) {
299 assert.strictEqual(e, reason);
300 assert.strictEqual(_promise, promise);
302 _promise = Promise.resolve().then(function() {
303 return new Promise(function(_, reject) {
304 setTimeout(function() {
311 asyncTest('A rejected promise derived from throwing in a fulfillment handler does trigger unhandledRejection', function(done) {
314 onUnhandledSucceed(done, function(reason, promise) {
315 assert.strictEqual(e, reason);
316 assert.strictEqual(_promise, promise);
318 _promise = Promise.resolve().then(function() {
323 asyncTest('A rejected promise derived from returning a synchronously-rejected promise in a fulfillment handler does trigger unhandledRejection', function(done) {
326 onUnhandledSucceed(done, function(reason, promise) {
327 assert.strictEqual(e, reason);
328 assert.strictEqual(_promise, promise);
330 _promise = Promise.resolve().then(function() {
331 return Promise.reject(e);
335 // Combinations with Promise.all
336 asyncTest('Catching the Promise.all() of a collection that includes a rejected promise prevents unhandledRejection', function(done) {
338 onUnhandledFail(done);
339 Promise.all([Promise.reject(e)]).then(assert.fail, function() {});
342 asyncTest('Catching the Promise.all() of a collection that includes a nextTick-async rejected promise prevents unhandledRejection', function(done) {
344 onUnhandledFail(done);
345 var p = new Promise(function(_, reject) {
346 process.nextTick(function() {
350 p = Promise.all([p]);
351 process.nextTick(function() {
352 p.then(assert.fail, function() {});
356 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) {
358 onUnhandledSucceed(done, function(reason, promise) {
359 assert.strictEqual(e, reason);
360 assert.strictEqual(p, promise);
362 var p = Promise.all([Promise.reject(e)]);
365 asyncTest('Waiting setTimeout(, 10) to catch a promise causes an unhandledRejection + rejectionHandled pair', function(done) {
367 var unhandledPromises = [];
369 process.on('unhandledRejection', function(reason, promise) {
370 assert.strictEqual(e, reason);
371 unhandledPromises.push(promise);
373 process.on('rejectionHandled', function(promise) {
374 assert.strictEqual(1, unhandledPromises.length);
375 assert.strictEqual(unhandledPromises[0], promise);
376 assert.strictEqual(thePromise, promise);
380 var thePromise = new Promise(function() {
383 setTimeout(function() {
384 thePromise.then(assert.fail, function(reason) {
385 assert.strictEqual(e, reason);
390 asyncTest('Waiting for some combination of process.nextTick + promise microtasks to attach a catch handler is still soon enough to prevent unhandledRejection', function(done) {
392 onUnhandledFail(done);
395 var a = Promise.reject(e);
396 process.nextTick(function() {
397 Promise.resolve().then(function() {
398 process.nextTick(function() {
399 Promise.resolve().then(function() {
400 a.catch(function() {});
407 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) {
409 onUnhandledFail(done);
411 setImmediate(function() {
412 var a = Promise.reject(e);
413 process.nextTick(function() {
414 Promise.resolve().then(function() {
415 process.nextTick(function() {
416 Promise.resolve().then(function() {
417 a.catch(function() {});
425 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) {
427 onUnhandledFail(done);
429 setTimeout(function() {
430 var a = Promise.reject(e);
431 process.nextTick(function() {
432 Promise.resolve().then(function() {
433 process.nextTick(function() {
434 Promise.resolve().then(function() {
435 a.catch(function() {});
443 asyncTest('Waiting for some combination of promise microtasks + process.nextTick to attach a catch handler is still soon enough to prevent unhandledRejection', function(done) {
445 onUnhandledFail(done);
448 var a = Promise.reject(e);
449 Promise.resolve().then(function() {
450 process.nextTick(function() {
451 Promise.resolve().then(function() {
452 process.nextTick(function() {
453 a.catch(function() {});
460 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) {
462 onUnhandledFail(done);
464 setImmediate(function() {
465 var a = Promise.reject(e);
466 Promise.resolve().then(function() {
467 process.nextTick(function() {
468 Promise.resolve().then(function() {
469 process.nextTick(function() {
470 a.catch(function() {});
478 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) {
480 onUnhandledFail(done);
482 setTimeout(function() {
483 var a = Promise.reject(e);
484 Promise.resolve().then(function() {
485 process.nextTick(function() {
486 Promise.resolve().then(function() {
487 process.nextTick(function() {
488 a.catch(function() {});
496 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) {
498 onUnhandledSucceed(done, function(reason, promise) {
499 assert.strictEqual(e, reason);
500 assert.strictEqual(p, promise);
502 var p = Promise.reject(e);
503 setImmediate(function() {
504 Promise.resolve().then(function () {
505 p.catch(function(){});
510 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) {
511 onUnhandledSucceed(done, function(reason, promise) {
512 assert.strictEqual(undefined, reason);
513 assert.strictEqual(p, promise);
515 setImmediate(function() {
516 Promise.resolve().then(function () {
517 Promise.resolve().then(function () {
518 Promise.resolve().then(function () {
519 Promise.resolve().then(function () {
520 p.catch(function(){});
526 var p = Promise.reject();
529 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) {
530 onUnhandledSucceed(done, function(reason, promise) {
531 assert.strictEqual(undefined, reason);
532 assert.strictEqual(p, promise);
534 var p = Promise.reject();
535 setImmediate(function() {
536 Promise.resolve().then(function () {
537 Promise.resolve().then(function () {
538 Promise.resolve().then(function () {
539 Promise.resolve().then(function () {
540 p.catch(function(){});
548 asyncTest('Promise unhandledRejection handler does not interfere with domain error handlers being given exceptions thrown from nextTick.', function(done) {
549 var d = domain.create();
550 var domainReceivedError;
551 d.on('error', function(e) {
552 domainReceivedError = e;
555 var e = new Error('error');
556 var domainError = new Error('domain error');
557 onUnhandledSucceed(done, function(reason, promise) {
558 assert.strictEqual(reason, e);
559 assert.strictEqual(domainReceivedError, domainError);
562 var a = Promise.reject(e);
563 process.nextTick(function() {
569 asyncTest('nextTick is immediately scheduled when called inside an event handler', function(done) {
571 var e = new Error('error');
572 process.on('unhandledRejection', function(reason, promise) {
574 process.nextTick(function() {
577 setTimeout(function() {
579 assert.deepEqual([1,2], order);
586 asyncTest('Throwing an error inside a rejectionHandled handler goes to unhandledException, and does not cause .catch() to throw an exception', function(done) {
589 var e2 = new Error();
590 var tearDownException = setupException(function(err) {
591 assert.equal(e2, err);
595 process.on('rejectionHandled', function() {
598 var p = Promise.reject(e);
599 setTimeout(function() {
601 p.catch(function(){});
603 done(new Error('fail'));