From 55e62905ab693f7798d6799d6ad8ace8a2c135ce Mon Sep 17 00:00:00 2001 From: Glen Mailer Date: Sat, 7 Dec 2013 16:21:49 +0000 Subject: [PATCH] Gracefully exit on SIGINT --- bin/_mocha | 4 +++- lib/runner.js | 26 +++++++++++++++++++++----- test/acceptance/misc/many.js | 27 +++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 test/acceptance/misc/many.js diff --git a/bin/_mocha b/bin/_mocha index 32c82a06cc..92fbdc5296 100644 --- a/bin/_mocha +++ b/bin/_mocha @@ -348,12 +348,14 @@ if (program.watch) { // load mocha.files = files; -mocha.run(program.exit ? process.exit : exitLater); +var runner = mocha.run(program.exit ? process.exit : exitLater); function exitLater(code) { process.on('exit', function() { process.exit(code) }) } +process.on('SIGINT', function() { runner.abort(); }) + // enable growl notifications function growl(runner, reporter) { diff --git a/lib/runner.js b/lib/runner.js index f01fc6aeee..39f447daa0 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -51,6 +51,7 @@ module.exports = Runner; function Runner(suite) { var self = this; this._globals = []; + this._aborting = false; this.suite = suite; this.total = suite.total(); this.failures = 0; @@ -205,12 +206,12 @@ Runner.prototype.fail = function(test, err){ * - If bail, then exit * - Failed `before` hook skips all tests in a suite and subsuites, * but jumps to corresponding `after` hook - * - Failed `before each` hook skips remaining tests in a + * - Failed `before each` hook skips remaining tests in a * suite and jumps to corresponding `after each` hook, * which is run only once * - Failed `after` hook does not alter * execution order - * - Failed `after each` hook skips remaining tests in a + * - Failed `after each` hook skips remaining tests in a * suite and subsuites, but executes other `after each` * hooks * @@ -259,7 +260,7 @@ Runner.prototype.hook = function(name, fn){ var testError = hook.error(); if (testError) self.fail(self.test, testError); if (err) { - self.failHook(hook, err); + self.failHook(hook, err); // stop executing hooks, notify callee of hook err return fn(err); @@ -397,7 +398,7 @@ Runner.prototype.runTests = function(suite, fn){ // for failed 'after each' hook start from errSuite parent, // otherwise start from errSuite itself self.suite = after ? errSuite.parent : errSuite; - + if (self.suite) { // call hookUp afterEach self.hookUp('afterEach', function(err2, errSuite2) { @@ -408,7 +409,7 @@ Runner.prototype.runTests = function(suite, fn){ fn(errSuite); }); } else { - // there is no need calling other 'after each' hooks + // there is no need calling other 'after each' hooks self.suite = orig; fn(errSuite); } @@ -418,6 +419,8 @@ Runner.prototype.runTests = function(suite, fn){ // if we bail after first err if (self.failures && suite._bail) return fn(); + if (self._aborting) return fn(); + if (err) return hookErr(err, errSuite, true); // next test @@ -500,6 +503,8 @@ Runner.prototype.runSuite = function(suite, fn){ } } + if (self._aborting) return done(); + var curr = suite.suites[i++]; if (!curr) return done(); self.runSuite(curr, next); @@ -584,6 +589,17 @@ Runner.prototype.run = function(fn){ return this; }; +/** + * Cleanly abort execution + * + * @return {Runner} for chaining + * @api public + */ +Runner.prototype.abort = function(){ + debug('aborting'); + this._aborting = true; +} + /** * Filter leaks with the given globals flagged as `ok`. * diff --git a/test/acceptance/misc/many.js b/test/acceptance/misc/many.js new file mode 100644 index 0000000000..c479ae43ee --- /dev/null +++ b/test/acceptance/misc/many.js @@ -0,0 +1,27 @@ +// Useful for testing SIGINT handler +// use env.big_number to tune iterations so that you have time to ctrl+c + +describe('a load of tests', function(){ + it('should fail the first test', function(){ + throw new Error('this should appear in the summary'); + }) + + var iterations = (process.env.big_number || 1e7); + function work() { + var a = 0; + for(var i=0; i