diff --git a/Makefile b/Makefile index f62d0023af..6afdbac32c 100644 --- a/Makefile +++ b/Makefile @@ -60,11 +60,11 @@ test-outputs/issue1327/case-out.json: test/regression/issue1327/case.js test-failing: ./bin/mocha \ --reporter $(REPORTER) \ - test/acceptance/failing/timeout.js > /dev/null 2>&1 ; \ + test/acceptance/failing > /dev/null 2>&1 ; \ failures="$$?" ; \ - if [ "$$failures" != '2' ] ; then \ + if [ "$$failures" != '4' ] ; then \ echo 'test-failing:' ; \ - echo " expected 2 failing tests but saw $$failures" ; \ + echo " expected 4 failing tests but saw $$failures" ; \ exit 1 ; \ fi diff --git a/lib/runnable.js b/lib/runnable.js index 7ce2f5cf90..6e4803fd7f 100644 --- a/lib/runnable.js +++ b/lib/runnable.js @@ -211,6 +211,10 @@ Runnable.prototype.run = function(fn){ var ms = self.timeout(); if (self.timedOut) return; if (finished) return multiple(err || self._trace); + + // Discard the resolution if this test has already failed asynchronously + if (self.state) return; + self.clearTimeout(); self.duration = new Date - start; finished = true; diff --git a/lib/runner.js b/lib/runner.js index 634c4b2882..4718f0fb9f 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -573,12 +573,11 @@ Runner.prototype.uncaught = function(err){ var runnable = this.currentRunnable; if (!runnable) return; - var wasAlreadyDone = runnable.state; - this.fail(runnable, err); - runnable.clearTimeout(); - if (wasAlreadyDone) return; + // Ignore errors if complete + if (runnable.state) return; + this.fail(runnable, err); // recover from test if ('test' == runnable.type) { diff --git a/test/acceptance/failing/uncaught-and-async.js b/test/acceptance/failing/uncaught-and-async.js new file mode 100644 index 0000000000..054d1d857b --- /dev/null +++ b/test/acceptance/failing/uncaught-and-async.js @@ -0,0 +1,26 @@ +'use strict'; + +/** + * This file should only generate one failure per spec despite the fact that + * Mocha is capable of detecting two distinct exceptions during test execution. + */ + +it('fails exactly once when a global error is thrown first', function(done) { + setTimeout(function() { + throw new Error('global error'); + + setTimeout(function() { + done(new Error('test error')); + }, 0); + }, 0); +}); + +it('fails exactly once when a global error is thrown second', function(done) { + setTimeout(function() { + done(new Error('test error')); + }, 0); + + setTimeout(function() { + throw new Error('global error'); + }, 0); +});