├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── es6-wrapped-promise.js ├── glue.js ├── index.js ├── package.json └── test ├── add-remove.tap.js ├── connection-handler-disconnects.tap.js ├── core-asynclistener-error-multiple-handled.simple.js ├── core-asynclistener-error-multiple-mix.simple.js ├── core-asynclistener-error-multiple-unhandled.simple.js ├── core-asynclistener-error-net.simple.js ├── core-asynclistener-error-throw-in-after.simple.js ├── core-asynclistener-error-throw-in-before-multiple.simple.js ├── core-asynclistener-error-throw-in-before.simple.js ├── core-asynclistener-error-throw-in-error.simple.js ├── core-asynclistener-error.simple.js ├── core-asynclistener-nexttick-remove.simple.js ├── core-asynclistener-only-add.simple.js ├── core-asynclistener-remove-before.simple.js ├── core-asynclistener-remove-inflight-error.simple.js ├── core-asynclistener-remove-inflight.simple.js ├── core-asynclistener.simple.js ├── core ├── core-asynclistener-add-inflight.js └── core-asynclistener-error-throw-in-before-inflight.js ├── errors-this-tick.tap.js ├── fork-listen2-problem.tap.js ├── fork-listener.js ├── function-length-preserved.tap.js ├── handle.tap.js ├── http-request.tap.js ├── native-promises.tap.js ├── no-after-following-error.tap.js ├── overlapping-nexttick.tap.js ├── promise-subclass.js ├── simple-counter-with-io.tap.js ├── simple-counter.tap.js ├── simplified-error.simple.js ├── spawn.tap.js ├── timers.tap.js └── zlib.tap.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .travis.yml 2 | test 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | cache: 4 | directories: 5 | - node_modules 6 | 7 | node_js: 8 | - "9" 9 | - "8" 10 | - "7" 11 | - "7.9.0" 12 | - "6" 13 | - "5" 14 | - "4" 15 | - "3" 16 | - "0.12" 17 | - "0.10" 18 | 19 | matrix: 20 | allow_failures: 21 | - node_js: '7' 22 | 23 | sudo: false 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2013-2017, Forrest L Norvell 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![NPM](https://nodei.co/npm/async-listener.png?downloads=true&stars=true)](https://nodei.co/npm/async-listener/) 2 | 3 | [![Build status](https://travis-ci.org/othiym23/async-listener.svg?branch=master)](https://travis-ci.org/othiym23/async-listener) 4 | 5 | # process.addAsyncListener polyfill 6 | 7 | This is an implementation of Trevor Norris's 8 | process.{addAsyncListener,removeAsyncListener} API for adding behavior to async 9 | calls. You can see his implementation (currently a work in progress) on 10 | [Node.js core pull request #6011](https://github.com/joyent/node/pull/6011). 11 | This polyfill / shim is intended for use in versions of Node prior to whatever 12 | version of Node in which Trevor's changes finally land (anticipated at the time 13 | of this writing as 0.11.7). 14 | 15 | Here's his documentation of the intended API, which will probably get cleaned up 16 | here later: 17 | 18 | ## createAsyncListener(callbacks[, initialStorage]) 19 | 20 | * `callbacks` {Object} 21 | * `initialStorage` {Value} 22 | 23 | Returns a constructed `AsyncListener` object. Which can then be passed to 24 | `process.addAsyncListener()` and `process.removeAsyncListener()`. Each 25 | function parameter is as follows: 26 | 27 | 1. `callbacks`: An `Object` which may contain four optional fields: 28 | * `create`: A `function (storage)` that is called when an asynchronous event 29 | is queued. Recives the `storage` attached to the listener. `storage` can be 30 | created by passing an `initialStorage` argument during construction, or by 31 | returning a `Value` from `create` which will be attached to the listener 32 | and overwrite the `initialStorage`. 33 | * `before`: A `function (context, storage)` that is called immediately 34 | before the asynchronous callback is about to run. It will be passed both 35 | the `context` (i.e. `this`) of the calling function and the `storage`. 36 | * `after`: A `function (context, storage)` called immediately after the 37 | asynchronous event's callback is run. Note that if the event's callback 38 | throws during execution this will not be called. 39 | * `error`: A `function (storage, error)` called if the event's callback 40 | threw. If `error` returns `true` then Node will assume the error has been 41 | properly handled and resume execution normally. 42 | 1. `initialStorage`: A `Value` (i.e. anything) that will be, by default, 43 | attached to all new event instances. This will be overwritten if a `Value` 44 | is returned by `create()`. 45 | 46 | 47 | ## addAsyncListener(callbacks[, initialStorage]) 48 | ## addAsyncListener(asyncListener) 49 | 50 | Returns a constructed `AsyncListener` object and immediately adds it to the 51 | listening queue. 52 | 53 | ## removeAsyncListener(asyncListener) 54 | 55 | Removes the `asyncListener` from the listening queue. 56 | -------------------------------------------------------------------------------- /es6-wrapped-promise.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (Promise, ensureAslWrapper) => { 4 | // Updates to this class should also be applied to the the ES3 version 5 | // in index.js. 6 | return class WrappedPromise extends Promise { 7 | constructor(executor) { 8 | var context, args; 9 | super(wrappedExecutor); 10 | var promise = this; 11 | 12 | try { 13 | executor.apply(context, args); 14 | } catch (err) { 15 | args[1](err); 16 | } 17 | 18 | return promise; 19 | function wrappedExecutor(resolve, reject) { 20 | context = this; 21 | args = [wrappedResolve, wrappedReject]; 22 | 23 | // These wrappers create a function that can be passed a function and an argument to 24 | // call as a continuation from the resolve or reject. 25 | function wrappedResolve(val) { 26 | ensureAslWrapper(promise, false); 27 | return resolve(val); 28 | } 29 | 30 | function wrappedReject(val) { 31 | ensureAslWrapper(promise, false); 32 | return reject(val); 33 | } 34 | } 35 | } 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /glue.js: -------------------------------------------------------------------------------- 1 | var wrap = require('shimmer').wrap; 2 | 3 | /* 4 | * 5 | * CONSTANTS 6 | * 7 | */ 8 | var HAS_CREATE_AL = 1 << 0; 9 | var HAS_BEFORE_AL = 1 << 1; 10 | var HAS_AFTER_AL = 1 << 2; 11 | var HAS_ERROR_AL = 1 << 3; 12 | 13 | /** 14 | * There is one list of currently active listeners that is mutated in place by 15 | * addAsyncListener and removeAsyncListener. This complicates error-handling, 16 | * for reasons that are discussed below. 17 | */ 18 | var listeners = []; 19 | 20 | /** 21 | * There can be multiple listeners with the same properties, so disambiguate 22 | * them by assigning them an ID at creation time. 23 | */ 24 | var uid = 0; 25 | 26 | /** 27 | * Ensure that errors coming from within listeners are handed off to domains, 28 | * process._fatalException, or uncaughtException without being treated like 29 | * user errors. 30 | */ 31 | var inAsyncTick = false; 32 | 33 | /** 34 | * Because asynchronous contexts can be nested, and errors can come from anywhere 35 | * in the stack, a little extra work is required to keep track of where in the 36 | * nesting we are. Because JS arrays are frequently mutated in place 37 | */ 38 | var listenerStack = []; 39 | 40 | /** 41 | * The error handler on a listener can capture errors thrown during synchronous 42 | * execution immediately after the listener is added. To capture both 43 | * synchronous and asynchronous errors, the error handler just uses the 44 | * "global" list of active listeners, and the rest of the code ensures that the 45 | * listener list is correct by using a stack of listener lists during 46 | * asynchronous execution. 47 | */ 48 | var asyncCatcher; 49 | 50 | /** 51 | * The guts of the system -- called each time an asynchronous event happens 52 | * while one or more listeners are active. 53 | */ 54 | var asyncWrap; 55 | 56 | /** 57 | * Simple helper function that's probably faster than using Array 58 | * filter methods and can be inlined. 59 | */ 60 | function union(dest, added) { 61 | var destLength = dest.length; 62 | var addedLength = added.length; 63 | var returned = []; 64 | 65 | if (destLength === 0 && addedLength === 0) return returned; 66 | 67 | for (var j = 0; j < destLength; j++) returned[j] = dest[j]; 68 | 69 | if (addedLength === 0) return returned; 70 | 71 | for (var i = 0; i < addedLength; i++) { 72 | var missing = true; 73 | for (j = 0; j < destLength; j++) { 74 | if (dest[j].uid === added[i].uid) { 75 | missing = false; 76 | break; 77 | } 78 | } 79 | if (missing) returned.push(added[i]); 80 | } 81 | 82 | return returned; 83 | } 84 | 85 | /* 86 | * For performance, split error-handlers and asyncCatcher up into two separate 87 | * code paths. 88 | */ 89 | 90 | // 0.9+ 91 | if (process._fatalException) { 92 | /** 93 | * Error handlers on listeners can throw, the catcher needs to be able to 94 | * discriminate between exceptions thrown by user code, and exceptions coming 95 | * from within the catcher itself. Use a global to keep track of which state 96 | * the catcher is currently in. 97 | */ 98 | var inErrorTick = false; 99 | 100 | /** 101 | * Throwing always happens synchronously. If the current array of values for 102 | * the current list of asyncListeners is put in a module-scoped variable right 103 | * before a call that can throw, it will always be correct when the error 104 | * handlers are run. 105 | */ 106 | var errorValues; 107 | 108 | asyncCatcher = function asyncCatcher(er) { 109 | var length = listeners.length; 110 | if (inErrorTick || length === 0) return false; 111 | 112 | var handled = false; 113 | 114 | /* 115 | * error handlers 116 | */ 117 | inErrorTick = true; 118 | for (var i = 0; i < length; ++i) { 119 | var listener = listeners[i]; 120 | if ((listener.flags & HAS_ERROR_AL) === 0) continue; 121 | 122 | var value = errorValues && errorValues[listener.uid]; 123 | handled = listener.error(value, er) || handled; 124 | } 125 | inErrorTick = false; 126 | 127 | /* Test whether there are any listener arrays on the stack. In the case of 128 | * synchronous throws when the listener is active, there may have been 129 | * none pushed yet. 130 | */ 131 | if (listenerStack.length > 0) listeners = listenerStack.pop(); 132 | errorValues = undefined; 133 | 134 | return handled && !inAsyncTick; 135 | }; 136 | 137 | asyncWrap = function asyncWrap(original, list, length) { 138 | var values = []; 139 | 140 | /* 141 | * listeners 142 | */ 143 | inAsyncTick = true; 144 | for (var i = 0; i < length; ++i) { 145 | var listener = list[i]; 146 | values[listener.uid] = listener.data; 147 | 148 | if ((listener.flags & HAS_CREATE_AL) === 0) continue; 149 | 150 | var value = listener.create(listener.data); 151 | if (value !== undefined) values[listener.uid] = value; 152 | } 153 | inAsyncTick = false; 154 | 155 | /* One of the main differences between this polyfill and the core 156 | * asyncListener support is that core avoids creating closures by putting a 157 | * lot of the state managemnt on the C++ side of Node (and of course also it 158 | * bakes support for async listeners into the Node C++ API through the 159 | * AsyncWrap class, which means that it doesn't monkeypatch basically every 160 | * async method like this does). 161 | */ 162 | return function () { 163 | // put the current values where the catcher can see them 164 | errorValues = values; 165 | 166 | /* More than one listener can end up inside these closures, so save the 167 | * current listeners on a stack. 168 | */ 169 | listenerStack.push(listeners); 170 | 171 | /* Activate both the listeners that were active when the closure was 172 | * created and the listeners that were previously active. 173 | */ 174 | listeners = union(list, listeners); 175 | 176 | /* 177 | * before handlers 178 | */ 179 | inAsyncTick = true; 180 | for (var i = 0; i < length; ++i) { 181 | if ((list[i].flags & HAS_BEFORE_AL) > 0) { 182 | list[i].before(this, values[list[i].uid]); 183 | } 184 | } 185 | inAsyncTick = false; 186 | 187 | // save the return value to pass to the after callbacks 188 | var returned = original.apply(this, arguments); 189 | 190 | /* 191 | * after handlers (not run if original throws) 192 | */ 193 | inAsyncTick = true; 194 | for (i = 0; i < length; ++i) { 195 | if ((list[i].flags & HAS_AFTER_AL) > 0) { 196 | list[i].after(this, values[list[i].uid]); 197 | } 198 | } 199 | inAsyncTick = false; 200 | 201 | // back to the previous listener list on the stack 202 | listeners = listenerStack.pop(); 203 | errorValues = undefined; 204 | 205 | return returned; 206 | }; 207 | }; 208 | 209 | wrap(process, '_fatalException', function (_fatalException) { 210 | return function _asyncFatalException(er) { 211 | return asyncCatcher(er) || _fatalException(er); 212 | }; 213 | }); 214 | } 215 | // 0.8 and below 216 | else { 217 | /** 218 | * If an error handler in asyncWrap throws, the process must die. Under 0.8 219 | * and earlier the only way to put a bullet through the head of the process 220 | * is to rethrow from inside the exception handler, so rethrow and set 221 | * errorThrew to tell the uncaughtHandler what to do. 222 | */ 223 | var errorThrew = false; 224 | 225 | /** 226 | * Under Node 0.8, this handler *only* handles synchronously thrown errors. 227 | * This simplifies it, which almost but not quite makes up for the hit taken 228 | * by putting everything in a try-catch. 229 | */ 230 | asyncCatcher = function uncaughtCatcher(er) { 231 | // going down hard 232 | if (errorThrew) throw er; 233 | 234 | var handled = false; 235 | 236 | /* 237 | * error handlers 238 | */ 239 | var length = listeners.length; 240 | for (var i = 0; i < length; ++i) { 241 | var listener = listeners[i]; 242 | if ((listener.flags & HAS_ERROR_AL) === 0) continue; 243 | handled = listener.error(null, er) || handled; 244 | } 245 | 246 | /* Rethrow if one of the before / after handlers fire, which will bring the 247 | * process down immediately. 248 | */ 249 | if (!handled && inAsyncTick) throw er; 250 | }; 251 | 252 | asyncWrap = function asyncWrap(original, list, length) { 253 | var values = []; 254 | 255 | /* 256 | * listeners 257 | */ 258 | inAsyncTick = true; 259 | for (var i = 0; i < length; ++i) { 260 | var listener = list[i]; 261 | values[listener.uid] = listener.data; 262 | 263 | if ((listener.flags & HAS_CREATE_AL) === 0) continue; 264 | 265 | var value = listener.create(listener.data); 266 | if (value !== undefined) values[listener.uid] = value; 267 | } 268 | inAsyncTick = false; 269 | 270 | /* One of the main differences between this polyfill and the core 271 | * asyncListener support is that core avoids creating closures by putting a 272 | * lot of the state managemnt on the C++ side of Node (and of course also it 273 | * bakes support for async listeners into the Node C++ API through the 274 | * AsyncWrap class, which means that it doesn't monkeypatch basically every 275 | * async method like this does). 276 | */ 277 | return function () { 278 | /*jshint maxdepth:4*/ 279 | 280 | // after() handlers don't run if threw 281 | var threw = false; 282 | 283 | // ...unless the error is handled 284 | var handled = false; 285 | 286 | /* More than one listener can end up inside these closures, so save the 287 | * current listeners on a stack. 288 | */ 289 | listenerStack.push(listeners); 290 | 291 | /* Activate both the listeners that were active when the closure was 292 | * created and the listeners that were previously active. 293 | */ 294 | listeners = union(list, listeners); 295 | 296 | /* 297 | * before handlers 298 | */ 299 | inAsyncTick = true; 300 | for (var i = 0; i < length; ++i) { 301 | if ((list[i].flags & HAS_BEFORE_AL) > 0) { 302 | list[i].before(this, values[list[i].uid]); 303 | } 304 | } 305 | inAsyncTick = false; 306 | 307 | // save the return value to pass to the after callbacks 308 | var returned; 309 | try { 310 | returned = original.apply(this, arguments); 311 | } 312 | catch (er) { 313 | threw = true; 314 | for (var i = 0; i < length; ++i) { 315 | if ((listeners[i].flags & HAS_ERROR_AL) == 0) continue; 316 | try { 317 | handled = listeners[i].error(values[list[i].uid], er) || handled; 318 | } 319 | catch (x) { 320 | errorThrew = true; 321 | throw x; 322 | } 323 | } 324 | 325 | if (!handled) { 326 | // having an uncaughtException handler here alters crash semantics 327 | process.removeListener('uncaughtException', asyncCatcher); 328 | process._originalNextTick(function () { 329 | process.addListener('uncaughtException', asyncCatcher); 330 | }); 331 | 332 | throw er; 333 | } 334 | } 335 | finally { 336 | /* 337 | * after handlers (not run if original throws) 338 | */ 339 | if (!threw || handled) { 340 | inAsyncTick = true; 341 | for (i = 0; i < length; ++i) { 342 | if ((list[i].flags & HAS_AFTER_AL) > 0) { 343 | list[i].after(this, values[list[i].uid]); 344 | } 345 | } 346 | inAsyncTick = false; 347 | } 348 | 349 | // back to the previous listener list on the stack 350 | listeners = listenerStack.pop(); 351 | } 352 | 353 | 354 | return returned; 355 | }; 356 | }; 357 | 358 | // will be the first to fire if async-listener is the first module loaded 359 | process.addListener('uncaughtException', asyncCatcher); 360 | } 361 | 362 | // for performance in the case where there are no handlers, just the listener 363 | function simpleWrap(original, list, length) { 364 | inAsyncTick = true; 365 | for (var i = 0; i < length; ++i) { 366 | var listener = list[i]; 367 | if (listener.create) listener.create(listener.data); 368 | } 369 | inAsyncTick = false; 370 | 371 | // still need to make sure nested async calls are made in the context 372 | // of the listeners active at their creation 373 | return function () { 374 | listenerStack.push(listeners); 375 | listeners = union(list, listeners); 376 | 377 | var returned = original.apply(this, arguments); 378 | 379 | listeners = listenerStack.pop(); 380 | 381 | return returned; 382 | }; 383 | } 384 | 385 | /** 386 | * Called each time an asynchronous function that's been monkeypatched in 387 | * index.js is called. If there are no listeners, return the function 388 | * unwrapped. If there are any asyncListeners and any of them have callbacks, 389 | * pass them off to asyncWrap for later use, otherwise just call the listener. 390 | */ 391 | function wrapCallback(original) { 392 | var length = listeners.length; 393 | 394 | // no context to capture, so avoid closure creation 395 | if (length === 0) return original; 396 | 397 | // capture the active listeners as of when the wrapped function was called 398 | var list = listeners.slice(); 399 | 400 | for (var i = 0; i < length; ++i) { 401 | if (list[i].flags > 0) return asyncWrap(original, list, length); 402 | } 403 | 404 | return simpleWrap(original, list, length); 405 | } 406 | 407 | function AsyncListener(callbacks, data) { 408 | if (typeof callbacks.create === 'function') { 409 | this.create = callbacks.create; 410 | this.flags |= HAS_CREATE_AL; 411 | } 412 | 413 | if (typeof callbacks.before === 'function') { 414 | this.before = callbacks.before; 415 | this.flags |= HAS_BEFORE_AL; 416 | } 417 | 418 | if (typeof callbacks.after === 'function') { 419 | this.after = callbacks.after; 420 | this.flags |= HAS_AFTER_AL; 421 | } 422 | 423 | if (typeof callbacks.error === 'function') { 424 | this.error = callbacks.error; 425 | this.flags |= HAS_ERROR_AL; 426 | } 427 | 428 | this.uid = ++uid; 429 | this.data = data === undefined ? null : data; 430 | } 431 | AsyncListener.prototype.create = undefined; 432 | AsyncListener.prototype.before = undefined; 433 | AsyncListener.prototype.after = undefined; 434 | AsyncListener.prototype.error = undefined; 435 | AsyncListener.prototype.data = undefined; 436 | AsyncListener.prototype.uid = 0; 437 | AsyncListener.prototype.flags = 0; 438 | 439 | function createAsyncListener(callbacks, data) { 440 | if (typeof callbacks !== 'object' || !callbacks) { 441 | throw new TypeError('callbacks argument must be an object'); 442 | } 443 | 444 | if (callbacks instanceof AsyncListener) { 445 | return callbacks; 446 | } 447 | else { 448 | return new AsyncListener(callbacks, data); 449 | } 450 | } 451 | 452 | function addAsyncListener(callbacks, data) { 453 | var listener; 454 | if (!(callbacks instanceof AsyncListener)) { 455 | listener = createAsyncListener(callbacks, data); 456 | } 457 | else { 458 | listener = callbacks; 459 | } 460 | 461 | // Make sure the listener isn't already in the list. 462 | var registered = false; 463 | for (var i = 0; i < listeners.length; i++) { 464 | if (listener === listeners[i]) { 465 | registered = true; 466 | break; 467 | } 468 | } 469 | 470 | if (!registered) listeners.push(listener); 471 | 472 | return listener; 473 | } 474 | 475 | function removeAsyncListener(listener) { 476 | for (var i = 0; i < listeners.length; i++) { 477 | if (listener === listeners[i]) { 478 | listeners.splice(i, 1); 479 | break; 480 | } 481 | } 482 | } 483 | 484 | process.createAsyncListener = createAsyncListener; 485 | process.addAsyncListener = addAsyncListener; 486 | process.removeAsyncListener = removeAsyncListener; 487 | 488 | module.exports = wrapCallback; 489 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (process.addAsyncListener) throw new Error("Don't require polyfill unless needed"); 4 | 5 | var shimmer = require('shimmer') 6 | , semver = require('semver') 7 | , wrap = shimmer.wrap 8 | , massWrap = shimmer.massWrap 9 | , wrapCallback = require('./glue.js') 10 | , util = require('util') 11 | ; 12 | 13 | var v6plus = semver.gte(process.version, '6.0.0'); 14 | var v7plus = semver.gte(process.version, '7.0.0'); 15 | var v8plus = semver.gte(process.version, '8.0.0'); 16 | var v11plus = semver.gte(process.version, '11.0.0'); 17 | 18 | var net = require('net'); 19 | 20 | // From Node.js v7.0.0, net._normalizeConnectArgs have been renamed net._normalizeArgs 21 | if (v7plus && !net._normalizeArgs) { 22 | // a polyfill in our polyfill etc so forth -- taken from node master on 2017/03/09 23 | net._normalizeArgs = function (args) { 24 | if (args.length === 0) { 25 | return [{}, null]; 26 | } 27 | 28 | var arg0 = args[0]; 29 | var options = {}; 30 | if (typeof arg0 === 'object' && arg0 !== null) { 31 | // (options[...][, cb]) 32 | options = arg0; 33 | } else if (isPipeName(arg0)) { 34 | // (path[...][, cb]) 35 | options.path = arg0; 36 | } else { 37 | // ([port][, host][...][, cb]) 38 | options.port = arg0; 39 | if (args.length > 1 && typeof args[1] === 'string') { 40 | options.host = args[1]; 41 | } 42 | } 43 | 44 | var cb = args[args.length - 1]; 45 | if (typeof cb !== 'function') 46 | return [options, null]; 47 | else 48 | return [options, cb]; 49 | } 50 | } else if (!v7plus && !net._normalizeConnectArgs) { 51 | // a polyfill in our polyfill etc so forth -- taken from node master on 2013/10/30 52 | net._normalizeConnectArgs = function (args) { 53 | var options = {}; 54 | 55 | function toNumber(x) { return (x = Number(x)) >= 0 ? x : false; } 56 | 57 | if (typeof args[0] === 'object' && args[0] !== null) { 58 | // connect(options, [cb]) 59 | options = args[0]; 60 | } 61 | else if (typeof args[0] === 'string' && toNumber(args[0]) === false) { 62 | // connect(path, [cb]); 63 | options.path = args[0]; 64 | } 65 | else { 66 | // connect(port, [host], [cb]) 67 | options.port = args[0]; 68 | if (typeof args[1] === 'string') { 69 | options.host = args[1]; 70 | } 71 | } 72 | 73 | var cb = args[args.length - 1]; 74 | return typeof cb === 'function' ? [options, cb] : [options]; 75 | }; 76 | } 77 | 78 | // In https://github.com/nodejs/node/pull/11796 `_listen2` was renamed 79 | // `_setUpListenHandle`. It's still aliased as `_listen2`, and currently the 80 | // Node internals still call the alias - but who knows for how long. So better 81 | // make sure we use the new name instead if available. 82 | if ('_setUpListenHandle' in net.Server.prototype) { 83 | wrap(net.Server.prototype, '_setUpListenHandle', wrapSetUpListenHandle); 84 | } else { 85 | wrap(net.Server.prototype, '_listen2', wrapSetUpListenHandle); 86 | } 87 | 88 | function wrapSetUpListenHandle(original) { 89 | return function () { 90 | this.on('connection', function (socket) { 91 | if (socket._handle) { 92 | socket._handle.onread = wrapCallback(socket._handle.onread); 93 | } 94 | }); 95 | 96 | try { 97 | return original.apply(this, arguments); 98 | } 99 | finally { 100 | // the handle will only not be set in cases where there has been an error 101 | if (this._handle && this._handle.onconnection) { 102 | this._handle.onconnection = wrapCallback(this._handle.onconnection); 103 | } 104 | } 105 | }; 106 | } 107 | 108 | function patchOnRead(ctx) { 109 | if (ctx && ctx._handle) { 110 | var handle = ctx._handle; 111 | if (!handle._originalOnread) { 112 | handle._originalOnread = handle.onread; 113 | } 114 | handle.onread = wrapCallback(handle._originalOnread); 115 | } 116 | } 117 | 118 | wrap(net.Socket.prototype, 'connect', function (original) { 119 | return function () { 120 | var args; 121 | // Node core uses an internal Symbol here to guard against the edge-case 122 | // where the user accidentally passes in an array. As we don't have access 123 | // to this Symbol we resort to this hack where we just detect if there is a 124 | // symbol or not. Checking for the number of Symbols is by no means a fool 125 | // proof solution, but it catches the most basic cases. 126 | if (v8plus && 127 | Array.isArray(arguments[0]) && 128 | Object.getOwnPropertySymbols(arguments[0]).length > 0) { 129 | // already normalized 130 | args = arguments[0]; 131 | } else { 132 | // From Node.js v7.0.0, net._normalizeConnectArgs have been renamed net._normalizeArgs 133 | args = v7plus 134 | ? net._normalizeArgs(arguments) 135 | : net._normalizeConnectArgs(arguments); 136 | } 137 | if (args[1]) args[1] = wrapCallback(args[1]); 138 | var result = original.apply(this, args); 139 | patchOnRead(this); 140 | return result; 141 | }; 142 | }); 143 | 144 | var http = require('http'); 145 | 146 | // NOTE: A rewrite occurred in 0.11 that changed the addRequest signature 147 | // from (req, host, port, localAddress) to (req, options) 148 | // Here, I use the longer signature to maintain 0.10 support, even though 149 | // the rest of the arguments aren't actually used 150 | wrap(http.Agent.prototype, 'addRequest', function (original) { 151 | return function (req) { 152 | var onSocket = req.onSocket; 153 | req.onSocket = wrapCallback(function (socket) { 154 | patchOnRead(socket); 155 | return onSocket.apply(this, arguments); 156 | }); 157 | return original.apply(this, arguments); 158 | }; 159 | }); 160 | 161 | var childProcess = require('child_process'); 162 | 163 | function wrapChildProcess(child) { 164 | if (Array.isArray(child.stdio)) { 165 | child.stdio.forEach(function (socket) { 166 | if (socket && socket._handle) { 167 | socket._handle.onread = wrapCallback(socket._handle.onread); 168 | wrap(socket._handle, 'close', activatorFirst); 169 | } 170 | }); 171 | } 172 | 173 | if (child._handle) { 174 | child._handle.onexit = wrapCallback(child._handle.onexit); 175 | } 176 | } 177 | 178 | // iojs v2.0.0+ 179 | if (childProcess.ChildProcess) { 180 | wrap(childProcess.ChildProcess.prototype, 'spawn', function (original) { 181 | return function () { 182 | var result = original.apply(this, arguments); 183 | wrapChildProcess(this); 184 | return result; 185 | }; 186 | }); 187 | } else { 188 | massWrap(childProcess, [ 189 | 'execFile', // exec is implemented in terms of execFile 190 | 'fork', 191 | 'spawn' 192 | ], function (original) { 193 | return function () { 194 | var result = original.apply(this, arguments); 195 | wrapChildProcess(result); 196 | return result; 197 | }; 198 | }); 199 | } 200 | 201 | // need unwrapped nextTick for use within < 0.9 async error handling 202 | if (!process._fatalException) { 203 | process._originalNextTick = process.nextTick; 204 | } 205 | 206 | var processors = []; 207 | if (process._nextDomainTick) processors.push('_nextDomainTick'); 208 | if (process._tickDomainCallback) processors.push('_tickDomainCallback'); 209 | 210 | massWrap( 211 | process, 212 | processors, 213 | activator 214 | ); 215 | wrap(process, 'nextTick', activatorFirst); 216 | 217 | var asynchronizers = [ 218 | 'setTimeout', 219 | 'setInterval' 220 | ]; 221 | if (global.setImmediate) asynchronizers.push('setImmediate'); 222 | 223 | var timers = require('timers'); 224 | var patchGlobalTimers = global.setTimeout === timers.setTimeout; 225 | 226 | massWrap( 227 | timers, 228 | asynchronizers, 229 | activatorFirst 230 | ); 231 | 232 | if (patchGlobalTimers) { 233 | massWrap( 234 | global, 235 | asynchronizers, 236 | activatorFirst 237 | ); 238 | } 239 | 240 | var dns = require('dns'); 241 | massWrap( 242 | dns, 243 | [ 244 | 'lookup', 245 | 'resolve', 246 | 'resolve4', 247 | 'resolve6', 248 | 'resolveCname', 249 | 'resolveMx', 250 | 'resolveNs', 251 | 'resolveTxt', 252 | 'resolveSrv', 253 | 'reverse' 254 | ], 255 | activator 256 | ); 257 | 258 | if (dns.resolveNaptr) wrap(dns, 'resolveNaptr', activator); 259 | 260 | var fs = require('fs'); 261 | massWrap( 262 | fs, 263 | [ 264 | 'watch', 265 | 'rename', 266 | 'truncate', 267 | 'chown', 268 | 'fchown', 269 | 'chmod', 270 | 'fchmod', 271 | 'stat', 272 | 'lstat', 273 | 'fstat', 274 | 'link', 275 | 'symlink', 276 | 'readlink', 277 | 'realpath', 278 | 'unlink', 279 | 'rmdir', 280 | 'mkdir', 281 | 'readdir', 282 | 'close', 283 | 'open', 284 | 'utimes', 285 | 'futimes', 286 | 'fsync', 287 | 'write', 288 | 'read', 289 | 'readFile', 290 | 'writeFile', 291 | 'appendFile', 292 | 'watchFile', 293 | 'unwatchFile', 294 | "exists", 295 | ], 296 | activator 297 | ); 298 | 299 | // only wrap lchown and lchmod on systems that have them. 300 | if (fs.lchown) wrap(fs, 'lchown', activator); 301 | if (fs.lchmod) wrap(fs, 'lchmod', activator); 302 | 303 | // only wrap ftruncate in versions of node that have it 304 | if (fs.ftruncate) wrap(fs, 'ftruncate', activator); 305 | 306 | // Wrap zlib streams 307 | var zlib; 308 | try { zlib = require('zlib'); } catch (err) { } 309 | if (zlib && zlib.Deflate && zlib.Deflate.prototype) { 310 | var proto = Object.getPrototypeOf(zlib.Deflate.prototype); 311 | if (proto._transform) { 312 | // streams2 313 | wrap(proto, "_transform", activator); 314 | } 315 | else if (proto.write && proto.flush && proto.end) { 316 | // plain ol' streams 317 | massWrap( 318 | proto, 319 | [ 320 | 'write', 321 | 'flush', 322 | 'end' 323 | ], 324 | activator 325 | ); 326 | } 327 | } 328 | 329 | // Wrap Crypto 330 | var crypto; 331 | try { crypto = require('crypto'); } catch (err) { } 332 | if (crypto) { 333 | 334 | var toWrap = [ 335 | 'pbkdf2', 336 | 'randomBytes', 337 | ]; 338 | if (!v11plus) { 339 | toWrap.push('pseudoRandomBytes'); 340 | } 341 | 342 | massWrap(crypto, toWrap, activator); 343 | } 344 | 345 | // It is unlikely that any userspace promise implementations have a native 346 | // implementation of both Promise and Promise.toString. 347 | var instrumentPromise = !!global.Promise && 348 | Promise.toString() === 'function Promise() { [native code] }' && 349 | Promise.toString.toString() === 'function toString() { [native code] }'; 350 | 351 | // Check that global Promise is native 352 | if (instrumentPromise) { 353 | // shoult not use any methods that have already been wrapped 354 | var promiseListener = process.addAsyncListener({ 355 | create: function create() { 356 | instrumentPromise = false; 357 | } 358 | }); 359 | 360 | // should not resolve synchronously 361 | global.Promise.resolve(true).then(function notSync() { 362 | instrumentPromise = false; 363 | }); 364 | 365 | process.removeAsyncListener(promiseListener); 366 | } 367 | 368 | /* 369 | * Native promises use the microtask queue to make all callbacks run 370 | * asynchronously to avoid Zalgo issues. Since the microtask queue is not 371 | * exposed externally, promises need to be modified in a fairly invasive and 372 | * complex way. 373 | * 374 | * The async boundary in promises that must be patched is between the 375 | * fulfillment of the promise and the execution of any callback that is waiting 376 | * for that fulfillment to happen. This means that we need to trigger a create 377 | * when resolve or reject is called and trigger before, after and error handlers 378 | * around the callback execution. There may be multiple callbacks for each 379 | * fulfilled promise, so handlers will behave similar to setInterval where 380 | * there may be multiple before after and error calls for each create call. 381 | * 382 | * async-listener monkeypatching has one basic entry point: `wrapCallback`. 383 | * `wrapCallback` should be called when create should be triggered and be 384 | * passed a function to wrap, which will execute the body of the async work. 385 | * The resolve and reject calls can be modified fairly easily to call 386 | * `wrapCallback`, but at the time of resolve and reject all the work to be done 387 | * on fulfillment may not be defined, since a call to then, chain or fetch can 388 | * be made even after the promise has been fulfilled. To get around this, we 389 | * create a placeholder function which will call a function passed into it, 390 | * since the call to the main work is being made from within the wrapped 391 | * function, async-listener will work correctly. 392 | * 393 | * There is another complication with monkeypatching Promises. Calls to then, 394 | * chain and catch each create new Promises that are fulfilled internally in 395 | * different ways depending on the return value of the callback. When the 396 | * callback return a Promise, the new Promise is resolved asynchronously after 397 | * the returned Promise has been also been resolved. When something other than 398 | * a promise is resolved the resolve call for the new Promise is put in the 399 | * microtask queue and asynchronously resolved. 400 | * 401 | * Then must be wrapped so that its returned promise has a wrapper that can be 402 | * used to invoke further continuations. This wrapper cannot be created until 403 | * after the callback has run, since the callback may return either a promise 404 | * or another value. Fortunately we already have a wrapper function around the 405 | * callback we can use (the wrapper created by resolve or reject). 406 | * 407 | * By adding an additional argument to this wrapper, we can pass in the 408 | * returned promise so it can have its own wrapper appended. the wrapper 409 | * function can the call the callback, and take action based on the return 410 | * value. If a promise is returned, the new Promise can proxy the returned 411 | * Promise's wrapper (this wrapper may not exist yet, but will by the time the 412 | * wrapper needs to be invoked). Otherwise, a new wrapper can be create the 413 | * same way as in resolve and reject. Since this wrapper is created 414 | * synchronously within another wrapper, it will properly appear as a 415 | * continuation from within the callback. 416 | */ 417 | 418 | if (instrumentPromise) { 419 | wrapPromise(); 420 | } 421 | 422 | function wrapPromise() { 423 | var Promise = global.Promise; 424 | 425 | // Updates to this class should also be applied to the the ES6 version 426 | // in es6-wrapped-promise.js. 427 | function wrappedPromise(executor) { 428 | if (!(this instanceof wrappedPromise)) { 429 | return Promise(executor); 430 | } 431 | 432 | if (typeof executor !== 'function') { 433 | return new Promise(executor); 434 | } 435 | 436 | var context, args; 437 | var promise = new Promise(wrappedExecutor); 438 | promise.__proto__ = wrappedPromise.prototype; 439 | 440 | try { 441 | executor.apply(context, args); 442 | } catch (err) { 443 | args[1](err); 444 | } 445 | 446 | return promise; 447 | 448 | function wrappedExecutor(resolve, reject) { 449 | context = this; 450 | args = [wrappedResolve, wrappedReject]; 451 | 452 | // These wrappers create a function that can be passed a function and an argument to 453 | // call as a continuation from the resolve or reject. 454 | function wrappedResolve(val) { 455 | ensureAslWrapper(promise, false); 456 | return resolve(val); 457 | } 458 | 459 | function wrappedReject(val) { 460 | ensureAslWrapper(promise, false); 461 | return reject(val); 462 | } 463 | } 464 | } 465 | 466 | util.inherits(wrappedPromise, Promise); 467 | 468 | wrap(Promise.prototype, 'then', wrapThen); 469 | // Node.js = 0 ? x : false; 669 | } 670 | 671 | // taken from node master on 2017/03/09 672 | function isPipeName(s) { 673 | return typeof s === 'string' && toNumber(s) === false; 674 | } 675 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "async-listener", 3 | "version": "0.6.10", 4 | "description": "Polyfill exporting trevnorris's 0.11+ asyncListener API.", 5 | "author": "Forrest L Norvell ", 6 | "contributors": [ 7 | "Tim Caswell ", 8 | "Forrest L Norvell " 9 | ], 10 | "main": "index.js", 11 | "scripts": { 12 | "test": "tap test/*.tap.js" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/othiym23/async-listener.git" 17 | }, 18 | "keywords": [ 19 | "polyfill", 20 | "shim", 21 | "zesty", 22 | "crazed", 23 | "experimental" 24 | ], 25 | "license": "BSD-2-Clause", 26 | "bugs": { 27 | "url": "https://github.com/othiym23/async-listener/issues" 28 | }, 29 | "engines": { 30 | "node": "<=0.11.8 || >0.11.10" 31 | }, 32 | "dependencies": { 33 | "semver": "^5.3.0", 34 | "shimmer": "^1.1.0" 35 | }, 36 | "devDependencies": { 37 | "tap": "^0.7.1" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/add-remove.tap.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var test = require('tap').test; 4 | 5 | test("async listener lifecycle", function (t) { 6 | t.plan(8); 7 | 8 | if (!process.addAsyncListener) require('../index.js'); 9 | 10 | t.ok(process.createAsyncListener, "can create async listeners"); 11 | var counted = 0; 12 | var listener = process.createAsyncListener( 13 | { 14 | create : function () { counted++; }, 15 | before : function () {}, 16 | after : function () {}, 17 | error : function () {} 18 | }, 19 | Object.create(null) 20 | ); 21 | 22 | t.ok(process.addAsyncListener, "can add async listeners"); 23 | t.doesNotThrow(function () { 24 | listener = process.addAsyncListener(listener); 25 | }, "adding does not throw"); 26 | 27 | t.ok(listener, "have a listener we can later remove"); 28 | 29 | t.ok(process.removeAsyncListener, "can remove async listeners"); 30 | t.doesNotThrow(function () { 31 | process.removeAsyncListener(listener); 32 | }, "removing does not throw"); 33 | 34 | t.doesNotThrow(function () { 35 | process.removeAsyncListener(listener); 36 | }, "failing remove does not throw"); 37 | 38 | t.equal(counted, 0, "didn't hit any async functions"); 39 | }); 40 | -------------------------------------------------------------------------------- /test/connection-handler-disconnects.tap.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var net = require('net'); 4 | var test = require('tap').test; 5 | if (!process.addAsyncListener) require('../index.js'); 6 | 7 | var PORT = 12346; 8 | 9 | test("another connection handler disconnects server", function (t) { 10 | t.plan(7); 11 | 12 | var client; 13 | 14 | // This tests that we don't crash when another connection listener 15 | // destroys the socket handle before we try to wrap 16 | // socket._handle.onread . 17 | // In this case, the connection handler declared below will run first, 18 | // because the wrapping event handler doesn't get added until 19 | // the server listens below. 20 | 21 | var server = net.createServer(function() {}); 22 | server.on( 23 | 'connection', 24 | function (socket) { 25 | t.ok(true, 'Reached second connection event'); 26 | socket.destroy(); 27 | t.ok(! socket._handle, 'Destroy removed the socket handle'); 28 | } 29 | ); 30 | 31 | server.on('error', function (err) { 32 | t.fail(true, 'It should not produce errors'); 33 | }); 34 | 35 | server.on( 36 | 'listening', 37 | function () { 38 | t.ok(true, 'Server listened ok'); 39 | 40 | // This will run both 'connection' handlers, with the one above 41 | // running first. 42 | // This should succeed even though the socket is destroyed. 43 | client = net.connect(PORT); 44 | client.on( 45 | 'connect', 46 | function () { 47 | t.ok(true, 'connected ok'); 48 | } 49 | ); 50 | 51 | client.on( 52 | 'close', 53 | function () { 54 | t.ok(true, 'disconnected ok'); 55 | t.ok( 56 | !client._handle, 57 | 'close removed the client handle' 58 | ); 59 | 60 | server.close(function () { 61 | t.ok( 62 | !server._handle, 63 | 'Destroy removed the server handle' 64 | ); 65 | }); 66 | } 67 | ) 68 | } 69 | ); 70 | 71 | // Another 'connection' handler is registered during this call. 72 | server.listen(PORT); 73 | 74 | }); 75 | -------------------------------------------------------------------------------- /test/core-asynclistener-error-multiple-handled.simple.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 23 | if (!process.addAsyncListener) require('../index.js'); 24 | if (!global.setImmediate) global.setImmediate = setTimeout; 25 | 26 | var assert = require('assert'); 27 | 28 | var active = null; 29 | var cntr = 0; 30 | 31 | function onAsync0() { 32 | return 0; 33 | } 34 | 35 | function onAsync1() { 36 | return 1; 37 | } 38 | 39 | function onError(stor) { 40 | results.push(stor); 41 | return true; 42 | } 43 | 44 | var results = []; 45 | var asyncNoHandleError0 = { 46 | create: onAsync0, 47 | error: onError 48 | }; 49 | var asyncNoHandleError1 = { 50 | create: onAsync1, 51 | error: onError 52 | }; 53 | 54 | var listeners = [ 55 | process.addAsyncListener(asyncNoHandleError0), 56 | process.addAsyncListener(asyncNoHandleError1) 57 | ]; 58 | 59 | process.nextTick(function() { 60 | throw new Error(); 61 | }); 62 | 63 | process.removeAsyncListener(listeners[0]); 64 | process.removeAsyncListener(listeners[1]); 65 | 66 | process.on('exit', function(code) { 67 | // If the exit code isn't ok then return early to throw the stack that 68 | // caused the bad return code. 69 | if (code !== 0) 70 | return; 71 | 72 | // Handling of errors should propagate to all listeners. 73 | assert.equal(results[0], 0); 74 | assert.equal(results[1], 1); 75 | assert.equal(results.length, 2); 76 | 77 | console.log('ok'); 78 | }); 79 | -------------------------------------------------------------------------------- /test/core-asynclistener-error-multiple-mix.simple.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 23 | if (!process.addAsyncListener) require('../index.js'); 24 | if (!global.setImmediate) global.setImmediate = setTimeout; 25 | 26 | var assert = require('assert'); 27 | 28 | var results = []; 29 | var asyncNoHandleError = { 30 | error: function(stor) { 31 | results.push(1); 32 | } 33 | }; 34 | 35 | var asyncHandleError = { 36 | error: function(stor) { 37 | results.push(0); 38 | return true; 39 | } 40 | }; 41 | 42 | var listeners = [ 43 | process.addAsyncListener(asyncHandleError), 44 | process.addAsyncListener(asyncNoHandleError) 45 | ]; 46 | 47 | // Even if an error handler returns true, both should fire. 48 | process.nextTick(function() { 49 | throw new Error(); 50 | }); 51 | 52 | process.removeAsyncListener(listeners[0]); 53 | process.removeAsyncListener(listeners[1]); 54 | 55 | process.on('exit', function(code) { 56 | // If the exit code isn't ok then return early to throw the stack that 57 | // caused the bad return code. 58 | if (code !== 0) 59 | return; 60 | 61 | // Mixed handling of errors should propagate to all listeners. 62 | assert.equal(results[0], 0); 63 | assert.equal(results[1], 1); 64 | assert.equal(results.length, 2); 65 | 66 | console.log('ok'); 67 | }); 68 | -------------------------------------------------------------------------------- /test/core-asynclistener-error-multiple-unhandled.simple.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 23 | if (!process.addAsyncListener) require('../index.js'); 24 | 25 | var assert = require('assert'); 26 | 27 | function onAsync0() { 28 | return 0; 29 | } 30 | 31 | function onAsync1() { 32 | return 1; 33 | } 34 | 35 | function onError(stor) { 36 | results.push(stor); 37 | } 38 | 39 | var results = []; 40 | var asyncNoHandleError0 = { 41 | create: onAsync0, 42 | error: onError 43 | }; 44 | var asyncNoHandleError1 = { 45 | create: onAsync1, 46 | error: onError 47 | }; 48 | 49 | var listeners = [ 50 | process.addAsyncListener(asyncNoHandleError0), 51 | process.addAsyncListener(asyncNoHandleError1) 52 | ]; 53 | 54 | var uncaughtFired = false; 55 | process.on('uncaughtException', function() { 56 | uncaughtFired = true; 57 | 58 | // Unhandled errors should propagate to all listeners. 59 | assert.equal(results[0], 0); 60 | assert.equal(results[1], 1); 61 | assert.equal(results.length, 2); 62 | }); 63 | 64 | process.nextTick(function() { 65 | throw new Error(); 66 | }); 67 | 68 | process.on('exit', function(code) { 69 | // If the exit code isn't ok then return early to throw the stack that 70 | // caused the bad return code. 71 | if (code !== 0) 72 | return; 73 | 74 | // Need to remove the async listeners or tests will always pass 75 | for (var i = 0; i < listeners.length; i++) 76 | process.removeAsyncListener(listeners[i]); 77 | 78 | assert.ok(uncaughtFired); 79 | console.log('ok'); 80 | }); 81 | -------------------------------------------------------------------------------- /test/core-asynclistener-error-net.simple.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | var PORT = 12346; 23 | 24 | if (!process.addAsyncListener) require('../index.js'); 25 | 26 | var assert = require('assert'); 27 | var dns = require('dns'); 28 | var fs = require('fs'); 29 | var net = require('net'); 30 | 31 | var errorMsgs = []; 32 | var caught = 0; 33 | var expectCaught = 0; 34 | 35 | var callbacksObj = { 36 | error: function(value, er) { 37 | var idx = errorMsgs.indexOf(er.message); 38 | caught++; 39 | 40 | // process._rawDebug('Handling error: ' + er.message); 41 | 42 | if (-1 < idx) 43 | errorMsgs.splice(idx, 1); 44 | else 45 | throw new Error('Message not found: ' + er.message); 46 | 47 | return true; 48 | } 49 | }; 50 | 51 | var listener = process.addAsyncListener(callbacksObj); 52 | 53 | process.on('exit', function(code) { 54 | process.removeAsyncListener(listener); 55 | 56 | if (code > 0) 57 | return; 58 | 59 | if (errorMsgs.length > 0) 60 | throw new Error('Errors not fired: ' + errorMsgs); 61 | 62 | assert.equal(caught, expectCaught); 63 | console.log('ok'); 64 | }); 65 | 66 | 67 | // Net 68 | var iter = 3; 69 | for (var i = 0; i < iter; i++) { 70 | errorMsgs.push('net - error: server connection'); 71 | errorMsgs.push('net - error: client data'); 72 | errorMsgs.push('net - error: server data'); 73 | } 74 | errorMsgs.push('net - error: server closed'); 75 | 76 | var server = net.createServer(function(c) { 77 | c.on('data', function() { 78 | if (0 === --iter) { 79 | server.close(function() { 80 | console.log('net - server closing'); 81 | throw new Error('net - error: server closed'); 82 | }); 83 | expectCaught++; 84 | } 85 | console.log('net - connection received data'); 86 | throw new Error('net - error: server data'); 87 | }); 88 | expectCaught++; 89 | 90 | c.end('bye'); 91 | console.log('net - connection received'); 92 | throw new Error('net - error: server connection'); 93 | }); 94 | expectCaught += iter; 95 | 96 | server.listen(PORT, function() { 97 | for (var i = 0; i < iter; i++) 98 | clientConnect(); 99 | }); 100 | 101 | function clientConnect() { 102 | var client = net.connect(PORT, function() { }); 103 | 104 | client.on('data', function() { 105 | client.end('see ya'); 106 | console.log('net - client received data'); 107 | throw new Error('net - error: client data'); 108 | }); 109 | expectCaught++; 110 | } 111 | -------------------------------------------------------------------------------- /test/core-asynclistener-error-throw-in-after.simple.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 23 | if (!process.addAsyncListener) require('../index.js'); 24 | if (!global.setImmediate) global.setImmediate = setTimeout; 25 | 26 | var assert = require('assert'); 27 | 28 | var once = 0; 29 | function onAsync0() {} 30 | 31 | var handlers = { 32 | after: function () { 33 | throw new Error('erk'); 34 | }, 35 | error: function () { 36 | // error handler must be called exactly *once* 37 | once++; 38 | 39 | return true; 40 | } 41 | }; 42 | 43 | var key = process.addAsyncListener(onAsync0, handlers); 44 | 45 | process.on('uncaughtException', function () { 46 | // process should propagate error regardless of 47 | // error handlers return value 48 | assert.equal(once, 1); 49 | console.log('ok'); 50 | }); 51 | 52 | setImmediate(function () { 53 | return 1; 54 | }); 55 | 56 | process.removeAsyncListener(key); 57 | -------------------------------------------------------------------------------- /test/core-asynclistener-error-throw-in-before-multiple.simple.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 23 | if (!process.addAsyncListener) require('../index.js'); 24 | if (!global.setImmediate) global.setImmediate = setTimeout; 25 | 26 | var assert = require('assert'); 27 | 28 | var once = 0; 29 | function onAsync0() {} 30 | function onAsync1() {} 31 | 32 | var handlers = { 33 | before : function () { 34 | throw 1; 35 | }, 36 | error : function (stor, err) { 37 | // must catch error thrown in before 38 | assert.equal(err, 1); 39 | 40 | once++; 41 | 42 | return true; 43 | } 44 | }; 45 | 46 | var handlers1 = { 47 | before : function () { 48 | throw 2; 49 | }, 50 | error : function (stor, err) { 51 | // must catch *other* handler's throw error 52 | assert.equal(err, 1); 53 | 54 | once++; 55 | 56 | return true; 57 | } 58 | }; 59 | 60 | var keys = [ 61 | process.addAsyncListener(onAsync0, handlers), 62 | process.addAsyncListener(onAsync1, handlers1) 63 | ]; 64 | 65 | process.on('uncaughtException', function () { 66 | // both error handlers must fire 67 | assert.equal(once, 2); 68 | 69 | console.log('ok'); 70 | }); 71 | 72 | setImmediate(function () { 73 | return 1; 74 | }); 75 | 76 | keys.forEach(function (key) { 77 | process.removeAsyncListener(key); 78 | }); 79 | 80 | -------------------------------------------------------------------------------- /test/core-asynclistener-error-throw-in-before.simple.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 23 | if (!process.addAsyncListener) require('../index.js'); 24 | if (!global.setImmediate) global.setImmediate = setTimeout; 25 | 26 | var assert = require('assert'); 27 | 28 | var once = 0; 29 | function onAsync0() {} 30 | 31 | var handlers = { 32 | before : function () { 33 | throw 1; 34 | }, 35 | error : function () { 36 | // error handler must be called exactly *once* 37 | once++; 38 | 39 | return true; 40 | } 41 | }; 42 | 43 | var key = process.addAsyncListener(onAsync0, handlers); 44 | 45 | process.on('uncaughtException', function () { 46 | // process should propagate error regardless of 47 | // error handlers return value 48 | assert.equal(once, 1); 49 | console.log('ok'); 50 | }); 51 | 52 | setImmediate(function () { 53 | return 1; 54 | }); 55 | 56 | process.removeAsyncListener(key); 57 | -------------------------------------------------------------------------------- /test/core-asynclistener-error-throw-in-error.simple.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 23 | if (!process.addAsyncListener) require('../index.js'); 24 | if (!global.setImmediate) global.setImmediate = setTimeout; 25 | 26 | var assert = require('assert'); 27 | var cluster = require('cluster'); 28 | 29 | function onAsync0() {} 30 | 31 | if (cluster.isMaster) { 32 | cluster.setupMaster({ 33 | silent : true 34 | }); 35 | cluster.fork(); 36 | cluster.on('exit', function (worker, code) { 37 | if (process._fatalException) { 38 | // verify child exited because of throw from 'error' 39 | assert.equal(code, 7); 40 | } 41 | else { 42 | // node < 0.9.x doesn't have error exit codes 43 | assert.equal(code, 1); 44 | } 45 | 46 | console.log('ok'); 47 | }); 48 | } else { 49 | var once = 0; 50 | 51 | var handlers = { 52 | error : function () { 53 | // the error handler should not be called again 54 | if (once++ !== 0) process.exit(5); 55 | 56 | throw new Error('error handler'); 57 | } 58 | }; 59 | 60 | var key = process.addAsyncListener(onAsync0, handlers); 61 | 62 | process.on('unhandledException', function () { 63 | // throwing in 'error' should bypass unhandledException 64 | process.exit(1); 65 | }); 66 | 67 | setImmediate(function () { 68 | throw new Error('setImmediate'); 69 | }); 70 | 71 | process.removeAsyncListener(key); 72 | } 73 | -------------------------------------------------------------------------------- /test/core-asynclistener-error.simple.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | var PORT = 12346; 23 | 24 | if (!process.addAsyncListener) require('../index.js'); 25 | if (!global.setImmediate) global.setImmediate = setTimeout; 26 | 27 | var assert = require('assert'); 28 | var dns = require('dns'); 29 | var fs = require('fs'); 30 | var net = require('net'); 31 | var addListener = process.addAsyncListener; 32 | var removeListener = process.removeAsyncListener; 33 | 34 | var caught = 0; 35 | var expectCaught = 0; 36 | 37 | function asyncL() { } 38 | 39 | var callbacksObj = { 40 | error: function(domain, er) { 41 | caught++; 42 | 43 | switch (er.message) { 44 | case 'sync throw': 45 | case 'setTimeout - simple': 46 | case 'setImmediate - simple': 47 | case 'setInterval - simple': 48 | case 'process.nextTick - simple': 49 | case 'setTimeout - nested': 50 | case 'process.nextTick - nested': 51 | case 'setImmediate - nested': 52 | case 'setTimeout2 - nested': 53 | case 'setInterval - nested': 54 | case 'fs - file does not exist': 55 | case 'fs - nested file does not exist': 56 | case 'fs - exists': 57 | case 'fs - realpath': 58 | case 'net - connection listener': 59 | case 'net - server listening': 60 | case 'net - client connect': 61 | case 'dns - lookup': 62 | return true; 63 | 64 | default: 65 | return false; 66 | } 67 | } 68 | }; 69 | 70 | process.on('exit', function() { 71 | console.log('caught:', caught); 72 | console.log('expected:', expectCaught); 73 | assert.equal(caught, expectCaught, 'caught all expected errors'); 74 | console.log('ok'); 75 | }); 76 | 77 | var listener = process.createAsyncListener(asyncL, callbacksObj); 78 | 79 | 80 | // Catch synchronous throws 81 | process.nextTick(function() { 82 | addListener(listener); 83 | 84 | expectCaught++; 85 | throw new Error('sync throw'); 86 | 87 | removeListener(listener); 88 | }); 89 | 90 | 91 | // Simple cases 92 | process.nextTick(function() { 93 | addListener(listener); 94 | 95 | setTimeout(function() { 96 | throw new Error('setTimeout - simple'); 97 | }); 98 | expectCaught++; 99 | 100 | setImmediate(function() { 101 | throw new Error('setImmediate - simple'); 102 | }); 103 | expectCaught++; 104 | 105 | var b = setInterval(function() { 106 | clearInterval(b); 107 | throw new Error('setInterval - simple'); 108 | }); 109 | expectCaught++; 110 | 111 | process.nextTick(function() { 112 | throw new Error('process.nextTick - simple'); 113 | }); 114 | expectCaught++; 115 | 116 | removeListener(listener); 117 | }); 118 | 119 | 120 | // Deeply nested 121 | process.nextTick(function() { 122 | addListener(listener); 123 | 124 | setTimeout(function() { 125 | process.nextTick(function() { 126 | setImmediate(function() { 127 | var b = setInterval(function() { 128 | clearInterval(b); 129 | throw new Error('setInterval - nested'); 130 | }); 131 | expectCaught++; 132 | throw new Error('setImmediate - nested'); 133 | }); 134 | expectCaught++; 135 | throw new Error('process.nextTick - nested'); 136 | }); 137 | expectCaught++; 138 | setTimeout(function() { 139 | throw new Error('setTimeout2 - nested'); 140 | }); 141 | expectCaught++; 142 | throw new Error('setTimeout - nested'); 143 | }); 144 | expectCaught++; 145 | 146 | removeListener(listener); 147 | }); 148 | 149 | 150 | // FS 151 | process.nextTick(function() { 152 | addListener(listener); 153 | 154 | fs.stat('does not exist', function() { 155 | throw new Error('fs - file does not exist'); 156 | }); 157 | expectCaught++; 158 | 159 | fs.exists('hi all', function() { 160 | throw new Error('fs - exists'); 161 | }); 162 | expectCaught++; 163 | 164 | fs.realpath('/some/path', function() { 165 | throw new Error('fs - realpath'); 166 | }); 167 | expectCaught++; 168 | 169 | removeListener(listener); 170 | }); 171 | 172 | 173 | // Nested FS 174 | process.nextTick(function() { 175 | addListener(listener); 176 | 177 | setTimeout(function() { 178 | setImmediate(function() { 179 | var b = setInterval(function() { 180 | clearInterval(b); 181 | process.nextTick(function() { 182 | fs.stat('does not exist', function() { 183 | throw new Error('fs - nested file does not exist'); 184 | }); 185 | expectCaught++; 186 | }); 187 | }); 188 | }); 189 | }); 190 | 191 | removeListener(listener); 192 | }); 193 | 194 | 195 | // Net 196 | process.nextTick(function() { 197 | addListener(listener); 198 | 199 | var server = net.createServer(function() { 200 | server.close(); 201 | throw new Error('net - connection listener'); 202 | }); 203 | expectCaught++; 204 | 205 | server.listen(PORT, function() { 206 | var client = net.connect(PORT, function() { 207 | client.end(); 208 | throw new Error('net - client connect'); 209 | }); 210 | expectCaught++; 211 | throw new Error('net - server listening'); 212 | }); 213 | expectCaught++; 214 | 215 | removeListener(listener); 216 | }); 217 | 218 | 219 | // DNS 220 | process.nextTick(function() { 221 | addListener(listener); 222 | 223 | dns.lookup('localhost', function() { 224 | throw new Error('dns - lookup'); 225 | }); 226 | expectCaught++; 227 | 228 | removeListener(listener); 229 | }); 230 | -------------------------------------------------------------------------------- /test/core-asynclistener-nexttick-remove.simple.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 23 | if (!process.addAsyncListener) require('../index.js'); 24 | 25 | var assert = require('assert'); 26 | var net = require('net'); 27 | var fs = require('fs'); 28 | 29 | var actualAsync = 0; 30 | var expectAsync = 0; 31 | 32 | 33 | process.on('exit', function() { 34 | console.log('expected', expectAsync); 35 | console.log('actual ', actualAsync); 36 | assert.equal(expectAsync, actualAsync); 37 | console.log('ok'); 38 | }); 39 | 40 | 41 | // --- Begin Testing --- // 42 | 43 | function onAsync() { 44 | actualAsync++; 45 | } 46 | 47 | 48 | var id; 49 | process.nextTick(function() { 50 | process.removeAsyncListener(id); 51 | }); 52 | id = process.addAsyncListener(onAsync); 53 | 54 | 55 | // Test listeners side-by-side 56 | var b = setInterval(function() { 57 | clearInterval(b); 58 | }); 59 | expectAsync++; 60 | 61 | var c = setInterval(function() { 62 | clearInterval(c); 63 | }); 64 | expectAsync++; 65 | 66 | setTimeout(function() { }); 67 | expectAsync++; 68 | 69 | setTimeout(function() { }); 70 | expectAsync++; 71 | 72 | process.nextTick(function() { }); 73 | expectAsync++; 74 | 75 | process.nextTick(function() { }); 76 | expectAsync++; 77 | 78 | setImmediate(function() { }); 79 | expectAsync++; 80 | 81 | setImmediate(function() { }); 82 | expectAsync++; 83 | 84 | setTimeout(function() { }, 100); 85 | expectAsync++; 86 | 87 | setTimeout(function() { }, 100); 88 | expectAsync++; 89 | 90 | 91 | // Async listeners should propagate with nested callbacks 92 | var interval = 3; 93 | 94 | process.nextTick(function() { 95 | setTimeout(function() { 96 | setImmediate(function() { 97 | var i = setInterval(function() { 98 | if (--interval <= 0) 99 | clearInterval(i); 100 | }); 101 | expectAsync++; 102 | }); 103 | expectAsync++; 104 | process.nextTick(function() { 105 | setImmediate(function() { 106 | setTimeout(function() { }, 200); 107 | expectAsync++; 108 | }); 109 | expectAsync++; 110 | }); 111 | expectAsync++; 112 | }); 113 | expectAsync++; 114 | }); 115 | expectAsync++; 116 | 117 | 118 | // Test callbacks from fs I/O 119 | fs.stat('something random', function() { }); 120 | expectAsync++; 121 | 122 | setImmediate(function() { 123 | fs.stat('random again', function() { }); 124 | expectAsync++; 125 | }); 126 | expectAsync++; 127 | 128 | 129 | // Test net I/O 130 | var server = net.createServer(function() { }); 131 | expectAsync++; 132 | 133 | server.listen(8080, function() { 134 | server.close(); 135 | expectAsync++; 136 | }); 137 | expectAsync++; 138 | -------------------------------------------------------------------------------- /test/core-asynclistener-only-add.simple.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 23 | if (!process.addAsyncListener) require('../index.js'); 24 | 25 | var assert = require('assert'); 26 | var net = require('net'); 27 | var fs = require('fs'); 28 | 29 | var actualAsync = 0; 30 | var expectAsync = 0; 31 | 32 | 33 | process.on('exit', function() { 34 | console.log('expected', expectAsync); 35 | console.log('actual ', actualAsync); 36 | assert.equal(expectAsync, actualAsync); 37 | console.log('ok'); 38 | }); 39 | 40 | 41 | // --- Begin Testing --- // 42 | 43 | function onAsync() { 44 | actualAsync++; 45 | } 46 | 47 | 48 | process.addAsyncListener(onAsync); 49 | 50 | 51 | // Test listeners side-by-side 52 | var b = setInterval(function() { 53 | clearInterval(b); 54 | }); 55 | expectAsync++; 56 | 57 | var c = setInterval(function() { 58 | clearInterval(c); 59 | }); 60 | expectAsync++; 61 | 62 | setTimeout(function() { }); 63 | expectAsync++; 64 | 65 | setTimeout(function() { }); 66 | expectAsync++; 67 | 68 | process.nextTick(function() { }); 69 | expectAsync++; 70 | 71 | process.nextTick(function() { }); 72 | expectAsync++; 73 | 74 | setImmediate(function() { }); 75 | expectAsync++; 76 | 77 | setImmediate(function() { }); 78 | expectAsync++; 79 | 80 | setTimeout(function() { }, 100); 81 | expectAsync++; 82 | 83 | setTimeout(function() { }, 100); 84 | expectAsync++; 85 | 86 | 87 | // Async listeners should propagate with nested callbacks 88 | var interval = 3; 89 | 90 | process.nextTick(function() { 91 | setTimeout(function() { 92 | setImmediate(function() { 93 | var i = setInterval(function() { 94 | if (--interval <= 0) 95 | clearInterval(i); 96 | }); 97 | expectAsync++; 98 | }); 99 | expectAsync++; 100 | process.nextTick(function() { 101 | setImmediate(function() { 102 | setTimeout(function() { }, 200); 103 | expectAsync++; 104 | }); 105 | expectAsync++; 106 | }); 107 | expectAsync++; 108 | }); 109 | expectAsync++; 110 | }); 111 | expectAsync++; 112 | 113 | 114 | // Test callbacks from fs I/O 115 | fs.stat('something random', function() { }); 116 | expectAsync++; 117 | 118 | setImmediate(function() { 119 | fs.stat('random again', function() { }); 120 | expectAsync++; 121 | }); 122 | expectAsync++; 123 | 124 | 125 | // Test net I/O 126 | var server = net.createServer(function() { }); 127 | expectAsync++; 128 | 129 | server.listen(8080, function() { 130 | server.close(); 131 | expectAsync++; 132 | }); 133 | expectAsync++; 134 | -------------------------------------------------------------------------------- /test/core-asynclistener-remove-before.simple.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 23 | if (!process.addAsyncListener) require('../index.js'); 24 | if (!global.setImmediate) global.setImmediate = setTimeout; 25 | 26 | var assert = require('assert'); 27 | 28 | function onAsync0() { 29 | return 0; 30 | } 31 | 32 | var set = 0; 33 | var asyncNoHandleError = { 34 | before : function () { 35 | set ++; 36 | }, 37 | after : function () { 38 | set ++; 39 | } 40 | }; 41 | 42 | var key = process.addAsyncListener(onAsync0, asyncNoHandleError); 43 | 44 | process.removeAsyncListener(key); 45 | 46 | setImmediate(function () { 47 | return 1; 48 | }); 49 | 50 | process.on('exit', function () { 51 | // the async handler should never be called 52 | assert.equal(set, 0); 53 | 54 | console.log('ok'); 55 | }); 56 | -------------------------------------------------------------------------------- /test/core-asynclistener-remove-inflight-error.simple.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 23 | if (!process.addAsyncListener) require('../index.js'); 24 | if (!global.setImmediate) global.setImmediate = setTimeout; 25 | 26 | var assert = require('assert'); 27 | 28 | function onAsync0() {} 29 | 30 | var set = 0; 31 | var asyncNoHandleError = { 32 | error : function () { 33 | set ++; 34 | } 35 | }; 36 | 37 | var key = process.addAsyncListener(onAsync0, asyncNoHandleError); 38 | 39 | setImmediate(function () { 40 | throw 1; 41 | }); 42 | 43 | process.removeAsyncListener(key); 44 | process.on('uncaughtException', function () { 45 | // throwing should call the error handler once, 46 | // then propagate to the uncaughtException 47 | assert.equal(set, 1); 48 | 49 | console.log('ok'); 50 | }); 51 | -------------------------------------------------------------------------------- /test/core-asynclistener-remove-inflight.simple.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 23 | if (!process.addAsyncListener) require('../index.js'); 24 | if (!global.setImmediate) global.setImmediate = setTimeout; 25 | 26 | var assert = require('assert'); 27 | 28 | function onAsync0() {} 29 | 30 | var set = 0; 31 | var asyncNoHandleError = { 32 | before : function () { 33 | set++; 34 | }, 35 | after : function () { 36 | set++; 37 | } 38 | }; 39 | 40 | var key = process.addAsyncListener(onAsync0, asyncNoHandleError); 41 | 42 | setImmediate(function () { 43 | return 1; 44 | }); 45 | 46 | process.removeAsyncListener(key); 47 | process.on('exit', function () { 48 | // calling removeAsyncListener *after* a callback is scheduled 49 | // should not affect the handler from responding to the callback 50 | assert.equal(set, 2); 51 | 52 | console.log('ok'); 53 | }); 54 | -------------------------------------------------------------------------------- /test/core-asynclistener.simple.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | var PORT = 12346; 23 | 24 | if (!process.addAsyncListener) require('../index.js'); 25 | if (!global.setImmediate) global.setImmediate = setTimeout; 26 | 27 | var assert = require('assert'); 28 | var net = require('net'); 29 | var fs = require('fs'); 30 | var dgram = require('dgram'); 31 | 32 | var addListener = process.addAsyncListener; 33 | var removeListener = process.removeAsyncListener; 34 | var actualAsync = 0; 35 | var expectAsync = 0; 36 | 37 | var callbacks = { 38 | create: function onAsync() { 39 | actualAsync++; 40 | } 41 | }; 42 | 43 | var listener = process.createAsyncListener(callbacks); 44 | 45 | process.on('exit', function() { 46 | console.log('expected', expectAsync); 47 | console.log('actual ', actualAsync); 48 | // TODO(trevnorris): Not a great test. If one was missed, but others 49 | // overflowed then the test would still pass. 50 | assert.ok(actualAsync >= expectAsync); 51 | }); 52 | 53 | 54 | // Test listeners side-by-side 55 | process.nextTick(function() { 56 | addListener(listener); 57 | 58 | var b = setInterval(function() { 59 | clearInterval(b); 60 | }); 61 | expectAsync++; 62 | 63 | var c = setInterval(function() { 64 | clearInterval(c); 65 | }); 66 | expectAsync++; 67 | 68 | setTimeout(function() { }); 69 | expectAsync++; 70 | 71 | setTimeout(function() { }); 72 | expectAsync++; 73 | 74 | process.nextTick(function() { }); 75 | expectAsync++; 76 | 77 | process.nextTick(function() { }); 78 | expectAsync++; 79 | 80 | setImmediate(function() { }); 81 | expectAsync++; 82 | 83 | setImmediate(function() { }); 84 | expectAsync++; 85 | 86 | setTimeout(function() { }, 10); 87 | expectAsync++; 88 | 89 | setTimeout(function() { }, 10); 90 | expectAsync++; 91 | 92 | removeListener(listener); 93 | }); 94 | 95 | 96 | // Async listeners should propagate with nested callbacks 97 | process.nextTick(function() { 98 | addListener(listener); 99 | var interval = 3; 100 | 101 | process.nextTick(function() { 102 | setTimeout(function() { 103 | setImmediate(function() { 104 | var i = setInterval(function() { 105 | if (--interval <= 0) 106 | clearInterval(i); 107 | }); 108 | expectAsync++; 109 | }); 110 | expectAsync++; 111 | process.nextTick(function() { 112 | setImmediate(function() { 113 | setTimeout(function() { }, 20); 114 | expectAsync++; 115 | }); 116 | expectAsync++; 117 | }); 118 | expectAsync++; 119 | }); 120 | expectAsync++; 121 | }); 122 | expectAsync++; 123 | 124 | removeListener(listener); 125 | }); 126 | 127 | 128 | // Test triggers with two async listeners 129 | process.nextTick(function() { 130 | addListener(listener); 131 | addListener(listener); 132 | 133 | setTimeout(function() { 134 | process.nextTick(function() { }); 135 | expectAsync += 2; 136 | }); 137 | expectAsync += 2; 138 | 139 | removeListener(listener); 140 | removeListener(listener); 141 | }); 142 | 143 | 144 | // Test callbacks from fs I/O 145 | process.nextTick(function() { 146 | addListener(listener); 147 | 148 | fs.stat('something random', function(err, stat) { }); 149 | expectAsync++; 150 | 151 | setImmediate(function() { 152 | fs.stat('random again', function(err, stat) { }); 153 | expectAsync++; 154 | }); 155 | expectAsync++; 156 | 157 | removeListener(listener); 158 | }); 159 | 160 | 161 | // Test net I/O 162 | process.nextTick(function() { 163 | addListener(listener); 164 | 165 | var server = net.createServer(function(c) { }); 166 | expectAsync++; 167 | 168 | server.listen(PORT, function() { 169 | server.close(); 170 | expectAsync++; 171 | }); 172 | expectAsync++; 173 | 174 | removeListener(listener); 175 | }); 176 | 177 | 178 | // Test UDP 179 | process.nextTick(function() { 180 | addListener(listener); 181 | 182 | var server = dgram.createSocket('udp4'); 183 | expectAsync++; 184 | 185 | server.bind(PORT); 186 | 187 | server.close(); 188 | expectAsync++; 189 | 190 | removeListener(listener); 191 | }); 192 | -------------------------------------------------------------------------------- /test/core/core-asynclistener-add-inflight.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 23 | if (!process.addAsyncListener) require('../../index.js'); 24 | 25 | var assert = require('assert'); 26 | 27 | function onAsync0() {} 28 | function onAsync1() {} 29 | 30 | var once = 0; 31 | var handlers0 = { 32 | before: function (stor, err) { 33 | // should catch the error *once* 34 | once++; 35 | } 36 | } 37 | 38 | var handlers1 = { 39 | before: function (stor, err) { 40 | // handler was added in flight, and should not be called 41 | throw "Should Never Be Called"; 42 | } 43 | } 44 | 45 | var key0 = process.addAsyncListener(onAsync0, handlers0); 46 | 47 | process.on('exit', function (err) { 48 | // handlers0 before handler must be called once only 49 | assert.equal(once, 1); 50 | console.log('ok'); 51 | }); 52 | 53 | setImmediate(function () { 54 | 1; 55 | }); 56 | 57 | process.addAsyncListener(onAsync1, handlers1); 58 | process.removeAsyncListener(key0); 59 | -------------------------------------------------------------------------------- /test/core/core-asynclistener-error-throw-in-before-inflight.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 23 | if (!process.addAsyncListener) require('../../index.js'); 24 | 25 | var assert = require('assert'); 26 | 27 | function onAsync0() {} 28 | function onAsync1() {} 29 | 30 | var once = 0; 31 | var handlers0 = { 32 | error: function (stor, err) { 33 | // should catch the error *once* 34 | once++; 35 | } 36 | } 37 | 38 | var handlers1 = { 39 | error: function (stor, err) { 40 | // this error handler is bound *after* the async callback 41 | // and it should not handle the error 42 | throw "Should Never Be Called"; 43 | } 44 | } 45 | 46 | var key0 = process.addAsyncListener(onAsync0, handlers0); 47 | 48 | process.on('uncaughtException', function (err) { 49 | // handlers0 error handler must be called once only 50 | assert.equal(once, 1); 51 | console.log('ok'); 52 | }); 53 | 54 | setImmediate(function () { 55 | throw 1; 56 | }); 57 | 58 | process.addAsyncListener(onAsync1, handlers1); 59 | process.removeAsyncListener(key0); 60 | -------------------------------------------------------------------------------- /test/errors-this-tick.tap.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | 5 | if (!process.addAsyncListener) require('../index.js'); 6 | 7 | function MiniCLS() { 8 | this.active = Object.create(null); 9 | this._stack = []; 10 | } 11 | 12 | MiniCLS.prototype.enter = function (context) { 13 | assert.ok(context, "context must be provided for entering"); 14 | 15 | this._stack.push(this.active); 16 | this.active = context; 17 | }; 18 | 19 | MiniCLS.prototype.exit = function (context) { 20 | assert.ok(context, "context must be provided for exiting"); 21 | 22 | if (this.active === context) { 23 | assert.ok(this._stack.length, "can't remove top context"); 24 | this.active = this._stack.pop(); 25 | return; 26 | } 27 | 28 | var index = this._stack.lastIndexOf(context); 29 | 30 | assert.ok(index >= 0, "context not currently entered; can't exit"); 31 | assert.ok(index, "can't remove top context"); 32 | 33 | this.active = this._stack[index - 1]; 34 | this._stack.length = index - 1; 35 | }; 36 | 37 | MiniCLS.prototype.run = function (fn) { 38 | var context = Object.create(this.active); 39 | this.enter(context); 40 | try { 41 | fn(context); 42 | return context; 43 | } 44 | finally { 45 | this.exit(context); 46 | } 47 | }; 48 | 49 | var cls = new MiniCLS(); 50 | process.addAsyncListener( 51 | { 52 | create : function () { return cls.active; }, 53 | before : function (context, domain) { cls.enter(domain); }, 54 | after : function (context, domain) { cls.exit(domain); }, 55 | error : function (domain) { if (domain) cls.exit(domain); } 56 | } 57 | ); 58 | 59 | process.on('uncaughtException', function (err) { 60 | if (err.message === 'oops') { 61 | console.log('ok got expected error: %s', err.message); 62 | } 63 | else { 64 | console.log('not ok got expected error: %s', err.stack); 65 | } 66 | }); 67 | 68 | cls.run(function () { 69 | throw new Error('oops'); 70 | }); 71 | -------------------------------------------------------------------------------- /test/fork-listen2-problem.tap.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fork = require('child_process').fork; 4 | var test = require('tap').test; 5 | 6 | var server 7 | 8 | test("parent listener", function (t) { 9 | server = require('net').createServer(); 10 | 11 | server.listen(8585, function () { 12 | t.ok(server, "parent listening on port 8585"); 13 | 14 | var listener = fork(__dirname + '/fork-listener.js'); 15 | t.ok(listener, "child process started"); 16 | 17 | listener.on('message', function (message) { 18 | if (message === 'shutdown') { 19 | t.ok(message, "child handled error properly"); 20 | listener.send('shutdown'); 21 | } 22 | else { 23 | t.fail("parent got unexpected message " + message); 24 | } 25 | t.end(); 26 | }); 27 | }); 28 | }); 29 | 30 | test("tearDown", function (t) { 31 | server.close(); 32 | t.end(); 33 | }) 34 | -------------------------------------------------------------------------------- /test/fork-listener.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var domain = require('domain'); 4 | 5 | if (!process.addAsyncListener) require('../index.js'); 6 | 7 | var d = domain.create(); 8 | d.on('error', function (error) { 9 | process.send(error.message); 10 | }); 11 | 12 | process.on('message', function (message) { 13 | if (message === 'shutdown') { 14 | process.exit(); 15 | } 16 | else { 17 | process.send("child got unexpected message " + message); 18 | } 19 | }); 20 | 21 | d.run(function () { 22 | var server = require('net').createServer(); 23 | 24 | server.on('error', function () { 25 | process.send('shutdown'); 26 | }); 27 | 28 | server.listen(8585, function () { 29 | process.send("child shouldn't be able to listen on port 8585"); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/function-length-preserved.tap.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test; 2 | 3 | test("asyncListener preserves function length", function (t) { 4 | t.plan(2); 5 | 6 | var fsLengthsPre = computeValueLengths(require('fs')); 7 | var httpLengthsPre = computeValueLengths(require('http')); 8 | 9 | if (!process.addAsyncListener) require('../index.js'); 10 | 11 | var fsLengthsPost = computeValueLengths(require('fs')); 12 | var httpLengthsPost = computeValueLengths(require('http')); 13 | 14 | t.same(fsLengthsPre, fsLengthsPost); 15 | t.same(httpLengthsPre, httpLengthsPost); 16 | }); 17 | 18 | function computeValueLengths(o) { 19 | var lengths = []; 20 | for (var k in o) { 21 | lengths.push(o[k].length); 22 | } 23 | return lengths; 24 | } 25 | -------------------------------------------------------------------------------- /test/handle.tap.js: -------------------------------------------------------------------------------- 1 | if (!process.addAsyncListener) require('../index.js'); 2 | 3 | var test = require('tap').test; 4 | var net = require('net'); 5 | 6 | test('synchronous errors during connect return a null _handle', function(t){ 7 | t.plan(3); 8 | 9 | // listening server 10 | var server = net.createServer().listen(8000); 11 | 12 | // client 13 | var client = net.connect({port: 8000}); 14 | 15 | client.on('connect', function(){ 16 | t.ok(true, 'connect'); 17 | // kill connection 18 | client.end(); 19 | }); 20 | 21 | client.on('error', function(){ 22 | server.close(); 23 | t.ok(true, 'done test'); 24 | }); 25 | 26 | client.on('end', function() { 27 | setTimeout(function(){ 28 | // try to reconnect, but this has an error 29 | // rather than throw the right error, we're going to get an async-listener error 30 | t.ok(true, 'end'); 31 | client.connect(8001); 32 | }, 100); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/http-request.tap.js: -------------------------------------------------------------------------------- 1 | if (!process.addAsyncListener) require('../index.js'); 2 | 3 | var extend = require('util')._extend; 4 | var test = require('tap').test; 5 | var http = require('http'); 6 | 7 | // Convert semver string to number set 8 | // TODO: This is *very* naive structure to check versions with, 9 | // but it works well enough for now... 10 | var nodeVersion = process.version.slice(1).split('.').map(Number) 11 | 12 | test('http.Agent socket reuse works', function(t){ 13 | function main (done) { 14 | var listener = addListner(); 15 | var times = 2; 16 | 17 | var agent = new http.Agent({ 18 | keepAlive: true, 19 | maxFreeSockets: 1, 20 | maxSockets: 1 21 | }); 22 | 23 | function after(rand, i) { 24 | if (--times === 0) { 25 | t.deepEqual( 26 | listener.root, 27 | expected, 28 | 'should have equal state structures' 29 | ); 30 | if (agent.destroy) { 31 | agent.destroy(); 32 | } 33 | done(); 34 | } 35 | } 36 | 37 | function ping(i) { 38 | listener.currentName = 'ping #' + i + ' request'; 39 | var addr = server.address(); 40 | var req = http.request({ 41 | agent: agent, 42 | port: addr.port, 43 | host: addr.host, 44 | path: '/sub' 45 | }, function (res) { 46 | // The second request is a logical continuation of 47 | // the first request, due to the http.Agent pooling 48 | if (i === 0) { 49 | t.equal( 50 | listener.current.name, 51 | 'ping #' + i + ' request', 52 | 'should be ping #' + i + ' request' 53 | ); 54 | } else { 55 | t.equal( 56 | listener.current.name, 57 | 'setImmediate to after #' + (i - 1), 58 | 'should be setImmediate to after #' + (i - 1) 59 | ); 60 | } 61 | 62 | listener.currentName = 'res.resume ping #' + i; 63 | const bufs = []; 64 | res.on('data', function (chunk) { 65 | bufs.push(chunk); 66 | }); 67 | res.on('end', function () { 68 | const body = Buffer.concat(bufs).toString(); 69 | t.equal('hello', body, 'should have body of "hello"') 70 | t.equal( 71 | listener.current.name, 72 | 'res.resume ping #' + i, 73 | 'should be res.resume ping #' + i 74 | ); 75 | listener.currentName = 'setImmediate to after #' + i; 76 | setImmediate(after, i); 77 | }); 78 | }); 79 | listener.currentName = 'req.end ping #' + i; 80 | req.end(); 81 | } 82 | 83 | for (var i = 0; i < times; i++) { 84 | listener.currentName = 'setImmediate #' + i; 85 | setImmediate(ping, i); 86 | } 87 | 88 | process.removeAsyncListener(listener.listener); 89 | 90 | // 91 | // NOTE: This expected structure building stuff is really complicated 92 | // because the interactions in node internals changed so much from 0.10 93 | // until now. It could be a lot simpler if we only cared about testing 94 | // the current stable, but this really needs to be tested back to 0.10. 95 | // 96 | // I'm sorry. :'( 97 | // 98 | function make (name, override) { 99 | return extend({ 100 | name: name, 101 | children: [], 102 | before: 1, 103 | after: 1, 104 | error: 0 105 | }, override || {}) 106 | } 107 | 108 | // 109 | // First ping branch 110 | // 111 | var innerResumeChildren = []; 112 | if (nodeVersion[0] < 8) { 113 | innerResumeChildren.push(make('res.resume ping #0')); 114 | } 115 | innerResumeChildren.push(make('setImmediate to after #0')); 116 | 117 | var innerResumeChildrenWrapped = [ 118 | make('res.resume ping #0', { 119 | children: innerResumeChildren 120 | }), 121 | make('res.resume ping #0'), 122 | make('res.resume ping #0') 123 | ]; 124 | var innerPingChildren = []; 125 | if (nodeVersion[0] == 0 && nodeVersion[1] < 12) { 126 | innerPingChildren.push(make('res.resume ping #0')); 127 | } 128 | innerPingChildren.push(make('res.resume ping #0', { 129 | children: nodeVersion[0] == 0 && nodeVersion[1] < 12 130 | ? innerResumeChildren 131 | : innerResumeChildrenWrapped 132 | })); 133 | if (nodeVersion[0] > 0 || nodeVersion[1] > 10) { 134 | if (nodeVersion[0] < 6 && nodeVersion[0] !== 4) { 135 | innerPingChildren.push(make('res.resume ping #0')); 136 | } 137 | innerPingChildren.push( 138 | make('res.resume ping #0', { 139 | children: [make('res.resume ping #0')] 140 | }), 141 | make('res.resume ping #0') 142 | ); 143 | } 144 | 145 | var firstImmediateChildren = [ 146 | make('ping #0 request', { 147 | children: [ 148 | make('ping #0 request', { 149 | children: innerPingChildren 150 | }), 151 | make('ping #0 request', { 152 | children: nodeVersion[0] > 0 || nodeVersion[1] > 10 153 | ? [make('req.end ping #1')] 154 | : [] 155 | }) 156 | ] 157 | }) 158 | ]; 159 | 160 | if (nodeVersion[0] > 4) { 161 | firstImmediateChildren.push(make('ping #0 request')); 162 | }; 163 | 164 | firstImmediateChildren.push( 165 | make('ping #0 request'), 166 | make('ping #0 request', { 167 | before: 0, 168 | after: 0 169 | }) 170 | ); 171 | 172 | var firstImmediate = make('setImmediate #0', { 173 | children: firstImmediateChildren 174 | }); 175 | 176 | // 177 | // Second ping branch 178 | // 179 | var innerPingChildren = []; 180 | if (nodeVersion[0] < 8) { 181 | innerPingChildren.push(make('res.resume ping #1')); 182 | } 183 | 184 | innerPingChildren.push(make('setImmediate to after #1', { 185 | after: 0 186 | })); 187 | 188 | var innerPingChildrenWrapped = [ 189 | make('res.resume ping #1', { 190 | children: innerPingChildren 191 | }), 192 | make('res.resume ping #1'), 193 | make('res.resume ping #1') 194 | ]; 195 | var innerImmediateChildren = []; 196 | if (nodeVersion[0] == 0 && nodeVersion[1] < 12) { 197 | innerImmediateChildren.push(make('res.resume ping #1')); 198 | } 199 | innerImmediateChildren.push(make('res.resume ping #1', { 200 | children: nodeVersion[0] == 0 && nodeVersion[1] < 12 201 | ? innerPingChildren 202 | : innerPingChildrenWrapped 203 | })); 204 | if (nodeVersion[0] > 0 || nodeVersion[1] > 10) { 205 | if (nodeVersion[0] < 6 && nodeVersion[0] !== 4) { 206 | innerImmediateChildren.push(make('res.resume ping #1')); 207 | } 208 | innerImmediateChildren.push( 209 | make('res.resume ping #1', { 210 | children: [make('res.resume ping #1')] 211 | }), 212 | make('res.resume ping #1') 213 | ); 214 | } 215 | 216 | var secondImmediate = make('setImmediate #1', { 217 | children: [ 218 | make('ping #1 request', { 219 | children: [ 220 | make('setImmediate to after #0', { 221 | children: innerImmediateChildren 222 | }), 223 | make('setImmediate to after #0', { 224 | children: [make('setImmediate to after #0')] 225 | }) 226 | ] 227 | }) 228 | ] 229 | }); 230 | 231 | // 232 | // Make expected object 233 | // 234 | var expected = make('root', { 235 | children: [ 236 | firstImmediate, 237 | secondImmediate 238 | ], 239 | before: 0, 240 | after: 0 241 | }); 242 | } 243 | 244 | var server = http.createServer(function (req, res) { 245 | res.end('hello'); 246 | }); 247 | 248 | // 249 | // Test client 250 | // 251 | server.listen(function () { 252 | main(function () { 253 | server.close(); 254 | server.on('close', function () { 255 | t.end(); 256 | }); 257 | }); 258 | }); 259 | }); 260 | 261 | function addListner() { 262 | var listener = process.addAsyncListener({ 263 | create: create, 264 | before: before, 265 | after: after, 266 | error: error 267 | }); 268 | 269 | var state = { 270 | listener: listener, 271 | currentName: 'root' 272 | }; 273 | 274 | state.root = create(); 275 | state.current = state.root; 276 | 277 | return state; 278 | 279 | function create () { 280 | var node = { 281 | name: state.currentName, 282 | children: [], 283 | before: 0, 284 | after: 0, 285 | error: 0 286 | }; 287 | 288 | if(state.current) state.current.children.push(node); 289 | return node; 290 | } 291 | 292 | function before(ctx, node) { 293 | state.current = node; 294 | state.current.before++; 295 | } 296 | 297 | function after(ctx, node) { 298 | node.after++; 299 | state.current = null; 300 | } 301 | 302 | function error(ctx, node) { 303 | node.error++; 304 | state.current = null; 305 | return false; 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /test/native-promises.tap.js: -------------------------------------------------------------------------------- 1 | if (!global.Promise) return; 2 | 3 | var test = require('tap').test; 4 | 5 | var unwrappedPromise = global.Promise; 6 | var resolvedBeforeWrap = unwrappedPromise.resolve(123) 7 | 8 | require('../index.js'); 9 | 10 | // Convert semver string to number set 11 | // TODO: This is *very* naive structure to check versions with, 12 | // but it works well enough for now... 13 | var nodeVersion = process.version.slice(1).split('.').map(Number) 14 | 15 | test('then', function(t) { 16 | var listener = addListner(); 17 | 18 | var promise = new Promise(function(accept, reject) { 19 | listener.currentName = 'accept'; 20 | accept(10); 21 | }); 22 | 23 | promise.then(function(val) { 24 | listener.currentName = 'nextTick in first then'; 25 | process.nextTick(function() { 26 | t.strictEqual(val, 10); 27 | }); 28 | listener.currentName = 'first then continuation'; 29 | }); 30 | 31 | listener.currentName = 'setImmediate in root'; 32 | setImmediate(function() { 33 | promise.then(function(val) { 34 | t.strictEqual(val, 10); 35 | t.strictEqual(this, global); 36 | listener.currentName = 'setTimeout in 2nd then'; 37 | setTimeout(function() { 38 | t.deepEqual(listener.root, expected); 39 | t.end(); 40 | }); 41 | listener.currentName = '2nd then continuation'; 42 | }); 43 | }); 44 | 45 | process.removeAsyncListener(listener.listener); 46 | 47 | var expected = { 48 | name: 'root', 49 | children: [ 50 | { 51 | name: 'accept', 52 | children: [ 53 | { 54 | name: 'nextTick in first then', 55 | children: [], 56 | before: 1, 57 | after: 1, 58 | error: 0 59 | }, 60 | { 61 | name: 'first then continuation', 62 | children: [], 63 | before: 0, 64 | after: 0, 65 | error: 0 66 | }, 67 | { 68 | name: 'setTimeout in 2nd then', 69 | children: [], 70 | before: 1, 71 | after: 0, 72 | error: 0 73 | }, 74 | { 75 | name: '2nd then continuation', 76 | children: [], 77 | before: 0, 78 | after: 0, 79 | error: 0 80 | } 81 | ], 82 | before: 2, 83 | after: 2, 84 | error: 0 85 | }, 86 | { 87 | name: 'accept', 88 | children: [], 89 | before: 1, 90 | after: 1, 91 | error: 0 92 | }, 93 | { 94 | name: 'setImmediate in root', 95 | children: [ 96 | { 97 | name: 'first then continuation', 98 | children: [], 99 | before: 1, 100 | after: 1, 101 | error: 0 102 | } 103 | ], 104 | before: 1, 105 | after: 1, 106 | error: 0 107 | } 108 | ], 109 | before: 0, 110 | after: 0, 111 | error: 0 112 | } 113 | }); 114 | 115 | test('catch', function(t) { 116 | var listener = addListner(); 117 | 118 | var promise = new Promise(function(accept, reject) { 119 | listener.currentName = 'reject'; 120 | reject(15); 121 | }); 122 | 123 | listener.currentName = 'catch'; 124 | promise.catch(function(val) { 125 | listener.currentName = 'nextTick in catch'; 126 | process.nextTick(function() { 127 | t.strictEqual(val, 15); 128 | }); 129 | listener.currentName = 'catch continuation'; 130 | }); 131 | 132 | listener.currentName = 'setImmediate in root'; 133 | setImmediate(function() { 134 | promise.then( 135 | function fullfilled() { 136 | throw new Error('should not be called on reject'); 137 | }, 138 | function rejected(val) { 139 | t.strictEqual(val, 15); 140 | t.strictEqual(this, global); 141 | listener.currentName = 'setTimeout in then'; 142 | setTimeout(function() { 143 | // some version of iojs use nextTick for some parts of its async 144 | if (listener.root.children.length === 3) { 145 | expected.children.splice(-1, 0, { 146 | name: 'catch', 147 | children: [], 148 | before: 1, 149 | after: 1, 150 | error: 0 151 | }) 152 | } 153 | t.deepEqual(listener.root, expected); 154 | t.end(); 155 | }); 156 | listener.currentName = 'then continuation'; 157 | } 158 | ) 159 | }); 160 | 161 | process.removeAsyncListener(listener.listener); 162 | 163 | var expected = { 164 | name: 'root', 165 | children: [ 166 | { 167 | name: 'reject', 168 | children: [ 169 | { 170 | name: 'nextTick in catch', 171 | children: [], 172 | before: 1, 173 | after: 1, 174 | error: 0 175 | }, 176 | { 177 | name: 'catch continuation', 178 | children: [], 179 | before: 0, 180 | after: 0, 181 | error: 0 182 | }, 183 | { 184 | name: 'setTimeout in then', 185 | children: [], 186 | before: 1, 187 | after: 0, 188 | error: 0 189 | }, 190 | { 191 | name: 'then continuation', 192 | children: [], 193 | before: 0, 194 | after: 0, 195 | error: 0 196 | } 197 | ], 198 | before: 2, 199 | after: 2, 200 | error: 0 201 | }, 202 | { 203 | name: 'setImmediate in root', 204 | children: [ 205 | { 206 | name: 'catch continuation', 207 | children: [], 208 | before: 0, 209 | after: 0, 210 | error: 0 211 | }, 212 | { 213 | name: 'catch continuation', 214 | children: [], 215 | before: 1, 216 | after: 1, 217 | error: 0 218 | } 219 | ], 220 | before: 1, 221 | after: 1, 222 | error: 0 223 | } 224 | ], 225 | before: 0, 226 | after: 0, 227 | error: 0 228 | }; 229 | }); 230 | 231 | test('Promise.resolve', function resolveTest(t) { 232 | var listener = addListner(); 233 | 234 | listener.currentName = 'resolve'; 235 | var p = Promise.resolve(123); 236 | 237 | p.then(function then(value) { 238 | listener.currentName = 'nextTick'; 239 | process.nextTick(function next() { 240 | t.equal(value, 123); 241 | t.deepEqual(listener.root, { 242 | name: 'root', 243 | children: [{ 244 | name: 'resolve', 245 | children: [{ 246 | name: 'nextTick', 247 | children: [], 248 | before: 1, 249 | after: 0, 250 | error: 0 251 | }], 252 | before: 1, 253 | after: 1, 254 | error: 0 255 | }, 256 | { 257 | name: 'resolve', 258 | children: [], 259 | before: 1, 260 | after: 1, 261 | error: 0 262 | }], 263 | before: 0, 264 | after: 0, 265 | error: 0 266 | }); 267 | t.end(); 268 | }); 269 | process.removeAsyncListener(listener.listener); 270 | }); 271 | }); 272 | 273 | test('Promise.reject', function rejectTest(t) { 274 | var listener = addListner(); 275 | 276 | listener.currentName = 'reject'; 277 | var p = Promise.reject(123); 278 | 279 | listener.currentName = 'catch'; 280 | p.catch(function then(value) { 281 | listener.currentName = 'nextTick'; 282 | process.nextTick(function next() { 283 | t.equal(value, 123); 284 | 285 | // some version of iojs use nextTick for some parts of its async 286 | if (listener.root.children.length === 2) { 287 | expected.children.push({ 288 | name: 'catch', 289 | children: [], 290 | before: 1, 291 | after: 1, 292 | error: 0 293 | }) 294 | } 295 | 296 | t.deepEqual(listener.root, expected); 297 | t.end(); 298 | }); 299 | 300 | listener.currentName = 'catch continuation'; 301 | }); 302 | 303 | process.removeAsyncListener(listener.listener); 304 | 305 | var expected = { 306 | name: 'root', 307 | children: [{ 308 | name: 'reject', 309 | children: [ 310 | { 311 | name: 'nextTick', 312 | children: [], 313 | before: 1, 314 | after: 0, 315 | error: 0 316 | }, 317 | { 318 | name: 'catch continuation', 319 | children: [], 320 | before: 0, 321 | after: 0, 322 | error: 0 323 | } 324 | ], 325 | before: 1, 326 | after: 1, 327 | error: 0 328 | }], 329 | before: 0, 330 | after: 0, 331 | error: 0 332 | } 333 | }); 334 | 335 | test('Promise.all', function allTest(t) { 336 | var listener = addListner(); 337 | 338 | listener.currentName = 'resolve 1'; 339 | var a = Promise.resolve(123); 340 | listener.currentName = 'resolve 2'; 341 | var b = Promise.resolve(456); 342 | listener.currentName = 'all'; 343 | var p = Promise.all([a, b]); 344 | 345 | p.then(function then(value) { 346 | listener.currentName = 'nextTick'; 347 | process.nextTick(function next() { 348 | process.removeAsyncListener(listener.listener); 349 | t.deepEqual(value, [123, 456]); 350 | t.deepEqual(listener.root, { 351 | name: 'root', 352 | children: [{ 353 | name: 'resolve 1', 354 | children: [{ 355 | // Internal continuation of a used for making the race future. 356 | name: 'all', 357 | children: [], 358 | before: 0, 359 | after: 0, 360 | error: 0 361 | }], 362 | before: 1, 363 | after: 1, 364 | error: 0 365 | }, { 366 | name: 'resolve 2', 367 | children: [ 368 | { 369 | name: 'all', 370 | children: [ 371 | { 372 | name: 'nextTick', 373 | children: [], 374 | before: 1, 375 | after: 0, 376 | error: 0 377 | }, 378 | { 379 | name: 'then continuation', 380 | children: [], 381 | before: 0, 382 | after: 0, 383 | error: 0 384 | } 385 | ], 386 | before: 1, 387 | after: 1, 388 | error: 0 389 | }, 390 | { 391 | // Internal continuation of b used for making the race future. 392 | name: 'all', 393 | children: [], 394 | before: 0, 395 | after: 0, 396 | error: 0 397 | } 398 | ], 399 | before: 1, 400 | after: 1, 401 | error: 0 402 | }, 403 | { 404 | name: 'all', 405 | children: [], 406 | before: 1, 407 | after: 1, 408 | error: 0 409 | }, 410 | { 411 | name: 'all', 412 | children: [], 413 | before: 0, 414 | after: 0, 415 | error: 0 416 | }, 417 | { 418 | name: 'all', 419 | children: [], 420 | before: 1, 421 | after: 1, 422 | error: 0 423 | }, 424 | { 425 | name: 'all', 426 | children: [], 427 | before: 0, 428 | after: 0, 429 | error: 0 430 | }, 431 | { 432 | name: 'all', 433 | children: [], 434 | before: 1, 435 | after: 1, 436 | error: 0 437 | }], 438 | before: 0, 439 | after: 0, 440 | error: 0 441 | }); 442 | t.end(); 443 | }); 444 | 445 | listener.currentName = 'then continuation'; 446 | }); 447 | }); 448 | 449 | test('Promise.all reject', function allTest(t) { 450 | var listener = addListner(); 451 | 452 | listener.currentName = 'resolve'; 453 | var a = Promise.resolve(123); 454 | listener.currentName = 'reject'; 455 | var b = Promise.reject(456); 456 | listener.currentName = 'all'; 457 | var p = Promise.all([a, b]); 458 | 459 | p.catch(function then(value) { 460 | listener.currentName = 'nextTick'; 461 | process.nextTick(function next() { 462 | // some version of iojs use nextTick for some parts of its async 463 | if (listener.root.children.length === 3) { 464 | expected.children.push({ 465 | name: 'all', 466 | children: [], 467 | before: 1, 468 | after: 1, 469 | error: 0 470 | }) 471 | } 472 | 473 | process.removeAsyncListener(listener.listener); 474 | t.equal(value, 456); 475 | t.deepEqual(listener.root, expected); 476 | t.end(); 477 | }); 478 | 479 | listener.currentName = 'catch continuation'; 480 | }); 481 | 482 | var expected = { 483 | name: 'root', 484 | children: [{ 485 | name: 'resolve', 486 | children: [{ 487 | // Internal continuation of a used for making the race future. 488 | name: 'all', 489 | children: [], 490 | before: 0, 491 | after: 0, 492 | error: 0 493 | }], 494 | before: 1, 495 | after: 1, 496 | error: 0 497 | }, { 498 | name: 'reject', 499 | children: [ 500 | { 501 | name: 'all', 502 | children: [ 503 | { 504 | name: 'nextTick', 505 | children: [], 506 | before: 1, 507 | after: 0, 508 | error: 0 509 | }, 510 | { 511 | name: 'catch continuation', 512 | children: [], 513 | before: 0, 514 | after: 0, 515 | error: 0 516 | } 517 | ], 518 | before: 1, 519 | after: 1, 520 | error: 0 521 | }, 522 | { 523 | // Internal continuation of b used for making the race future. 524 | name: 'all', 525 | children: [], 526 | before: 0, 527 | after: 0, 528 | error: 0 529 | } 530 | ], 531 | before: 1, 532 | after: 1, 533 | error: 0 534 | }, 535 | { 536 | name: 'all', 537 | children: [], 538 | before: 1, 539 | after: 1, 540 | error: 0 541 | }, 542 | { 543 | name: 'all', 544 | children: [], 545 | before: 0, 546 | after: 0, 547 | error: 0 548 | }, 549 | { 550 | name: 'all', 551 | children: [], 552 | before: 0, 553 | after: 0, 554 | error: 0 555 | }, 556 | { 557 | name: 'all', 558 | children: [], 559 | before: 1, 560 | after: 1, 561 | error: 0 562 | }, 563 | { 564 | name: 'all', 565 | children: [], 566 | before: 1, 567 | after: 1, 568 | error: 0 569 | }], 570 | before: 0, 571 | after: 0, 572 | error: 0 573 | } 574 | }); 575 | 576 | test('Promise.race', function raceTest(t) { 577 | var listener = addListner(); 578 | 579 | listener.currentName = 'resolve 1'; 580 | var a = Promise.resolve(123); 581 | listener.currentName = 'resolve 2'; 582 | var b = Promise.resolve(456); 583 | listener.currentName = 'race'; 584 | var p = Promise.race([a, b]); 585 | 586 | p.then(function then(value) { 587 | listener.currentName = 'nextTick'; 588 | process.nextTick(function next() { 589 | process.removeAsyncListener(listener.listener); 590 | t.equal(value, 123); 591 | t.deepEqual(listener.root, { 592 | name: 'root', 593 | children: [{ 594 | name: 'resolve 1', 595 | children: [ 596 | { 597 | name: 'race', 598 | children: [ 599 | { 600 | name: 'nextTick', 601 | children: [], 602 | before: 1, 603 | after: 0, 604 | error: 0 605 | }, 606 | { 607 | name: 'then continuation', 608 | children: [], 609 | before: 0, 610 | after: 0, 611 | error: 0 612 | } 613 | ], 614 | before: 1, 615 | after: 1, 616 | error: 0 617 | }, 618 | { 619 | // Internal continuation of a used for making the race future. 620 | name: 'race', 621 | children: [], 622 | before: 0, 623 | after: 0, 624 | error: 0 625 | } 626 | ], 627 | before: 1, 628 | after: 1, 629 | error: 0 630 | }, { 631 | name: 'resolve 2', 632 | children: [{ 633 | // Internal continuation of b used for making the race future. 634 | name: 'race', 635 | children: [], 636 | before: 0, 637 | after: 0, 638 | error: 0 639 | }], 640 | before: 1, 641 | after: 1, 642 | error: 0 643 | }, 644 | { 645 | name: 'race', 646 | children: [], 647 | before: 1, 648 | after: 1, 649 | error: 0 650 | }, 651 | { 652 | name: 'race', 653 | children: [], 654 | before: 0, 655 | after: 0, 656 | error: 0 657 | }, 658 | { 659 | name: 'race', 660 | children: [], 661 | before: 1, 662 | after: 1, 663 | error: 0 664 | }, 665 | { 666 | name: 'race', 667 | children: [], 668 | before: 0, 669 | after: 0, 670 | error: 0 671 | }, 672 | { 673 | name: 'race', 674 | children: [], 675 | before: 1, 676 | after: 1, 677 | error: 0 678 | }], 679 | before: 0, 680 | after: 0, 681 | error: 0 682 | }); 683 | t.end(); 684 | }); 685 | 686 | listener.currentName = 'then continuation'; 687 | }); 688 | }); 689 | 690 | test('Promise.race - reject', function raceTest(t) { 691 | var listener = addListner(); 692 | 693 | listener.currentName = 'reject'; 694 | var a = Promise.reject(123); 695 | listener.currentName = 'resolve'; 696 | var b = Promise.resolve(456); 697 | listener.currentName = 'race'; 698 | var p = Promise.race([a, b]); 699 | 700 | p.catch(function then(value) { 701 | listener.currentName = 'nextTick'; 702 | process.nextTick(function next() { 703 | process.removeAsyncListener(listener.listener); 704 | t.equal(value, 123); 705 | 706 | // some version of iojs use nextTick for some parts of its async 707 | if (listener.root.children.length === 3) { 708 | expected.children.push({ 709 | name: 'race', 710 | children: [], 711 | before: 1, 712 | after: 1, 713 | error: 0 714 | }) 715 | } 716 | 717 | t.deepEqual(listener.root, expected); 718 | t.end(); 719 | }); 720 | 721 | listener.currentName = 'catch continuation'; 722 | }); 723 | 724 | var expected = { 725 | name: 'root', 726 | children: [{ 727 | name: 'reject', 728 | children: [ 729 | { 730 | name: 'race', 731 | children: [ 732 | { 733 | name: 'nextTick', 734 | children: [], 735 | before: 1, 736 | after: 0, 737 | error: 0 738 | }, 739 | { 740 | name: 'catch continuation', 741 | children: [], 742 | before: 0, 743 | after: 0, 744 | error: 0 745 | } 746 | ], 747 | before: 1, 748 | after: 1, 749 | error: 0 750 | }, 751 | { 752 | // Internal continuation of a used for making the race future. 753 | name: 'race', 754 | children: [], 755 | before: 0, 756 | after: 0, 757 | error: 0 758 | } 759 | ], 760 | before: 1, 761 | after: 1, 762 | error: 0 763 | }, { 764 | name: 'resolve', 765 | children: [{ 766 | // Internal continuation of b used for making the race future. 767 | name: 'race', 768 | children: [], 769 | before: 0, 770 | after: 0, 771 | error: 0 772 | }], 773 | before: 1, 774 | after: 1, 775 | error: 0 776 | }, 777 | { 778 | name: 'race', 779 | children: [], 780 | before: 0, 781 | after: 0, 782 | error: 0 783 | }, 784 | { 785 | name: 'race', 786 | children: [], 787 | before: 1, 788 | after: 1, 789 | error: 0 790 | }, 791 | { 792 | name: 'race', 793 | children: [], 794 | before: 1, 795 | after: 1, 796 | error: 0 797 | }, 798 | { 799 | name: 'race', 800 | children: [], 801 | before: 0, 802 | after: 0, 803 | error: 0 804 | }, 805 | { 806 | name: 'race', 807 | children: [], 808 | before: 1, 809 | after: 1, 810 | error: 0 811 | }], 812 | before: 0, 813 | after: 0, 814 | error: 0 815 | } 816 | }); 817 | 818 | test('instanceof', function diferTest(t) { 819 | var p = Promise.resolve(10); 820 | 821 | t.ok(p instanceof Promise, 'instanceof should work on wrapped Promise'); 822 | t.ok(p instanceof unwrappedPromise, 'instanceof should work on unwrapped Promise'); 823 | t.end() 824 | }); 825 | 826 | test('then chain with promise', function(t) { 827 | var listener = addListner(); 828 | 829 | listener.currentName = 'accept'; 830 | var promise = Promise.resolve(10); 831 | 832 | promise 833 | .then(function(val) { 834 | return new Promise(function wait(accept) { 835 | listener.currentName = 'nextTick in nested promise'; 836 | process.nextTick(function() { 837 | listener.currentName = 'accept from nextTick'; 838 | accept(val); 839 | }); 840 | }); 841 | }) 842 | .then(function validate(val) { 843 | t.strictEqual(val, 10); 844 | t.strictEqual(this, global); 845 | 846 | listener.currentName = 'setTimeout in 2nd then'; 847 | setTimeout(function() { 848 | t.deepEqual(listener.root, expected); 849 | t.end(); 850 | }); 851 | 852 | listener.currentName = '2nd then continuation'; 853 | }); 854 | 855 | process.removeAsyncListener(listener.listener); 856 | 857 | // Promise resolution changed slightly in node v6, 858 | // now resolve/reject wraps again on completion. 859 | var children = [] 860 | if (nodeVersion[0] >= 6) { 861 | children.push({ 862 | name: 'accept from nextTick', 863 | children: [], 864 | before: 0, 865 | after: 0, 866 | error: 0 867 | }) 868 | } 869 | children.push( 870 | { 871 | name: 'setTimeout in 2nd then', 872 | children: [], 873 | before: 1, 874 | after: 0, 875 | error: 0 876 | }, 877 | { 878 | name: '2nd then continuation', 879 | children: [], 880 | before: 0, 881 | after: 0, 882 | error: 0 883 | } 884 | ) 885 | 886 | var expected = { 887 | name: 'root', 888 | children: [ 889 | { 890 | name: 'accept', 891 | children: [ 892 | { 893 | name: 'nextTick in nested promise', 894 | children: [ 895 | { 896 | name: 'accept from nextTick', 897 | children: children, 898 | before: children.length - 1, 899 | after: children.length - 1, 900 | error: 0 901 | } 902 | ], 903 | before: 1, 904 | after: 1, 905 | error: 0 906 | } 907 | ], 908 | before: 1, 909 | after: 1, 910 | error: 0 911 | }, 912 | { 913 | name: 'accept', 914 | children: [], 915 | before: 1, 916 | after: 1, 917 | error: 0 918 | }, 919 | { 920 | name: 'accept', 921 | children: [], 922 | before: 1, 923 | after: 1, 924 | error: 0 925 | } 926 | ], 927 | before: 0, 928 | after: 0, 929 | error: 0 930 | } 931 | }); 932 | 933 | test('then chain with rejected promise', function(t) { 934 | var listener = addListner(); 935 | 936 | listener.currentName = 'reject'; 937 | var promise = Promise.reject(10); 938 | 939 | promise 940 | .then(fail, function(val) { 941 | return new Promise(function wait(accept, reject) { 942 | listener.currentName = 'nextTick in nested promise'; 943 | process.nextTick(function() { 944 | listener.currentName = 'reject from nextTick'; 945 | reject(val); 946 | }); 947 | }); 948 | }) 949 | .then(fail, function validate(val) { 950 | t.strictEqual(val, 10); 951 | t.strictEqual(this, global); 952 | 953 | listener.currentName = 'setTimeout in 2nd then'; 954 | setTimeout(function() { 955 | // some version of iojs use nextTick for some parts of its async 956 | if (listener.root.children.length === 2) { 957 | expected.children.splice(1, 0, { 958 | name: 'reject', 959 | children: [], 960 | before: 1, 961 | after: 1, 962 | error: 0 963 | }) 964 | } 965 | 966 | t.deepEqual(listener.root, expected); 967 | t.end(); 968 | }); 969 | 970 | listener.currentName = '2nd then continuation'; 971 | }); 972 | 973 | function fail() { 974 | t.fail('should not be called'); 975 | t.end(); 976 | } 977 | 978 | process.removeAsyncListener(listener.listener); 979 | 980 | // Promise resolution changed slightly in node v6, 981 | // now resolve/reject wraps again on completion. 982 | var children = [] 983 | if (nodeVersion[0] >= 6) { 984 | children.push({ 985 | name: 'reject from nextTick', 986 | children: [], 987 | before: 0, 988 | after: 0, 989 | error: 0 990 | }) 991 | } 992 | children.push( 993 | { 994 | name: 'setTimeout in 2nd then', 995 | children: [], 996 | before: 1, 997 | after: 0, 998 | error: 0 999 | }, 1000 | { 1001 | name: '2nd then continuation', 1002 | children: [], 1003 | before: 0, 1004 | after: 0, 1005 | error: 0 1006 | } 1007 | ) 1008 | 1009 | var expected = { 1010 | name: 'root', 1011 | children: [ 1012 | { 1013 | name: 'reject', 1014 | children: [ 1015 | { 1016 | name: 'nextTick in nested promise', 1017 | children: [ 1018 | { 1019 | name: 'reject from nextTick', 1020 | children: children, 1021 | before: children.length - 1, 1022 | after: children.length - 1, 1023 | error: 0 1024 | } 1025 | ], 1026 | before: 1, 1027 | after: 1, 1028 | error: 0 1029 | } 1030 | ], 1031 | before: 1, 1032 | after: 1, 1033 | error: 0 1034 | }, 1035 | { 1036 | name: 'reject', 1037 | children: [], 1038 | before: 0, 1039 | after: 0, 1040 | error: 0 1041 | }, 1042 | { 1043 | name: 'reject', 1044 | children: [], 1045 | before: 1, 1046 | after: 1, 1047 | error: 0 1048 | }, 1049 | { 1050 | name: 'reject', 1051 | children: [], 1052 | before: 0, 1053 | after: 0, 1054 | error: 0 1055 | }, 1056 | { 1057 | name: 'reject', 1058 | children: [], 1059 | before: 1, 1060 | after: 1, 1061 | error: 0 1062 | } 1063 | ], 1064 | before: 0, 1065 | after: 0, 1066 | error: 0 1067 | } 1068 | }); 1069 | 1070 | test('multi catch with promise', function(t) { 1071 | var listener = addListner(); 1072 | 1073 | listener.currentName = 'reject'; 1074 | var promise = Promise.reject(10); 1075 | 1076 | promise 1077 | .catch(function(val) { 1078 | return new Promise(function wait(accept, reject) { 1079 | listener.currentName = 'nextTick in nested promise'; 1080 | process.nextTick(function() { 1081 | listener.currentName = 'reject from nextTick'; 1082 | reject(val); 1083 | }); 1084 | }); 1085 | }) 1086 | .catch(function validate(val) { 1087 | t.strictEqual(val, 10); 1088 | t.strictEqual(this, global); 1089 | 1090 | listener.currentName = 'setTimeout in 2nd catch'; 1091 | setTimeout(function() { 1092 | // some version of iojs use nextTick for some parts of its async 1093 | if (listener.root.children.length === 2) { 1094 | expected.children.splice(1, 0, { 1095 | name: 'reject', 1096 | children: [], 1097 | before: 1, 1098 | after: 1, 1099 | error: 0 1100 | }) 1101 | } 1102 | 1103 | t.deepEqual(listener.root, expected); 1104 | t.end(); 1105 | }); 1106 | 1107 | listener.currentName = '2nd catch continuation'; 1108 | }); 1109 | 1110 | process.removeAsyncListener(listener.listener); 1111 | 1112 | // Promise resolution changed slightly in node v6, 1113 | // now resolve/reject wraps again on completion. 1114 | var children = [] 1115 | if (nodeVersion[0] >= 6) { 1116 | children.push({ 1117 | name: 'reject from nextTick', 1118 | children: [], 1119 | before: 0, 1120 | after: 0, 1121 | error: 0 1122 | }) 1123 | } 1124 | children.push( 1125 | { 1126 | name: 'setTimeout in 2nd catch', 1127 | children: [], 1128 | before: 1, 1129 | after: 0, 1130 | error: 0 1131 | }, 1132 | { 1133 | name: '2nd catch continuation', 1134 | children: [], 1135 | before: 0, 1136 | after: 0, 1137 | error: 0 1138 | } 1139 | ) 1140 | 1141 | var expected = { 1142 | name: 'root', 1143 | children: [ 1144 | { 1145 | name: 'reject', 1146 | children: [ 1147 | { 1148 | name: 'nextTick in nested promise', 1149 | children: [ 1150 | { 1151 | name: 'reject from nextTick', 1152 | children: children, 1153 | before: children.length - 1, 1154 | after: children.length - 1, 1155 | error: 0 1156 | } 1157 | ], 1158 | before: 1, 1159 | after: 1, 1160 | error: 0 1161 | } 1162 | ], 1163 | before: 1, 1164 | after: 1, 1165 | error: 0 1166 | }, 1167 | { 1168 | name: 'reject', 1169 | children: [], 1170 | before: 1, 1171 | after: 1, 1172 | error: 0 1173 | }, 1174 | { 1175 | name: 'reject', 1176 | children: [], 1177 | before: 1, 1178 | after: 1, 1179 | error: 0 1180 | } 1181 | ], 1182 | before: 0, 1183 | after: 0, 1184 | error: 0 1185 | } 1186 | }); 1187 | 1188 | test('throw in executor', function(t) { 1189 | var listener = addListner(); 1190 | 1191 | var promise = new Promise(function unsafe() { 1192 | listener.currentName = 'throw'; 1193 | throw 10; 1194 | }); 1195 | 1196 | promise.catch(function(val) { 1197 | t.equal(val, 10, 'should match thrown value') 1198 | if (listener.root.children.length === 2) { 1199 | expected.children.splice(1, 0, { 1200 | name: 'throw', 1201 | children: [], 1202 | before: 1, 1203 | after: 0, 1204 | error: 0 1205 | }) 1206 | } 1207 | 1208 | t.deepEqual(listener.root, expected); 1209 | t.end(); 1210 | }); 1211 | 1212 | process.removeAsyncListener(listener.listener); 1213 | 1214 | var expected = { 1215 | name: 'root', 1216 | children: [ 1217 | { 1218 | name: 'throw', 1219 | children: [ 1220 | ], 1221 | before: 1, 1222 | after: 0, 1223 | error: 0 1224 | } 1225 | ], 1226 | before: 0, 1227 | after: 0, 1228 | error: 0 1229 | } 1230 | }); 1231 | 1232 | test('Promise.resolve().catch().then()', function (t) { 1233 | var listenerState = addListner(); 1234 | 1235 | t.plan(1); 1236 | listenerState.currentName = 'resolve' 1237 | var p = Promise.resolve(1) 1238 | 1239 | listenerState.currentName = 'return of 1st catch that didnt get run' 1240 | p = p.catch(function () {}) 1241 | 1242 | p = p.then(function () { 1243 | listenerState.currentName = 'returned by 1st then' 1244 | throw new Error() 1245 | }) 1246 | 1247 | p = p.catch(function () { 1248 | listenerState.currentName = 'returned by 2nd catch' 1249 | throw new Error 1250 | }); 1251 | 1252 | p = p.then(function () {}, function () { 1253 | listenerState.currentName = 'returned by 2nd then' 1254 | throw new Error() 1255 | }); 1256 | 1257 | p = p.catch(function () { 1258 | t.deepEqual(listenerState.root, expected); 1259 | }); 1260 | 1261 | var expected = { 1262 | name: 'root', 1263 | children: [ 1264 | { 1265 | name: 'resolve', 1266 | children: [ 1267 | { 1268 | name: 'return of 1st catch that didnt get run', 1269 | children: [ 1270 | { 1271 | name: 'returned by 1st then', 1272 | children: [ 1273 | { 1274 | name: 'returned by 2nd catch', 1275 | children: [ 1276 | { 1277 | name: 'returned by 2nd then', 1278 | children: [], 1279 | before: 1, 1280 | after: 0, 1281 | error: 0 1282 | } 1283 | ], 1284 | before: 1, 1285 | after: 1, 1286 | error: 0 1287 | } 1288 | ], 1289 | before: 1, 1290 | after: 1, 1291 | error: 0 1292 | } 1293 | ], 1294 | before: 1, 1295 | after: 1, 1296 | error: 0 1297 | } 1298 | ], 1299 | before: 1, 1300 | after: 1, 1301 | error: 0 1302 | }, 1303 | { 1304 | name: 'return of 1st catch that didnt get run', 1305 | children: [], 1306 | before: 0, 1307 | after: 0, 1308 | error: 0 1309 | }, 1310 | { 1311 | name: 'return of 1st catch that didnt get run', 1312 | children: [], 1313 | before: 1, 1314 | after: 0, 1315 | error: 0 1316 | }, 1317 | { 1318 | name: 'return of 1st catch that didnt get run', 1319 | children: [], 1320 | before: 1, 1321 | after: 0, 1322 | error: 0 1323 | }, 1324 | { 1325 | name: 'return of 1st catch that didnt get run', 1326 | children: [], 1327 | before: 0, 1328 | after: 0, 1329 | error: 0 1330 | }, 1331 | { 1332 | name: 'return of 1st catch that didnt get run', 1333 | children: [], 1334 | before: 1, 1335 | after: 0, 1336 | error: 0 1337 | }, 1338 | { 1339 | name: 'return of 1st catch that didnt get run', 1340 | children: [], 1341 | before: 1, 1342 | after: 0, 1343 | error: 0 1344 | } 1345 | ], 1346 | before: 0, 1347 | after: 0, 1348 | error: 0 1349 | } 1350 | 1351 | process.removeAsyncListener(listenerState.listener); 1352 | }); 1353 | 1354 | test('continue from unwrapped promise', function(t) { 1355 | var listener = addListner(); 1356 | 1357 | listener.currentName = 'resolve'; 1358 | resolvedBeforeWrap.then(function(val) { 1359 | t.equal(val, 123, 'should match resolved value'); 1360 | listener.currentName = '2nd resolve'; 1361 | return 456; 1362 | }).then(function (val) { 1363 | t.equal(val, 456, 'should match resolved value'); 1364 | t.deepEqual(listener.root, expected); 1365 | t.end(); 1366 | }); 1367 | 1368 | process.removeAsyncListener(listener.listener); 1369 | 1370 | var expected = { 1371 | name: 'root', 1372 | children: [{ 1373 | name : 'resolve', 1374 | children : [{ 1375 | name : '2nd resolve', 1376 | children : [], 1377 | before : 1, 1378 | after : 0, 1379 | error : 0 1380 | }], 1381 | before : 1, 1382 | after : 1, 1383 | error : 0 1384 | }, 1385 | { 1386 | name : 'resolve', 1387 | children : [], 1388 | before : 1, 1389 | after : 0, 1390 | error : 0 1391 | }], 1392 | before: 0, 1393 | after: 0, 1394 | error: 0 1395 | }; 1396 | }); 1397 | 1398 | test('return unwrapped promise', function(t) { 1399 | var listener = addListner(); 1400 | 1401 | listener.currentName = 'resolve'; 1402 | Promise.resolve(890).then(function (val) { 1403 | t.equal(val, 890, 'should match resolved value'); 1404 | return resolvedBeforeWrap; 1405 | }).then(function(val) { 1406 | t.equal(val, 123, 'should match resolved value'); 1407 | return 456; 1408 | }).then(function (val) { 1409 | t.equal(val, 456, 'should match resolved value'); 1410 | t.deepEqual(listener.root, expected); 1411 | t.end(); 1412 | }); 1413 | 1414 | process.removeAsyncListener(listener.listener); 1415 | 1416 | var expected = { 1417 | name: 'root', 1418 | children: [{ 1419 | name : 'resolve', 1420 | children : [], 1421 | before : 1, 1422 | after : 1, 1423 | error : 0 1424 | }, 1425 | { 1426 | name : 'resolve', 1427 | children : [], 1428 | before : 1, 1429 | after : 1, 1430 | error : 0 1431 | }, 1432 | { 1433 | name : 'resolve', 1434 | children : [{ 1435 | name : 'resolve', 1436 | children : [], 1437 | before : 1, 1438 | after : 0, 1439 | error : 0 1440 | }], 1441 | before : 1, 1442 | after : 1, 1443 | error : 0 1444 | }, 1445 | { 1446 | name : 'resolve', 1447 | children : [], 1448 | before : 1, 1449 | after : 0, 1450 | error : 0 1451 | }], 1452 | before: 0, 1453 | after: 0, 1454 | error: 0 1455 | }; 1456 | }); 1457 | 1458 | test('resume context after unwrapped promise', function(t) { 1459 | var listener = addListner(); 1460 | 1461 | listener.currentName = 'resolve'; 1462 | var wrapped = Promise.resolve(456); 1463 | 1464 | listener.currentName = 'unwrapped resolve'; 1465 | resolvedBeforeWrap.then(function(val) { 1466 | t.equal(val, 123, 'should match resolved value'); 1467 | listener.currentName = 'maybe internal resolve'; 1468 | return wrapped 1469 | }).then(function (val) { 1470 | t.equal(val, 456, 'should match resolved value'); 1471 | listener.currentName = 'return after continuing from wrapped promise'; 1472 | return 89 1473 | }).then(function (val) { 1474 | t.equal(val, 89, 'should match resolved value'); 1475 | t.deepEqual(listener.root, expected); 1476 | t.end(); 1477 | }); 1478 | 1479 | process.removeAsyncListener(listener.listener); 1480 | 1481 | // Promise resolution changed slightly in node v6, 1482 | // now resolve/reject wraps again on completion. 1483 | var children = [] 1484 | if (nodeVersion[0] >= 6) { 1485 | children.push({ 1486 | name : 'maybe internal resolve', 1487 | children : [], 1488 | before : 0, 1489 | after : 0, 1490 | error : 0 1491 | }) 1492 | } 1493 | children.push({ 1494 | name : 'return after continuing from wrapped promise', 1495 | children : [], 1496 | before : 1, 1497 | after : 0, 1498 | error : 0 1499 | }) 1500 | 1501 | var expected = { 1502 | name: 'root', 1503 | children: [{ 1504 | name : 'resolve', 1505 | children : children, 1506 | before : children.length, 1507 | after : children.length, 1508 | error : 0 1509 | }, 1510 | { 1511 | name : 'unwrapped resolve', 1512 | children : [], 1513 | before : 1, 1514 | after : 1, 1515 | error : 0 1516 | }, 1517 | { 1518 | name : 'unwrapped resolve', 1519 | children : [], 1520 | before : 1, 1521 | after : 1, 1522 | error : 0 1523 | }, 1524 | { 1525 | name : 'unwrapped resolve', 1526 | children : [], 1527 | before : 1, 1528 | after : 0, 1529 | error : 0 1530 | }], 1531 | before: 0, 1532 | after: 0, 1533 | error: 0 1534 | }; 1535 | }); 1536 | 1537 | function addListner() { 1538 | var listener = process.addAsyncListener({ 1539 | create: create, 1540 | before: before, 1541 | after: after, 1542 | error: error 1543 | }); 1544 | 1545 | 1546 | var state = { 1547 | listener: listener, 1548 | currentName: 'root' 1549 | }; 1550 | 1551 | state.root = create(); 1552 | state.current = state.root; 1553 | 1554 | return state; 1555 | 1556 | function create () { 1557 | var node = { 1558 | name: state.currentName, 1559 | children: [], 1560 | before: 0, 1561 | after: 0, 1562 | error: 0 1563 | }; 1564 | 1565 | if(state.current) state.current.children.push(node); 1566 | return node; 1567 | } 1568 | 1569 | function before(ctx, node) { 1570 | state.current = node; 1571 | state.current.before++; 1572 | } 1573 | 1574 | function after(ctx, node) { 1575 | node.after++; 1576 | state.current = null; 1577 | } 1578 | 1579 | function error(ctx, node) { 1580 | node.error++; 1581 | state.current = null; 1582 | return false; 1583 | } 1584 | } 1585 | 1586 | // for the following, 1587 | // 1588 | // https://github.com/v8/v8/commits/master/src/js/promise-extra.js 1589 | // 1590 | // is helpful context -- none of these are part of ES2015 promises, and were 1591 | // set up to be removed. 1592 | 1593 | // Node.js = 6) { 1992 | children.push({ 1993 | name: 'accept from nextTick', 1994 | children: [], 1995 | before: 0, 1996 | after: 0, 1997 | error: 0 1998 | }) 1999 | } 2000 | children.push( 2001 | { 2002 | name: 'setTimeout in 2nd chain', 2003 | children: [], 2004 | before: 1, 2005 | after: 0, 2006 | error: 0 2007 | }, 2008 | { 2009 | name: '2nd then continuation', 2010 | children: [], 2011 | before: 0, 2012 | after: 0, 2013 | error: 0 2014 | } 2015 | ) 2016 | 2017 | var expected = { 2018 | name: 'root', 2019 | children: [ 2020 | { 2021 | name: 'accept', 2022 | children: [ 2023 | { 2024 | name: 'nextTick in nested promise', 2025 | children: [ 2026 | { 2027 | name: 'accept from nextTick', 2028 | children: children, 2029 | before: children.length - 1, 2030 | after: children.length - 1, 2031 | error: 0 2032 | } 2033 | ], 2034 | before: 1, 2035 | after: 1, 2036 | error: 0 2037 | } 2038 | ], 2039 | before: 1, 2040 | after: 1, 2041 | error: 0 2042 | }, 2043 | { 2044 | name: 'accept', 2045 | children: [], 2046 | before: 1, 2047 | after: 1, 2048 | error: 0 2049 | }, 2050 | { 2051 | name: 'accept', 2052 | children: [], 2053 | before: 1, 2054 | after: 1, 2055 | error: 0 2056 | } 2057 | ], 2058 | before: 0, 2059 | after: 0, 2060 | error: 0 2061 | } 2062 | }); 2063 | 2064 | test('multi chain with rejected promise', function(t) { 2065 | var listener = addListner(); 2066 | 2067 | listener.currentName = 'reject'; 2068 | var promise = Promise.reject(10); 2069 | 2070 | promise 2071 | .chain(fail, function(val) { 2072 | return new Promise(function wait(accept, reject) { 2073 | listener.currentName = 'nextTick in nested promise'; 2074 | process.nextTick(function() { 2075 | listener.currentName = 'reject from nextTick'; 2076 | reject(val); 2077 | }); 2078 | }); 2079 | }) 2080 | .chain(fail, function validate(val) { 2081 | t.strictEqual(val, 10); 2082 | t.strictEqual(this, global); 2083 | 2084 | listener.currentName = 'setTimeout in 2nd chain'; 2085 | setTimeout(function() { 2086 | // some version of iojs use nextTick for some parts of its async 2087 | if (listener.root.children.length === 2) { 2088 | expected.children.splice(1, 0, { 2089 | name: 'reject', 2090 | children: [], 2091 | before: 1, 2092 | after: 1, 2093 | error: 0 2094 | }) 2095 | } 2096 | 2097 | t.deepEqual(listener.root, expected); 2098 | t.end(); 2099 | }); 2100 | 2101 | listener.currentName = '2nd chain continuation'; 2102 | }); 2103 | 2104 | function fail() { 2105 | t.fail('should not be called'); 2106 | t.end(); 2107 | } 2108 | 2109 | process.removeAsyncListener(listener.listener); 2110 | 2111 | // Promise resolution changed slightly in node v6, 2112 | // now resolve/reject wraps again on completion. 2113 | var children = [] 2114 | if (nodeVersion[0] >= 6) { 2115 | children.push({ 2116 | name: 'reject from nextTick', 2117 | children: [], 2118 | before: 0, 2119 | after: 0, 2120 | error: 0 2121 | }) 2122 | } 2123 | children.push( 2124 | { 2125 | name: 'setTimeout in 2nd chain', 2126 | children: [], 2127 | before: 1, 2128 | after: 0, 2129 | error: 0 2130 | }, 2131 | { 2132 | name: '2nd chain continuation', 2133 | children: [], 2134 | before: 0, 2135 | after: 0, 2136 | error: 0 2137 | } 2138 | ) 2139 | 2140 | var expected = { 2141 | name: 'root', 2142 | children: [ 2143 | { 2144 | name: 'reject', 2145 | children: [ 2146 | { 2147 | name: 'nextTick in nested promise', 2148 | children: [ 2149 | { 2150 | name: 'reject from nextTick', 2151 | children: children, 2152 | before: children.length - 1, 2153 | after: children.length - 1, 2154 | error: 0 2155 | } 2156 | ], 2157 | before: 1, 2158 | after: 1, 2159 | error: 0 2160 | } 2161 | ], 2162 | before: 1, 2163 | after: 1, 2164 | error: 0 2165 | }, 2166 | { 2167 | name: 'reject', 2168 | children: [], 2169 | before: 0, 2170 | after: 0, 2171 | error: 0 2172 | }, 2173 | { 2174 | name: 'reject', 2175 | children: [], 2176 | before: 1, 2177 | after: 1, 2178 | error: 0 2179 | }, 2180 | { 2181 | name: 'reject', 2182 | children: [], 2183 | before: 0, 2184 | after: 0, 2185 | error: 0 2186 | }, 2187 | { 2188 | name: 'reject', 2189 | children: [], 2190 | before: 1, 2191 | after: 1, 2192 | error: 0 2193 | } 2194 | ], 2195 | before: 0, 2196 | after: 0, 2197 | error: 0 2198 | } 2199 | }); 2200 | } 2201 | 2202 | test('es6 subclasses', function(t) { 2203 | if (nodeVersion[0] < 6) { 2204 | t.pass('class syntax is not supported before node 6'); 2205 | t.end(); 2206 | return; 2207 | } 2208 | 2209 | // Promise subclasses do 2 asserts per promise. 2210 | t.plan(13); 2211 | 2212 | var SubclassedPromise = require('./promise-subclass.js'); 2213 | var StandardSubclassedPromise = SubclassedPromise(t, false); 2214 | var SubclassedPromiseCustomSpecies = SubclassedPromise(t, true); 2215 | 2216 | var s = StandardSubclassedPromise.resolve(42).then(function(val) { 2217 | t.strictEqual(val, 42); 2218 | t.end(); 2219 | }); 2220 | 2221 | var p1 = 2222 | new StandardSubclassedPromise(function(resolve) { resolve(123); }) 2223 | .then(function() {}); 2224 | t.ok(p1 instanceof StandardSubclassedPromise, 2225 | 'should be StandardSubclassedPromise instance'); 2226 | t.ok(p1 instanceof unwrappedPromise, 'should be unwrappedPromise instance'); 2227 | t.ok(p1 instanceof Promise, 'should be base Promise instance'); 2228 | 2229 | var p2 = 2230 | new SubclassedPromiseCustomSpecies(function(resolve) { resolve(123); }) 2231 | .then(function() {}); 2232 | t.notOk(p2 instanceof SubclassedPromiseCustomSpecies, 2233 | 'should not be SubclassedPromiseCustomSpecies instance'); 2234 | t.ok(p2 instanceof unwrappedPromise, 'should be unwrappedPromise instance'); 2235 | t.ok(p2 instanceof Promise, 'should be base Promise instance'); 2236 | }); 2237 | -------------------------------------------------------------------------------- /test/no-after-following-error.tap.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test; 2 | 3 | if (!global.setImmediate) global.setImmediate = setTimeout; 4 | 5 | test("after handler not run on throw", function (t) { 6 | t.plan(2); 7 | 8 | if (!process.addAsyncListener) require('../index.js'); 9 | 10 | var key = process.createAsyncListener( 11 | { 12 | create : function () { return {}; }, 13 | after : function asyncAfter() { t.fail("after was called"); }, 14 | error : function asyncError(domain) { t.ok(domain, "got error"); } 15 | } 16 | ); 17 | 18 | process.addAsyncListener(key); 19 | 20 | setImmediate(function () { 21 | throw new Error('whoops'); 22 | }); 23 | 24 | function handler(err) { 25 | process.removeAsyncListener(key); 26 | process.removeListener('uncaughtException', handler); 27 | t.ok(err, "error was propagated"); 28 | } 29 | 30 | process.on('uncaughtException', handler); 31 | }); 32 | -------------------------------------------------------------------------------- /test/overlapping-nexttick.tap.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test 2 | , assert = require('assert') 3 | ; 4 | 5 | if (!global.setImmediate) global.setImmediate = setTimeout; 6 | 7 | /** 8 | * 9 | * 10 | * 11 | * 12 | * SETUP AND BOILERPLATE 13 | * 14 | * 15 | * 16 | */ 17 | if (!process.addAsyncListener) require('../index.js'); 18 | 19 | /* 20 | * CLS code 21 | */ 22 | function Namespace () { 23 | this.active = Object.create(null); 24 | this._stack = []; 25 | this.id = null; 26 | } 27 | 28 | Namespace.prototype.set = function (key, value) { 29 | this.active[key] = value; 30 | return value; 31 | }; 32 | 33 | Namespace.prototype.get = function (key) { 34 | return this.active[key]; 35 | }; 36 | 37 | Namespace.prototype.enter = function (context) { 38 | assert.ok(context, "context must be provided for entering"); 39 | 40 | this._stack.push(this.active); 41 | this.active = context; 42 | }; 43 | 44 | Namespace.prototype.exit = function (context) { 45 | assert.ok(context, "context must be provided for exiting"); 46 | 47 | if (this.active === context) { 48 | assert.ok(this._stack.length, "can't remove top context"); 49 | this.active = this._stack.pop(); 50 | return; 51 | } 52 | 53 | var index = this._stack.lastIndexOf(context); 54 | 55 | assert.ok(index >= 0, "context not currently entered; can't exit"); 56 | assert.ok(index, "can't remove top context"); 57 | 58 | this.active = this._stack[index - 1]; 59 | this._stack.length = index - 1; 60 | }; 61 | 62 | Namespace.prototype.createContext = function () { 63 | return Object.create(this.active); 64 | }; 65 | 66 | Namespace.prototype.run = function (fn) { 67 | var context = this.createContext(); 68 | this.enter(context); 69 | try { 70 | fn(context); 71 | return context; 72 | } 73 | finally { 74 | this.exit(context); 75 | } 76 | }; 77 | 78 | Namespace.prototype.bind = function (fn, context) { 79 | if (!context) context = this.active; 80 | var self = this; 81 | return function () { 82 | self.enter(context); 83 | try { 84 | return fn.apply(this, arguments); 85 | } 86 | finally { 87 | self.exit(context); 88 | } 89 | }; 90 | }; 91 | 92 | function create(name) { 93 | assert.ok(name, "namespace must be given a name!"); 94 | 95 | var namespace = new Namespace(name); 96 | namespace.id = process.addAsyncListener( 97 | { 98 | create : function () { return namespace.active; }, 99 | before : function (context, domain) { namespace.enter(domain); }, 100 | after : function (context, domain) { namespace.exit(domain); } 101 | } 102 | ); 103 | 104 | return namespace; 105 | } 106 | 107 | /* 108 | * Transaction code 109 | */ 110 | var id = 1337; 111 | function Transaction() { this.id = id++; } 112 | 113 | /* 114 | * Tracer code 115 | */ 116 | var namespace = create("__NR_tracer"); 117 | function getTransaction() { 118 | var state = namespace.get('state'); 119 | if (state) return state.transaction; 120 | } 121 | 122 | function transactionProxy(handler) { 123 | return function wrapTransactionInvocation() { 124 | var state = {transaction : new Transaction()}; 125 | 126 | var context = namespace.createContext(); 127 | context.state = state; 128 | 129 | return namespace.bind(handler, context).apply(this, arguments); 130 | }; 131 | } 132 | 133 | 134 | /** 135 | * 136 | * 137 | * 138 | * 139 | * TESTS 140 | * 141 | * 142 | * 143 | */ 144 | 145 | test("overlapping requests", function (t) { 146 | t.plan(2); 147 | 148 | t.test("simple overlap", function (t) { 149 | t.plan(3); 150 | 151 | setImmediate(function () { console.log('!'); }); 152 | 153 | var n = create("test2"); 154 | t.ok(!n.get('state'), "state should not yet be visible"); 155 | 156 | n.run(function () { 157 | n.set('state', true); 158 | t.ok(n.get('state'), "state should be visible"); 159 | 160 | setImmediate(function () { t.ok(n.get('state'), "state should be visible"); }); 161 | }); 162 | }); 163 | 164 | t.test("two process.nextTicks", function (t) { 165 | t.plan(6); 166 | 167 | function handler(id) { 168 | var transaction = getTransaction(); 169 | t.ok(transaction, "transaction should be visible"); 170 | t.equal((transaction || {}).id, id, "transaction matches"); 171 | } 172 | 173 | t.ok(!getTransaction(), "transaction should not yet be visible"); 174 | 175 | var first; 176 | var proxied = transactionProxy(function () { 177 | t.ok(getTransaction(), "transaction should be visible"); 178 | 179 | first = getTransaction().id; 180 | process.nextTick(function () { handler(first); }, 42); 181 | }); 182 | proxied(); 183 | 184 | process.nextTick(transactionProxy(function () { 185 | t.ok(getTransaction(), "transaction should be visible"); 186 | 187 | var second = getTransaction().id; 188 | t.notEqual(first, second, "different transaction IDs"); 189 | process.nextTick(function () { handler(second); }, 42); 190 | }), 42); 191 | }); 192 | }); 193 | -------------------------------------------------------------------------------- /test/promise-subclass.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (tap, customSpecies) => { 4 | if (customSpecies) { 5 | return class SubclassedPromise extends Promise { 6 | static get [Symbol.species]() { return Promise; } 7 | then(onSuccess, onReject) { 8 | tap.type(onSuccess, 'function'); 9 | tap.type(onReject, 'undefined'); 10 | return super.then(onSuccess, onReject); 11 | } 12 | }; 13 | } else { 14 | return class SubclassedPromise extends Promise { 15 | then(onSuccess, onReject) { 16 | tap.type(onSuccess, 'function'); 17 | tap.type(onReject, 'undefined'); 18 | return super.then(onSuccess, onReject); 19 | } 20 | }; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /test/simple-counter-with-io.tap.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test; 2 | 3 | if (!global.setImmediate) global.setImmediate = setTimeout; 4 | 5 | test("asyncListeners work as expected with process.nextTick", function (t) { 6 | t.plan(1); 7 | 8 | if (!process.addAsyncListener) require('../index.js'); 9 | 10 | console.log('+'); 11 | // comment out this line to get the expected result: 12 | setImmediate(function () { console.log('!'); }); 13 | 14 | var counter = 1; 15 | var current; 16 | process.addAsyncListener( 17 | { 18 | create : function listener() { return counter++; }, 19 | before : function (_, domain) { current = domain; }, 20 | after : function () { current = null; } 21 | } 22 | ); 23 | 24 | setImmediate(function () { t.equal(current, 1); }); 25 | // uncomment this line to get the expected result: 26 | // process.removeAsyncListener(id); 27 | }); 28 | 29 | -------------------------------------------------------------------------------- /test/simple-counter.tap.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test; 2 | 3 | test("asyncListeners work as expected with process.nextTick", function (t) { 4 | t.plan(4); 5 | 6 | if (!process.addAsyncListener) require('../index.js'); 7 | 8 | var active 9 | , cntr = 0 10 | ; 11 | 12 | process.addAsyncListener( 13 | { 14 | create : function () { return { val : ++cntr }; }, 15 | before : function (context, data) { active = data.val; }, 16 | after : function () { active = null; } 17 | } 18 | ); 19 | 20 | process.nextTick(function () { 21 | t.equal(active, 1); 22 | process.nextTick(function () { t.equal(active, 3); }); 23 | }); 24 | 25 | process.nextTick(function () { 26 | t.equal(active, 2); 27 | process.nextTick(function () { t.equal(active, 4); }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/simplified-error.simple.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | if (!process.addAsyncListener) require('../index.js'); 23 | 24 | var assert = require('assert'); 25 | var fs = require('fs'); 26 | var addListener = process.addAsyncListener; 27 | var removeListener = process.removeAsyncListener; 28 | 29 | var caught = 0; 30 | var expectCaught = 0; 31 | 32 | var callbacksObj = { 33 | error: function(domain, er) { 34 | caught++; 35 | 36 | switch (er.message) { 37 | case 'fs - nested file does not exist': 38 | return true; 39 | 40 | default: 41 | return false; 42 | } 43 | } 44 | }; 45 | 46 | process.on('exit', function() { 47 | console.log('caught:', caught); 48 | console.log('expected:', expectCaught); 49 | assert.equal(caught, expectCaught, 'caught all expected errors'); 50 | console.log('ok'); 51 | }); 52 | 53 | var listener = process.createAsyncListener(callbacksObj); 54 | 55 | // Nested FS 56 | process.nextTick(function() { 57 | addListener(listener); 58 | 59 | setTimeout(function() { 60 | fs.stat('does not exist', function() { 61 | throw new Error('fs - nested file does not exist'); 62 | }); 63 | expectCaught++; 64 | }); 65 | 66 | removeListener(listener); 67 | }); 68 | -------------------------------------------------------------------------------- /test/spawn.tap.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test 2 | , assert = require('assert') 3 | ; 4 | 5 | if (!global.setImmediate) global.setImmediate = setTimeout; 6 | 7 | if (!process.addAsyncListener) require('../index.js'); 8 | 9 | var childProcess = require('child_process') 10 | , exec = childProcess.exec 11 | , execFile = childProcess.execFile 12 | , spawn = childProcess.spawn 13 | ; 14 | 15 | test('ChildProcess', function (t) { 16 | t.plan(3); 17 | 18 | t.test('exec', function (t) { 19 | t.plan(3); 20 | 21 | var active 22 | , cntr = 0 23 | ; 24 | 25 | process.addAsyncListener( 26 | { 27 | create : function () { return { val : ++cntr }; }, 28 | before : function (context, data) { active = data.val; }, 29 | after : function () { active = null; } 30 | } 31 | ); 32 | 33 | t.equal(active, undefined, 34 | 'starts in initial context'); 35 | process.nextTick(function () { 36 | t.equal(active, 1, 37 | 'after tick: 1st context'); 38 | var child = exec('node --version'); 39 | child.on('exit', function (code) { 40 | t.ok(active >= 2, 41 | 'after exec#exit: entered additional contexts'); 42 | }) 43 | }); 44 | }); 45 | 46 | t.test('execFile', function (t) { 47 | t.plan(3); 48 | 49 | var active 50 | , cntr = 0 51 | ; 52 | 53 | process.addAsyncListener( 54 | { 55 | create : function () { return { val : ++cntr }; }, 56 | before : function (context, data) { active = data.val; }, 57 | after : function () { active = null; } 58 | } 59 | ); 60 | 61 | t.equal(active, undefined, 62 | 'starts in initial context'); 63 | process.nextTick(function () { 64 | t.equal(active, 1, 65 | 'after nextTick: 1st context'); 66 | execFile('node', ['--version'], function (err, code) { 67 | t.ok(active >= 2, 68 | 'after execFile: entered additional contexts'); 69 | }); 70 | }); 71 | }); 72 | 73 | t.test('spawn', function (t) { 74 | t.plan(3); 75 | 76 | var active 77 | , cntr = 0 78 | ; 79 | 80 | process.addAsyncListener( 81 | { 82 | create : function () { return { val : ++cntr }; }, 83 | before : function (context, data) { active = data.val; }, 84 | after : function () { active = null; } 85 | } 86 | ); 87 | 88 | t.equal(active, undefined, 89 | 'starts in initial context'); 90 | process.nextTick(function () { 91 | t.equal(active, 1, 92 | 'after tick: 1st context'); 93 | var child = spawn('node', ['--version']); 94 | child.on('exit', function (code) { 95 | t.ok(active >= 2, 96 | 'after spawn#exit: entered additional contexts'); 97 | }) 98 | }); 99 | }); 100 | }); 101 | -------------------------------------------------------------------------------- /test/timers.tap.js: -------------------------------------------------------------------------------- 1 | if (!process.addAsyncListener) require('../index.js'); 2 | 3 | var test = require('tap').test; 4 | var net = require('net'); 5 | 6 | test('test process.nextTick', function (t) { 7 | test_helper(t, function (listener, done) { 8 | listener.currentName = 'process.nextTick'; 9 | process.nextTick(done); 10 | }, { 11 | name: 'root', 12 | children: [ 13 | { 14 | name: 'process.nextTick', 15 | children: [], 16 | before: 1, 17 | after: 1, 18 | error: 0 19 | } 20 | ], 21 | before: 0, 22 | after: 0, 23 | error: 0 24 | }); 25 | }); 26 | 27 | test('test setTimeout', function (t) { 28 | test_helper(t, function (listener, done) { 29 | listener.currentName = 'setTimeout'; 30 | setTimeout(done, 1); 31 | }, { 32 | name: 'root', 33 | children: [ 34 | { 35 | name: 'setTimeout', 36 | children: [], 37 | before: 1, 38 | after: 1, 39 | error: 0 40 | } 41 | ], 42 | before: 0, 43 | after: 0, 44 | error: 0 45 | }); 46 | }); 47 | 48 | test('test setImmediate', function (t) { 49 | test_helper(t, function (listener, done) { 50 | listener.currentName = 'setImmediate'; 51 | setImmediate(done); 52 | }, { 53 | name: 'root', 54 | children: [ 55 | { 56 | name: 'setImmediate', 57 | children: [], 58 | before: 1, 59 | after: 1, 60 | error: 0 61 | } 62 | ], 63 | before: 0, 64 | after: 0, 65 | error: 0 66 | }); 67 | }); 68 | 69 | test('test setInterval', function (t) { 70 | test_helper(t, function (listener, done) { 71 | listener.currentName = 'setInterval'; 72 | var count = 0; 73 | var interval = setInterval(function () { 74 | if (++count === 2) { 75 | clearInterval(interval); 76 | done(); 77 | } 78 | }); 79 | }, { 80 | name: 'root', 81 | children: [ 82 | { 83 | name: 'setInterval', 84 | children: [], 85 | before: 2, 86 | after: 2, 87 | error: 0 88 | } 89 | ], 90 | before: 0, 91 | after: 0, 92 | error: 0 93 | }); 94 | }); 95 | 96 | function test_helper (t, run, expect) { 97 | // Trigger callback out-of-band from async listener 98 | var done; 99 | var interval = setInterval(function () { 100 | if (done) { 101 | clearInterval(interval); 102 | t.deepEqual(listener.root, expect); 103 | t.end(); 104 | } 105 | }, 5); 106 | 107 | var listener = addListner(); 108 | run(listener, function () { done = true; }); 109 | process.removeAsyncListener(listener.listener); 110 | } 111 | 112 | function addListner() { 113 | var listener = process.addAsyncListener({ 114 | create: create, 115 | before: before, 116 | after: after, 117 | error: error 118 | }); 119 | 120 | 121 | var state = { 122 | listener: listener, 123 | currentName: 'root' 124 | }; 125 | 126 | state.root = create(); 127 | state.current = state.root; 128 | 129 | return state; 130 | 131 | function create () { 132 | var node = { 133 | name: state.currentName, 134 | children: [], 135 | before: 0, 136 | after: 0, 137 | error: 0 138 | }; 139 | 140 | if(state.current) state.current.children.push(node); 141 | return node; 142 | } 143 | 144 | function before(ctx, node) { 145 | state.current = node; 146 | state.current.before++; 147 | } 148 | 149 | function after(ctx, node) { 150 | node.after++; 151 | state.current = null; 152 | } 153 | 154 | function error(ctx, node) { 155 | node.error++; 156 | state.current = null; 157 | return false; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /test/zlib.tap.js: -------------------------------------------------------------------------------- 1 | if (!process.addAsyncListener) require('../index.js'); 2 | 3 | var test = require('tap').test; 4 | var zlib = require('zlib'); 5 | 6 | // Convert semver string to number set 7 | // TODO: This is *very* naive structure to check versions with, 8 | // but it works well enough for now... 9 | var nodeVersion = process.version.slice(1).split('.').map(Number) 10 | 11 | var compressors = ['deflate', 'deflateRaw', 'gzip']; 12 | var decompressors = ['inflate', 'inflateRaw', 'gunzip']; 13 | 14 | compressors.forEach(function (method) { 15 | var name = 'zlib.' + method; 16 | var el = { 17 | name: name, 18 | children: [], 19 | before: 1, 20 | after: 1, 21 | error: 0 22 | }; 23 | var list = [ el ]; 24 | if (nodeVersion[0] >= 6) { 25 | list.push(el); 26 | } 27 | 28 | var children = [ 29 | { 30 | name: name, 31 | // Compressors use streams internally, 32 | // so there's a bunch of nested stuff. 33 | children: [ 34 | { 35 | name: name, 36 | children: [ 37 | { 38 | name: name, 39 | children: list, 40 | before: 1, 41 | after: 1, 42 | error: 0 43 | } 44 | ], 45 | before: 1, 46 | after: 1, 47 | error: 0 48 | } 49 | ], 50 | before: 1, 51 | after: 1, 52 | error: 0 53 | } 54 | ] 55 | if (nodeVersion[0] >= 9) { 56 | children.unshift({ 57 | name: name, 58 | children: [], 59 | before: 1, 60 | after: 1, 61 | error: 0 62 | }) 63 | } 64 | 65 | test('test ' + name, function (t) { 66 | test_helper(t, function (listener, done) { 67 | listener.currentName = name; 68 | zlib[method](new Buffer('Goodbye World'), done); 69 | }, { 70 | name: 'root', 71 | children: children, 72 | before: 0, 73 | after: 0, 74 | error: 0 75 | }); 76 | }); 77 | }); 78 | 79 | decompressors.forEach(function (method, i) { 80 | var preMethod = compressors[i]; 81 | var name = 'zlib.' + method; 82 | var el = { 83 | name: name, 84 | children: [], 85 | before: 1, 86 | after: 1, 87 | error: 0 88 | }; 89 | var list = [ el ]; 90 | if (nodeVersion[0] >= 6) { 91 | list.push(el); 92 | } 93 | 94 | var children = [ 95 | { 96 | name: name, 97 | // Compressors use streams internally, 98 | // so there's a bunch of nested stuff. 99 | children: [ 100 | { 101 | name: name, 102 | children: [ 103 | { 104 | name: name, 105 | children: list, 106 | before: 1, 107 | after: 1, 108 | error: 0 109 | } 110 | ], 111 | before: 1, 112 | after: 1, 113 | error: 0 114 | } 115 | ], 116 | before: 1, 117 | after: 1, 118 | error: 0 119 | } 120 | ] 121 | if (nodeVersion[0] >= 9) { 122 | children.unshift({ 123 | name: name, 124 | children: [], 125 | before: 1, 126 | after: 1, 127 | error: 0 128 | }) 129 | } 130 | 131 | test('test ' + name, function (t) { 132 | zlib[preMethod](new Buffer('Goodbye World'), function (err, buf) { 133 | t.ok(!err, 'should not have errored in preparation') 134 | test_helper(t, function (listener, done) { 135 | listener.currentName = name; 136 | zlib[method](buf, done); 137 | }, { 138 | name: 'root', 139 | children: children, 140 | before: 0, 141 | after: 0, 142 | error: 0 143 | }); 144 | }); 145 | }); 146 | }); 147 | 148 | function test_helper (t, run, expect) { 149 | // Trigger callback out-of-band from async listener 150 | var args; 151 | var interval = setInterval(function () { 152 | if (args) { 153 | clearInterval(interval); 154 | t.ok(!args[0], 'should not have errored'); 155 | t.deepEqual(listener.root, expect); 156 | t.end(); 157 | } 158 | }, 5); 159 | 160 | var listener = addListner(); 161 | run(listener, function () { 162 | args = Array.prototype.slice.call(arguments); 163 | }); 164 | process.removeAsyncListener(listener.listener); 165 | } 166 | 167 | function addListner() { 168 | var listener = process.addAsyncListener({ 169 | create: create, 170 | before: before, 171 | after: after, 172 | error: error 173 | }); 174 | 175 | 176 | var state = { 177 | listener: listener, 178 | currentName: 'root' 179 | }; 180 | 181 | state.root = create(); 182 | state.current = state.root; 183 | 184 | return state; 185 | 186 | function create () { 187 | var node = { 188 | name: state.currentName, 189 | children: [], 190 | before: 0, 191 | after: 0, 192 | error: 0 193 | }; 194 | 195 | if(state.current) state.current.children.push(node); 196 | return node; 197 | } 198 | 199 | function before(ctx, node) { 200 | state.current = node; 201 | state.current.before++; 202 | } 203 | 204 | function after(ctx, node) { 205 | node.after++; 206 | state.current = null; 207 | } 208 | 209 | function error(ctx, node) { 210 | node.error++; 211 | state.current = null; 212 | return false; 213 | } 214 | } 215 | --------------------------------------------------------------------------------