haste-server/node_modules/mocha/lib/runnable.js

164 lines
2.9 KiB
JavaScript
Raw Permalink Normal View History

2012-02-06 20:09:01 +01:00
/**
* Module dependencies.
*/
var EventEmitter = require('events').EventEmitter
, debug = require('debug')('runnable');
/**
* Expose `Runnable`.
*/
module.exports = Runnable;
/**
* Initialize a new `Runnable` with the given `title` and callback `fn`.
*
* @param {String} title
* @param {Function} fn
* @api private
*/
function Runnable(title, fn) {
this.title = title;
this.fn = fn;
this.async = fn && fn.length;
this.sync = ! this.async;
this._timeout = 2000;
this.timedOut = false;
this.context = this;
}
/**
* Inherit from `EventEmitter.prototype`.
*/
Runnable.prototype.__proto__ = EventEmitter.prototype;
/**
* Set & get timeout `ms`.
*
* @param {Number} ms
* @return {Runnable|Number} ms or self
* @api private
*/
Runnable.prototype.timeout = function(ms){
if (0 == arguments.length) return this._timeout;
debug('timeout %d', ms);
this._timeout = ms;
if (this.timer) this.resetTimeout();
return this;
};
/**
* Return the full title generated by recursively
* concatenating the parent's full title.
*
* @return {String}
* @api public
*/
Runnable.prototype.fullTitle = function(){
return this.parent.fullTitle() + ' ' + this.title;
};
/**
* Clear the timeout.
*
* @api private
*/
Runnable.prototype.clearTimeout = function(){
clearTimeout(this.timer);
};
/**
* Reset the timeout.
*
* @api private
*/
Runnable.prototype.resetTimeout = function(){
var self = this
, ms = this.timeout();
this.clearTimeout();
if (ms) {
this.timer = setTimeout(function(){
self.callback(new Error('timeout of ' + ms + 'ms exceeded'));
self.timedOut = true;
}, ms);
}
};
/**
* Run the test and invoke `fn(err)`.
*
* @param {Function} fn
* @api private
*/
Runnable.prototype.run = function(fn){
var self = this
, ms = this.timeout()
, start = new Date
, ctx = this.context
, finished
, emitted;
// timeout
if (this.async) {
if (ms) {
this.timer = setTimeout(function(){
done(new Error('timeout of ' + ms + 'ms exceeded'));
self.timedOut = true;
}, ms);
}
}
// called multiple times
function multiple() {
if (emitted) return;
emitted = true;
self.emit('error', new Error('done() called multiple times'));
}
// finished
function done(err) {
if (self.timedOut) return;
if (finished) return multiple();
self.clearTimeout();
self.duration = new Date - start;
finished = true;
fn(err);
}
// for .resetTimeout()
this.callback = done;
// async
if (this.async) {
try {
this.fn.call(ctx, function(err){
if (err instanceof Error) return done(err);
if (null != err) return done(new Error('done() invoked with non-Error: ' + err));
done();
});
} catch (err) {
done(err);
}
return;
}
// sync
try {
if (!this.pending) this.fn.call(ctx);
this.duration = new Date - start;
fn();
} catch (err) {
fn(err);
}
};