source: Dev/trunk/node_modules/mocha/lib/runnable.js @ 484

Last change on this file since 484 was 484, checked in by hendrikvanantwerpen, 11 years ago

Commit node_modules, to make checkouts and builds more deterministic.

File size: 4.2 KB
Line 
1
2/**
3 * Module dependencies.
4 */
5
6var EventEmitter = require('events').EventEmitter
7  , debug = require('debug')('mocha:runnable')
8  , milliseconds = require('./ms');
9
10/**
11 * Save timer references to avoid Sinon interfering (see GH-237).
12 */
13
14var Date = global.Date
15  , setTimeout = global.setTimeout
16  , setInterval = global.setInterval
17  , clearTimeout = global.clearTimeout
18  , clearInterval = global.clearInterval;
19
20/**
21 * Object#toString().
22 */
23
24var toString = Object.prototype.toString;
25
26/**
27 * Expose `Runnable`.
28 */
29
30module.exports = Runnable;
31
32/**
33 * Initialize a new `Runnable` with the given `title` and callback `fn`.
34 *
35 * @param {String} title
36 * @param {Function} fn
37 * @api private
38 */
39
40function Runnable(title, fn) {
41  this.title = title;
42  this.fn = fn;
43  this.async = fn && fn.length;
44  this.sync = ! this.async;
45  this._timeout = 2000;
46  this._slow = 75;
47  this.timedOut = false;
48}
49
50/**
51 * Inherit from `EventEmitter.prototype`.
52 */
53
54Runnable.prototype.__proto__ = EventEmitter.prototype;
55
56/**
57 * Set & get timeout `ms`.
58 *
59 * @param {Number|String} ms
60 * @return {Runnable|Number} ms or self
61 * @api private
62 */
63
64Runnable.prototype.timeout = function(ms){
65  if (0 == arguments.length) return this._timeout;
66  if ('string' == typeof ms) ms = milliseconds(ms);
67  debug('timeout %d', ms);
68  this._timeout = ms;
69  if (this.timer) this.resetTimeout();
70  return this;
71};
72
73/**
74 * Set & get slow `ms`.
75 *
76 * @param {Number|String} ms
77 * @return {Runnable|Number} ms or self
78 * @api private
79 */
80
81Runnable.prototype.slow = function(ms){
82  if (0 === arguments.length) return this._slow;
83  if ('string' == typeof ms) ms = milliseconds(ms);
84  debug('timeout %d', ms);
85  this._slow = ms;
86  return this;
87};
88
89/**
90 * Return the full title generated by recursively
91 * concatenating the parent's full title.
92 *
93 * @return {String}
94 * @api public
95 */
96
97Runnable.prototype.fullTitle = function(){
98  return this.parent.fullTitle() + ' ' + this.title;
99};
100
101/**
102 * Clear the timeout.
103 *
104 * @api private
105 */
106
107Runnable.prototype.clearTimeout = function(){
108  clearTimeout(this.timer);
109};
110
111/**
112 * Inspect the runnable void of private properties.
113 *
114 * @return {String}
115 * @api private
116 */
117
118Runnable.prototype.inspect = function(){
119  return JSON.stringify(this, function(key, val){
120    if ('_' == key[0]) return;
121    if ('parent' == key) return '#<Suite>';
122    if ('ctx' == key) return '#<Context>';
123    return val;
124  }, 2);
125};
126
127/**
128 * Reset the timeout.
129 *
130 * @api private
131 */
132
133Runnable.prototype.resetTimeout = function(){
134  var self = this;
135  var ms = this.timeout() || 1e9;
136
137  this.clearTimeout();
138  this.timer = setTimeout(function(){
139    self.callback(new Error('timeout of ' + ms + 'ms exceeded'));
140    self.timedOut = true;
141  }, ms);
142};
143
144/**
145 * Run the test and invoke `fn(err)`.
146 *
147 * @param {Function} fn
148 * @api private
149 */
150
151Runnable.prototype.run = function(fn){
152  var self = this
153    , ms = this.timeout()
154    , start = new Date
155    , ctx = this.ctx
156    , finished
157    , emitted;
158
159  if (ctx) ctx.runnable(this);
160
161  // timeout
162  if (this.async) {
163    if (ms) {
164      this.timer = setTimeout(function(){
165        done(new Error('timeout of ' + ms + 'ms exceeded'));
166        self.timedOut = true;
167      }, ms);
168    }
169  }
170
171  // called multiple times
172  function multiple(err) {
173    if (emitted) return;
174    emitted = true;
175    self.emit('error', err || new Error('done() called multiple times'));
176  }
177
178  // finished
179  function done(err) {
180    if (self.timedOut) return;
181    if (finished) return multiple(err);
182    self.clearTimeout();
183    self.duration = new Date - start;
184    finished = true;
185    fn(err);
186  }
187
188  // for .resetTimeout()
189  this.callback = done;
190
191  // async
192  if (this.async) {
193    try {
194      this.fn.call(ctx, function(err){
195        if (err instanceof Error || toString.call(err) === "[object Error]") return done(err);
196        if (null != err) return done(new Error('done() invoked with non-Error: ' + err));
197        done();
198      });
199    } catch (err) {
200      done(err);
201    }
202    return;
203  }
204
205  if (this.asyncOnly) {
206    return done(new Error('--async-only option in use without declaring `done()`'));
207  }
208
209  // sync
210  try {
211    if (!this.pending) this.fn.call(ctx);
212    this.duration = new Date - start;
213    fn();
214  } catch (err) {
215    fn(err);
216  }
217};
Note: See TracBrowser for help on using the repository browser.