From d6521f5f1220082b4d28921c27c93448c0cd8d40 Mon Sep 17 00:00:00 2001 From: Roman Shtylman Date: Tue, 3 Dec 2013 18:18:12 -0500 Subject: [PATCH] update to latest events code from node.js - does not include support for domains --- .../raw.github.com/Gozala/extendables/v0.2.0 | 1 - History.md | 5 + events.js | 378 ++++++++++++------ .../Gozala/extendables/v0.2.0/extendables.js | 1 - package.json | 6 +- tests/add-listeners.js | 63 +++ tests/check-listener-leaks.js | 86 ++++ tests/common.js | 41 ++ tests/implementation.js | 1 - tests/index.js | 12 + tests/listeners-side-effects.js | 55 +++ tests/listeners.js | 51 +++ tests/max-listeners.js | 50 +++ tests/modify-in-emit.js | 76 ++++ tests/node_modules/implementation.js | 1 - tests/num-args.js | 44 ++ tests/once.js | 59 +++ tests/remove-all-listeners.js | 72 ++++ tests/remove-listeners.js | 84 ++++ tests/set-max-listeners-side-effects.js | 29 ++ tests/subclass.js | 51 +++ tests/test-events.js | 171 -------- 22 files changed, 1040 insertions(+), 297 deletions(-) delete mode 160000 @modules/raw.github.com/Gozala/extendables/v0.2.0 delete mode 100644 node_modules/raw.github.com/Gozala/extendables/v0.2.0/extendables.js create mode 100644 tests/add-listeners.js create mode 100644 tests/check-listener-leaks.js create mode 100644 tests/common.js delete mode 100644 tests/implementation.js create mode 100644 tests/index.js create mode 100644 tests/listeners-side-effects.js create mode 100644 tests/listeners.js create mode 100644 tests/max-listeners.js create mode 100644 tests/modify-in-emit.js delete mode 100644 tests/node_modules/implementation.js create mode 100644 tests/num-args.js create mode 100644 tests/once.js create mode 100644 tests/remove-all-listeners.js create mode 100644 tests/remove-listeners.js create mode 100644 tests/set-max-listeners-side-effects.js create mode 100644 tests/subclass.js delete mode 100644 tests/test-events.js diff --git a/@modules/raw.github.com/Gozala/extendables/v0.2.0 b/@modules/raw.github.com/Gozala/extendables/v0.2.0 deleted file mode 160000 index 6f23992..0000000 --- a/@modules/raw.github.com/Gozala/extendables/v0.2.0 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6f23992e318f78efd5b58ef9c481e793d7d3bbf1 diff --git a/History.md b/History.md index 6e2bb8c..3a57a00 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,10 @@ # History # +## unreleased + + - Update to latest events code from node.js 0.10 + - copy tests from node.js + ## 0.4.0 / 2011-07-03 ## - Switching to graphquire@0.8.0 diff --git a/events.js b/events.js index 67ce755..c0c4808 100644 --- a/events.js +++ b/events.js @@ -1,146 +1,286 @@ -/* vim:set ts=2 sw=2 sts=2 expandtab */ -/*jshint newcap: true undef: true es5: true node: true devel: true - forin: true */ -/*global define: true */ -(typeof define !== "function" ? function($){ $(require, exports, module); } : define)(function(require, exports, module, undefined) { +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +var util = require('util/'); -var Extendable = require('raw.github.com/Gozala/extendables/v0.2.0/extendables').Extendable; -var isArray = Array.isArray; +function EventEmitter() { + this._events = this._events || {}; + this._maxListeners = this._maxListeners || undefined; +} +module.exports = EventEmitter; + +// Backwards-compat with node 0.10.x +EventEmitter.EventEmitter = EventEmitter; + +EventEmitter.prototype._events = undefined; +EventEmitter.prototype._maxListeners = undefined; + +// By default EventEmitters will print a warning if more than 10 listeners are +// added to it. This is a useful default which helps finding memory leaks. +EventEmitter.defaultMaxListeners = 10; -// By default EventEmitters will print a warning if more than -// 10 listeners are added to it. This is a useful default which -// helps finding memory leaks. -// // Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. -var MAX_LISTENERS = 10; -var ERROR_TYEPE = 'error'; - -exports.version = "0.2.0"; -exports.EventEmitter = Extendable.extend({ - setMaxListeners: function setMaxListeners(n) { - if (!this._events) this._events = {}; - this._events.maxListeners = n; - }, - emit: function emit(type) { - var args = Array.prototype.slice.call(arguments, 1); - var listeners = this.listeners(type); - if (type === ERROR_TYEPE && !listeners.length) - console.error(args[0]); - - listeners.forEach(function(listener) { - try { - listener.apply(this, args); - } catch (error) { - // We emit `error` event if listener threw an exception. If there are - // no listeners for `error` events or if listener for `error` event - // threw then we dump error directly to the console. - if (type !== ERROR_TYEPE && this.listeners(ERROR_TYEPE).length) - this.emit(ERROR_TYEPE, error); - else - console.error(error); - } - }, this); - }, - on: function on(type, listener) { - if (!this._events) - this._events = {}; +EventEmitter.prototype.setMaxListeners = function(n) { + if (!util.isNumber(n) || n < 0 || isNaN(n)) + throw TypeError('n must be a positive number'); + this._maxListeners = n; + return this; +}; + +EventEmitter.prototype.emit = function(type) { + var er, handler, len, args, i, listeners; - // To avoid recursion in the case that type == "newListeners"! Before - // adding it to the listeners, first emit "newListeners". - this.emit('newListener', type, listener); - - var events = this._events[type]; - if (!events) { - // Optimize the case of one listener. Don't need the extra array object. - this._events[type] = listener; - - // If listener is an array and if listener is not registered yet. - } else if (isArray(events) && !~events.indexOf(listener)) { - - // Check for listener leak - if (!events.warned) { - var m = events.maxListeners !== undefined ? events.maxListeners : - MAX_LISTENERS; - - if (m && m > 0 && events.length > m) { - events.warned = true; - console.error('warning: possible EventEmitter memory ' + - 'leak detected. %d listeners added. ' + - 'Use emitter.setMaxListeners() to increase limit.', - this._events[type].length); - } + if (!this._events) + this._events = {}; + + // If there is no 'error' event listener then throw. + if (type === 'error') { + if (!this._events.error || + (util.isObject(this._events.error) && !this._events.error.length)) { + er = arguments[1]; + if (er instanceof Error) { + throw er; // Unhandled 'error' event + } else { + throw TypeError('Uncaught, unspecified "error" event.'); } + return false; + } + } + + handler = this._events[type]; - events.push(listener); + if (util.isUndefined(handler)) + return false; - // If it's not the same listener adding it - } else if (events !== listener) { - // Adding the second element, need to change to array. - this._events[type] = [events, listener]; + if (util.isFunction(handler)) { + switch (arguments.length) { + // fast cases + case 1: + handler.call(this); + break; + case 2: + handler.call(this, arguments[1]); + break; + case 3: + handler.call(this, arguments[1], arguments[2]); + break; + // slower + default: + len = arguments.length; + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; + handler.apply(this, args); } + } else if (util.isObject(handler)) { + len = arguments.length; + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; - return this; - }, - once: function once(type, listener) { - var self = this; - function g() { - self.removeListener(type, g); - listener.apply(self, arguments); + listeners = handler.slice(); + len = listeners.length; + for (i = 0; i < len; i++) + listeners[i].apply(this, args); + } + + return true; +}; + +EventEmitter.prototype.addListener = function(type, listener) { + var m; + + if (!util.isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events) + this._events = {}; + + // To avoid recursion in the case that type === "newListener"! Before + // adding it to the listeners, first emit "newListener". + if (this._events.newListener) + this.emit('newListener', type, + util.isFunction(listener.listener) ? + listener.listener : listener); + + if (!this._events[type]) + // Optimize the case of one listener. Don't need the extra array object. + this._events[type] = listener; + else if (util.isObject(this._events[type])) + // If we've already got an array, just append. + this._events[type].push(listener); + else + // Adding the second element, need to change to array. + this._events[type] = [this._events[type], listener]; + + // Check for listener leak + if (util.isObject(this._events[type]) && !this._events[type].warned) { + var m; + if (!util.isUndefined(this._maxListeners)) { + m = this._maxListeners; + } else { + m = EventEmitter.defaultMaxListeners; } - g.listener = listener; - self.on(type, g); - return this; - }, - removeListener: function removeListener(type, listener) { - if ('function' !== typeof listener) { - throw new Error('removeListener only takes instances of Function'); + if (m && m > 0 && this._events[type].length > m) { + this._events[type].warned = true; + console.error('(node) warning: possible EventEmitter memory ' + + 'leak detected. %d listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.', + this._events[type].length); + console.trace(); + } + } + + return this; +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.once = function(type, listener) { + if (!util.isFunction(listener)) + throw TypeError('listener must be a function'); + + var fired = false; + + function g() { + this.removeListener(type, g); + + if (!fired) { + fired = true; + listener.apply(this, arguments); } + } - // does not use listeners(), so no side effect of creating _events[type] - if (!this._events || !this._events[type]) return this; + g.listener = listener; + this.on(type, g); - var list = this._events[type]; + return this; +}; + +// emits a 'removeListener' event iff the listener was removed +EventEmitter.prototype.removeListener = function(type, listener) { + var list, position, length, i; + + if (!util.isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events || !this._events[type]) + return this; - if (isArray(list)) { - var position = -1; - for (var i = 0, length = list.length; i < length; i++) { - if (list[i] === listener || - (list[i].listener && list[i].listener === listener)) - { - position = i; - break; - } + list = this._events[type]; + length = list.length; + position = -1; + + if (list === listener || + (util.isFunction(list.listener) && list.listener === listener)) { + delete this._events[type]; + if (this._events.removeListener) + this.emit('removeListener', type, listener); + + } else if (util.isObject(list)) { + for (i = length; i-- > 0;) { + if (list[i] === listener || + (list[i].listener && list[i].listener === listener)) { + position = i; + break; } + } - if (position < 0) return this; - list.splice(position, 1); - if (list.length === 0) - delete this._events[type]; - } else if (list === listener || - (list.listener && list.listener === listener)) - { + if (position < 0) + return this; + + if (list.length === 1) { + list.length = 0; delete this._events[type]; + } else { + list.splice(position, 1); } + if (this._events.removeListener) + this.emit('removeListener', type, listener); + } + + return this; +}; + +EventEmitter.prototype.removeAllListeners = function(type) { + var key, listeners; + + if (!this._events) return this; - }, - removeAllListeners: function removeAllListeners(type) { - // does not use listeners(), so no side effect of creating _events[type] - if (type && this._events && this._events[type]) this._events[type] = null; + + // not listening for removeListener, no need to emit + if (!this._events.removeListener) { + if (arguments.length === 0) + this._events = {}; + else if (this._events[type]) + delete this._events[type]; return this; - }, - listeners: function listeners(type) { - if (!this._events) this._events = {}; - if (!this._events[type]) this._events[type] = []; - if (!isArray(this._events[type])) { - this._events[type] = [this._events[type]]; + } + + // emit removeListener for all listeners on all events + if (arguments.length === 0) { + for (key in this._events) { + if (key === 'removeListener') continue; + this.removeAllListeners(key); } - return this._events[type].slice(0); + this.removeAllListeners('removeListener'); + this._events = {}; + return this; } -}); -}); + listeners = this._events[type]; + + if (util.isFunction(listeners)) { + this.removeListener(type, listeners); + } else { + // LIFO order + while (listeners.length) + this.removeListener(type, listeners[listeners.length - 1]); + } + delete this._events[type]; + + return this; +}; + +EventEmitter.prototype.listeners = function(type) { + var ret; + if (!this._events || !this._events[type]) + ret = []; + else if (util.isFunction(this._events[type])) + ret = [this._events[type]]; + else + ret = this._events[type].slice(); + return ret; +}; + +EventEmitter.listenerCount = function(emitter, type) { + var ret; + if (!emitter._events || !emitter._events[type]) + ret = 0; + else if (util.isFunction(emitter._events[type])) + ret = 1; + else + ret = emitter._events[type].length; + return ret; +}; diff --git a/node_modules/raw.github.com/Gozala/extendables/v0.2.0/extendables.js b/node_modules/raw.github.com/Gozala/extendables/v0.2.0/extendables.js deleted file mode 100644 index 5dd4e98..0000000 --- a/node_modules/raw.github.com/Gozala/extendables/v0.2.0/extendables.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../../../../@modules/raw.github.com/Gozala/extendables/v0.2.0/extendables'); diff --git a/package.json b/package.json index c54b2e3..8d41c54 100644 --- a/package.json +++ b/package.json @@ -13,15 +13,15 @@ "bugs": { "web": "http://github.com/Gozala/events/issues/" }, - "devDependencies": { - "test": ">=0.0.10" + "dependencies": { + "util": "0.10.0" }, "main": "./events.js", "engines": { "node": ">=0.4.x" }, "scripts": { - "test": "node tests/test-events.js" + "test": "node tests/index.js" }, "licenses": [{ "type": "MIT", diff --git a/tests/add-listeners.js b/tests/add-listeners.js new file mode 100644 index 0000000..5ab874c --- /dev/null +++ b/tests/add-listeners.js @@ -0,0 +1,63 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'); +var events = require('../'); + +var e = new events.EventEmitter(); + +var events_new_listener_emited = []; +var listeners_new_listener_emited = []; +var times_hello_emited = 0; + +// sanity check +assert.equal(e.addListener, e.on); + +e.on('newListener', function(event, listener) { + console.log('newListener: ' + event); + events_new_listener_emited.push(event); + listeners_new_listener_emited.push(listener); +}); + +function hello(a, b) { + console.log('hello'); + times_hello_emited += 1; + assert.equal('a', a); + assert.equal('b', b); +} +e.on('hello', hello); + +var foo = function() {}; +e.once('foo', foo); + +console.log('start'); + +e.emit('hello', 'a', 'b'); + + +// just make sure that this doesn't throw: +var f = new events.EventEmitter(); +f.setMaxListeners(0); + +assert.deepEqual(['hello', 'foo'], events_new_listener_emited); +assert.deepEqual([hello, foo], listeners_new_listener_emited); +assert.equal(1, times_hello_emited); + diff --git a/tests/check-listener-leaks.js b/tests/check-listener-leaks.js new file mode 100644 index 0000000..e07866a --- /dev/null +++ b/tests/check-listener-leaks.js @@ -0,0 +1,86 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'); +var events = require('../'); + +var e = new events.EventEmitter(); + +// default +for (var i = 0; i < 10; i++) { + e.on('default', function() {}); +} +assert.ok(!e._events['default'].hasOwnProperty('warned')); +e.on('default', function() {}); +assert.ok(e._events['default'].warned); + +// specific +e.setMaxListeners(5); +for (var i = 0; i < 5; i++) { + e.on('specific', function() {}); +} +assert.ok(!e._events['specific'].hasOwnProperty('warned')); +e.on('specific', function() {}); +assert.ok(e._events['specific'].warned); + +// only one +e.setMaxListeners(1); +e.on('only one', function() {}); +assert.ok(!e._events['only one'].hasOwnProperty('warned')); +e.on('only one', function() {}); +assert.ok(e._events['only one'].hasOwnProperty('warned')); + +// unlimited +e.setMaxListeners(0); +for (var i = 0; i < 1000; i++) { + e.on('unlimited', function() {}); +} +assert.ok(!e._events['unlimited'].hasOwnProperty('warned')); + +// process-wide +events.EventEmitter.defaultMaxListeners = 42; +e = new events.EventEmitter(); + +for (var i = 0; i < 42; ++i) { + e.on('fortytwo', function() {}); +} +assert.ok(!e._events['fortytwo'].hasOwnProperty('warned')); +e.on('fortytwo', function() {}); +assert.ok(e._events['fortytwo'].hasOwnProperty('warned')); +delete e._events['fortytwo'].warned; + +events.EventEmitter.defaultMaxListeners = 44; +e.on('fortytwo', function() {}); +assert.ok(!e._events['fortytwo'].hasOwnProperty('warned')); +e.on('fortytwo', function() {}); +assert.ok(e._events['fortytwo'].hasOwnProperty('warned')); + +// but _maxListeners still has precedence over defaultMaxListeners +events.EventEmitter.defaultMaxListeners = 42; +e = new events.EventEmitter(); +e.setMaxListeners(1); +e.on('uno', function() {}); +assert.ok(!e._events['uno'].hasOwnProperty('warned')); +e.on('uno', function() {}); +assert.ok(e._events['uno'].hasOwnProperty('warned')); + +// chainable +assert.strictEqual(e, e.setMaxListeners(1)); diff --git a/tests/common.js b/tests/common.js new file mode 100644 index 0000000..fde651b --- /dev/null +++ b/tests/common.js @@ -0,0 +1,41 @@ +var mustCallChecks = []; + +function runCallChecks(exitCode) { + if (exitCode !== 0) return; + + var failed = mustCallChecks.filter(function(context) { + return context.actual !== context.expected; + }); + + failed.forEach(function(context) { + console.log('Mismatched %s function calls. Expected %d, actual %d.', + context.name, + context.expected, + context.actual); + console.log(context.stack.split('\n').slice(2).join('\n')); + }); + + if (failed.length) process.exit(1); +} + + +exports.mustCall = function(fn, expected) { + if (typeof expected !== 'number') expected = 1; + + var context = { + expected: expected, + actual: 0, + stack: (new Error).stack, + name: fn.name || '' + }; + + // add the exit listener only once to avoid listener leak warnings + if (mustCallChecks.length === 0) process.on('exit', runCallChecks); + + mustCallChecks.push(context); + + return function() { + context.actual++; + return fn.apply(this, arguments); + }; +}; diff --git a/tests/implementation.js b/tests/implementation.js deleted file mode 100644 index 05ce1a7..0000000 --- a/tests/implementation.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('events'); diff --git a/tests/index.js b/tests/index.js new file mode 100644 index 0000000..2b06931 --- /dev/null +++ b/tests/index.js @@ -0,0 +1,12 @@ +require('./add-listeners.js'); +require('./check-listener-leaks.js'); +require('./listeners-side-effects.js'); +require('./listeners.js'); +require('./max-listeners.js'); +require('./modify-in-emit.js'); +require('./num-args.js'); +require('./once.js'); +require('./remove-all-listeners.js'); +require('./remove-listeners.js'); +require('./set-max-listeners-side-effects.js'); +require('./subclass.js'); diff --git a/tests/listeners-side-effects.js b/tests/listeners-side-effects.js new file mode 100644 index 0000000..15ff3d3 --- /dev/null +++ b/tests/listeners-side-effects.js @@ -0,0 +1,55 @@ + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'); +var EventEmitter = require('../').EventEmitter; + +var e = new EventEmitter; +var fl; // foo listeners + +fl = e.listeners('foo'); +assert(Array.isArray(fl)); +assert(fl.length === 0); +assert.deepEqual(e._events, {}); + +e.on('foo', assert.fail); +fl = e.listeners('foo'); +assert(e._events.foo === assert.fail); +assert(Array.isArray(fl)); +assert(fl.length === 1); +assert(fl[0] === assert.fail); + +e.listeners('bar'); +assert(!e._events.hasOwnProperty('bar')); + +e.on('foo', assert.ok); +fl = e.listeners('foo'); + +assert(Array.isArray(e._events.foo)); +assert(e._events.foo.length === 2); +assert(e._events.foo[0] === assert.fail); +assert(e._events.foo[1] === assert.ok); + +assert(Array.isArray(fl)); +assert(fl.length === 2); +assert(fl[0] === assert.fail); +assert(fl[1] === assert.ok); diff --git a/tests/listeners.js b/tests/listeners.js new file mode 100644 index 0000000..0ed9a53 --- /dev/null +++ b/tests/listeners.js @@ -0,0 +1,51 @@ + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'); +var events = require('../'); + +function listener() {} +function listener2() {} + +var e1 = new events.EventEmitter(); +e1.on('foo', listener); +var fooListeners = e1.listeners('foo'); +assert.deepEqual(e1.listeners('foo'), [listener]); +e1.removeAllListeners('foo'); +assert.deepEqual(e1.listeners('foo'), []); +assert.deepEqual(fooListeners, [listener]); + +var e2 = new events.EventEmitter(); +e2.on('foo', listener); +var e2ListenersCopy = e2.listeners('foo'); +assert.deepEqual(e2ListenersCopy, [listener]); +assert.deepEqual(e2.listeners('foo'), [listener]); +e2ListenersCopy.push(listener2); +assert.deepEqual(e2.listeners('foo'), [listener]); +assert.deepEqual(e2ListenersCopy, [listener, listener2]); + +var e3 = new events.EventEmitter(); +e3.on('foo', listener); +var e3ListenersCopy = e3.listeners('foo'); +e3.on('foo', listener2); +assert.deepEqual(e3.listeners('foo'), [listener, listener2]); +assert.deepEqual(e3ListenersCopy, [listener]); diff --git a/tests/max-listeners.js b/tests/max-listeners.js new file mode 100644 index 0000000..75e8f73 --- /dev/null +++ b/tests/max-listeners.js @@ -0,0 +1,50 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'); +var events = require('../'); + +var gotEvent = false; + +var e = new events.EventEmitter(); + +e.on('maxListeners', function() { + gotEvent = true; +}); + +// Should not corrupt the 'maxListeners' queue. +e.setMaxListeners(42); + +assert.throws(function() { + e.setMaxListeners(NaN); +}); + +assert.throws(function() { + e.setMaxListeners(-1); +}); + +assert.throws(function() { + e.setMaxListeners("and even this"); +}); + +e.emit('maxListeners'); + +assert(gotEvent); diff --git a/tests/modify-in-emit.js b/tests/modify-in-emit.js new file mode 100644 index 0000000..3470270 --- /dev/null +++ b/tests/modify-in-emit.js @@ -0,0 +1,76 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'); +var events = require('../'); + +var callbacks_called = []; + +var e = new events.EventEmitter(); + +function callback1() { + callbacks_called.push('callback1'); + e.on('foo', callback2); + e.on('foo', callback3); + e.removeListener('foo', callback1); +} + +function callback2() { + callbacks_called.push('callback2'); + e.removeListener('foo', callback2); +} + +function callback3() { + callbacks_called.push('callback3'); + e.removeListener('foo', callback3); +} + +e.on('foo', callback1); +assert.equal(1, e.listeners('foo').length); + +e.emit('foo'); +assert.equal(2, e.listeners('foo').length); +assert.deepEqual(['callback1'], callbacks_called); + +e.emit('foo'); +assert.equal(0, e.listeners('foo').length); +assert.deepEqual(['callback1', 'callback2', 'callback3'], callbacks_called); + +e.emit('foo'); +assert.equal(0, e.listeners('foo').length); +assert.deepEqual(['callback1', 'callback2', 'callback3'], callbacks_called); + +e.on('foo', callback1); +e.on('foo', callback2); +assert.equal(2, e.listeners('foo').length); +e.removeAllListeners('foo'); +assert.equal(0, e.listeners('foo').length); + +// Verify that removing callbacks while in emit allows emits to propagate to +// all listeners +callbacks_called = []; + +e.on('foo', callback2); +e.on('foo', callback3); +assert.equal(2, e.listeners('foo').length); +e.emit('foo'); +assert.deepEqual(['callback2', 'callback3'], callbacks_called); +assert.equal(0, e.listeners('foo').length); diff --git a/tests/node_modules/implementation.js b/tests/node_modules/implementation.js deleted file mode 100644 index 5a5c348..0000000 --- a/tests/node_modules/implementation.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../../events'); diff --git a/tests/num-args.js b/tests/num-args.js new file mode 100644 index 0000000..1e49d8a --- /dev/null +++ b/tests/num-args.js @@ -0,0 +1,44 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'); +var events = require('../'); + +var e = new events.EventEmitter(), + num_args_emited = []; + +e.on('numArgs', function() { + var numArgs = arguments.length; + console.log('numArgs: ' + numArgs); + num_args_emited.push(numArgs); +}); + +console.log('start'); + +e.emit('numArgs'); +e.emit('numArgs', null); +e.emit('numArgs', null, null); +e.emit('numArgs', null, null, null); +e.emit('numArgs', null, null, null, null); +e.emit('numArgs', null, null, null, null, null); + +assert.deepEqual([0, 1, 2, 3, 4, 5], num_args_emited); + diff --git a/tests/once.js b/tests/once.js new file mode 100644 index 0000000..6145319 --- /dev/null +++ b/tests/once.js @@ -0,0 +1,59 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'); +var events = require('../'); + +var e = new events.EventEmitter(); +var times_hello_emited = 0; + +e.once('hello', function(a, b) { + times_hello_emited++; +}); + +e.emit('hello', 'a', 'b'); +e.emit('hello', 'a', 'b'); +e.emit('hello', 'a', 'b'); +e.emit('hello', 'a', 'b'); + +var remove = function() { + assert.fail(1, 0, 'once->foo should not be emitted', '!'); +}; + +e.once('foo', remove); +e.removeListener('foo', remove); +e.emit('foo'); + +var times_recurse_emitted = 0; + +e.once('e', function() { + e.emit('e'); + times_recurse_emitted++; +}); + +e.once('e', function() { + times_recurse_emitted++; +}); + +e.emit('e'); + +assert.equal(1, times_hello_emited); +assert.equal(2, times_recurse_emitted); diff --git a/tests/remove-all-listeners.js b/tests/remove-all-listeners.js new file mode 100644 index 0000000..2e61b27 --- /dev/null +++ b/tests/remove-all-listeners.js @@ -0,0 +1,72 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('./common'); +var assert = require('assert'); +var events = require('../'); + +function expect(expected) { + var actual = []; + process.on('exit', function() { + assert.deepEqual(actual.sort(), expected.sort()); + }); + function listener(name) { + actual.push(name) + } + return common.mustCall(listener, expected.length); +} + +function listener() {} + +var e1 = new events.EventEmitter(); +e1.on('foo', listener); +e1.on('bar', listener); +e1.on('baz', listener); +e1.on('baz', listener); +var fooListeners = e1.listeners('foo'); +var barListeners = e1.listeners('bar'); +var bazListeners = e1.listeners('baz'); +e1.on('removeListener', expect(['bar', 'baz', 'baz'])); +e1.removeAllListeners('bar'); +e1.removeAllListeners('baz'); +assert.deepEqual(e1.listeners('foo'), [listener]); +assert.deepEqual(e1.listeners('bar'), []); +assert.deepEqual(e1.listeners('baz'), []); +// after calling removeAllListeners, +// the old listeners array should stay unchanged +assert.deepEqual(fooListeners, [listener]); +assert.deepEqual(barListeners, [listener]); +assert.deepEqual(bazListeners, [listener, listener]); +// after calling removeAllListeners, +// new listeners arrays are different from the old +assert.notEqual(e1.listeners('bar'), barListeners); +assert.notEqual(e1.listeners('baz'), bazListeners); + +var e2 = new events.EventEmitter(); +e2.on('foo', listener); +e2.on('bar', listener); +// expect LIFO order +e2.on('removeListener', expect(['foo', 'bar', 'removeListener'])); +e2.on('removeListener', expect(['foo', 'bar'])); +e2.removeAllListeners(); +console.error(e2); +assert.deepEqual([], e2.listeners('foo')); +assert.deepEqual([], e2.listeners('bar')); diff --git a/tests/remove-listeners.js b/tests/remove-listeners.js new file mode 100644 index 0000000..401e6a9 --- /dev/null +++ b/tests/remove-listeners.js @@ -0,0 +1,84 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('./common'); +var assert = require('assert'); +var events = require('../'); + +var count = 0; + +function listener1() { + console.log('listener1'); + count++; +} + +function listener2() { + console.log('listener2'); + count++; +} + +function listener3() { + console.log('listener3'); + count++; +} + +function remove1() { + assert(0); +} + +function remove2() { + assert(0); +} + +var e1 = new events.EventEmitter(); +e1.on('hello', listener1); +e1.on('removeListener', common.mustCall(function(name, cb) { + assert.equal(name, 'hello'); + assert.equal(cb, listener1); +})); +e1.removeListener('hello', listener1); +assert.deepEqual([], e1.listeners('hello')); + +var e2 = new events.EventEmitter(); +e2.on('hello', listener1); +e2.on('removeListener', assert.fail); +e2.removeListener('hello', listener2); +assert.deepEqual([listener1], e2.listeners('hello')); + +var e3 = new events.EventEmitter(); +e3.on('hello', listener1); +e3.on('hello', listener2); +e3.on('removeListener', common.mustCall(function(name, cb) { + assert.equal(name, 'hello'); + assert.equal(cb, listener1); +})); +e3.removeListener('hello', listener1); +assert.deepEqual([listener2], e3.listeners('hello')); + +var e4 = new events.EventEmitter(); +e4.on('removeListener', common.mustCall(function(name, cb) { + if (cb !== remove1) return; + this.removeListener('quux', remove2); + this.emit('quux'); +}, 2)); +e4.on('quux', remove1); +e4.on('quux', remove2); +e4.removeListener('quux', remove1); diff --git a/tests/set-max-listeners-side-effects.js b/tests/set-max-listeners-side-effects.js new file mode 100644 index 0000000..654b01c --- /dev/null +++ b/tests/set-max-listeners-side-effects.js @@ -0,0 +1,29 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'); +var events = require('../'); + +var e = new events.EventEmitter; + +assert.deepEqual(e._events, {}); +e.setMaxListeners(5); +assert.deepEqual(e._events, {}); diff --git a/tests/subclass.js b/tests/subclass.js new file mode 100644 index 0000000..7759381 --- /dev/null +++ b/tests/subclass.js @@ -0,0 +1,51 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'); +var EventEmitter = require('../').EventEmitter; +var util = require('util'); + +util.inherits(MyEE, EventEmitter); + +function MyEE(cb) { + this.once(1, cb); + this.emit(1); + this.removeAllListeners(); + EventEmitter.call(this); +} + +var called = false; +var myee = new MyEE(function() { + called = true; +}); + + +util.inherits(ErrorEE, EventEmitter); +function ErrorEE() { + this.emit('error', new Error('blerg')); +} + +assert.throws(function() { + new ErrorEE(); +}, /blerg/); + +assert(called); +assert.deepEqual(myee._events, {}); diff --git a/tests/test-events.js b/tests/test-events.js deleted file mode 100644 index e19d1d1..0000000 --- a/tests/test-events.js +++ /dev/null @@ -1,171 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 expandtab */ -/*jshint newcap: true undef: true es5: true node: true devel: true - forin: true */ -/*global define: true */ - -(typeof define !== "function" ? function($){ $(require, exports, module); } : define)(function(require, exports, module, undefined) { - -"use strict"; - -var EventEmitter = require("implementation").EventEmitter; - -exports['test:add listeners'] = function(assert) { - var e = new EventEmitter(); - - var events_new_listener_emited = []; - var times_hello_emited = 0; - - e.on("newListener", function (event, listener) { - events_new_listener_emited.push(event); - }); - - e.on("hello", function (a, b) { - times_hello_emited += 1; - assert.equal("a", a, "first argument was passed"); - assert.equal("b", b, "secrond argument was passed"); - assert.equal(this, e, '`this` pseudo-variable is bound to instance'); - }); - - e.emit("hello", "a", "b"); -}; - -exports['test:remove listeners'] = function(assert) { - var count = 0; - - function listener1 () { - count++; - } - function listener2 () { - count++; - } - function listener3 () { - count++; - } - - var e1 = new EventEmitter(); - e1.on("hello", listener1); - assert.equal(1, e1.listeners('hello').length, "one listener is registered"); - e1.removeListener("hello", listener1); - assert.equal(0, e1.listeners('hello').length, "only listenere was removed"); - - var e2 = new EventEmitter(); - e2.on("hello", listener1); - assert.equal(1, e2.listeners('hello').length, "one listener was registered"); - e2.removeListener("hello", listener2); - assert.equal(1, e2.listeners('hello').length, "anther listener registered"); - assert.equal(listener1, e2.listeners('hello')[0], - "order of registration is preserved"); - - var e3 = new EventEmitter(); - e3.on("hello", listener1); - assert.equal(1, e3.listeners('hello').length, "only one listener registered"); - e3.on("hello", listener2); - assert.equal(2, e3.listeners('hello').length, "one more listener added"); - e3.removeListener("hello", listener1); - assert.equal(1, e3.listeners('hello').length, "one listener is removed"); - assert.equal(listener2, e3.listeners('hello')[0], "correct listener remains"); -}; - -exports['test: modify in emit'] = function(assert) { - var callbacks_called = [ ]; - var e = new EventEmitter(); - - function callback2() { - callbacks_called.push("callback2"); - e.removeListener("foo", callback2); - } - function callback3() { - callbacks_called.push("callback3"); - e.removeListener("foo", callback3); - } - function callback1() { - callbacks_called.push("callback1"); - e.on("foo", callback2); - e.on("foo", callback3); - e.removeListener("foo", callback1); - } - - e.on("foo", callback1); - assert.equal(1, e.listeners("foo").length, "one listener is registered"); - - e.emit("foo"); - assert.equal(2, e.listeners("foo").length, - "listener registered additional listener"); - assert.equal(1, callbacks_called.length, "listener was called only 1 time"); - assert.equal('callback1', callbacks_called[0], - "callback1 listener was called"); - - e.emit("foo"); - assert.equal(0, e.listeners("foo").length, "listeners removed themself"); - assert.equal(3, callbacks_called.length, "all of the listeners were called"); - assert.equal('callback1', callbacks_called[0], - "callback1 was called first"); - assert.equal('callback2', callbacks_called[1], - "callback2 was called second"); - assert.equal('callback3', callbacks_called[2], - "callback3 was called third"); - - e.emit("foo"); - assert.equal(0, e.listeners("foo").length, "no listener are registered"); - assert.equal(3, callbacks_called.length, "no listeners were called"); - assert.equal('callback1', callbacks_called[0], "callback1 is still #1"); - assert.equal('callback2', callbacks_called[1], "callback2 is still #2"); - assert.equal('callback3', callbacks_called[2], "callback3 is still #3"); - - e.on("foo", callback1); - e.on("foo", callback2); - assert.equal(2, e.listeners("foo").length, "two listeners were added"); - e.removeAllListeners("foo"); - assert.equal(0, e.listeners("foo").length, "all listeners were removed"); - - // Verify that removing callbacks while in emit allows emits to propagate to - // all listeners - callbacks_called = [ ]; - - e.on("foo", callback2); - e.on("foo", callback3); - assert.equal(2, e.listeners("foo").length, "two liseners were added"); - e.emit("foo"); - assert.equal(2, callbacks_called.length, "two listeners were called"); - assert.equal('callback2', callbacks_called[0], "callback2 was called 1st"); - assert.equal('callback3', callbacks_called[1], "callback3 was called 2nd"); - assert.equal(0, e.listeners("foo").length, "listeners romeved themselfs"); -}; - -exports['test:adding same listener'] = function(assert) { - function foo() {} - var e = new EventEmitter(); - e.on("foo", foo); - e.on("foo", foo); - assert.equal( - 1, - e.listeners("foo").length, - "listener reregistration is ignored" - ); -}; - -exports['test:errors are reported if listener throws'] = function(assert) { - var e = new EventEmitter(), - reported = false; - e.on('error', function(e) { reported = true; }); - e.on('boom', function() { throw new Error('Boom!'); }); - e.emit('boom', 3); - assert.ok(reported, 'error should be reported through event'); -}; - -exports['test:once'] = function(assert) { - var e = new EventEmitter(); - var called = false; - - e.once('foo', function(value) { - assert.ok(!called, "listener called only once"); - assert.equal(value, "bar", "correct argument was passed"); - }); - - e.emit('foo', 'bar'); - e.emit('foo', 'baz'); -}; - -require("test").run(exports); - -});