From a4e804a8cb8070c3e8e603f2161567a06e2bdbce Mon Sep 17 00:00:00 2001 From: Norman Date: Sun, 15 Oct 2017 09:15:30 -0700 Subject: [PATCH] [Fix] Fix premature end of tests (and running sibling tests) when test includes subtests Fixes #222. --- lib/test.js | 44 +++++++------- test/add-subtest-async.js | 1 + test/async_end.js | 21 +++++++ test/nested-sync-noplan-noend.js | 24 +++++--- test/nested_test_ordering.js | 98 ++++++++++++++++++++++++++++++++ 5 files changed, 156 insertions(+), 32 deletions(-) create mode 100644 test/async_end.js create mode 100644 test/nested_test_ordering.js diff --git a/lib/test.js b/lib/test.js index 127fe4ef..a7f18397 100644 --- a/lib/test.js +++ b/lib/test.js @@ -104,19 +104,11 @@ Test.prototype.test = function (name, opts, cb) { this.emit('test', t); t.on('prerun', function () { self.assertCount++; - }) + }); - if (!self._pendingAsserts()) { - nextTick(function () { - self._end(); - }); + if (!this._pendingAsserts()) { + this._runProgeny(); } - - nextTick(function() { - if (!self._plan && self.pendingCount == self._progeny.length) { - self._end(); - } - }); }; Test.prototype.comment = function (msg) { @@ -144,7 +136,6 @@ Test.prototype.timeoutAfter = function(ms) { } Test.prototype.end = function (err) { - var self = this; if (arguments.length >= 1 && !!err) { this.ifError(err); } @@ -153,18 +144,10 @@ Test.prototype.end = function (err) { this.fail('.end() called twice'); } this.calledEnd = true; - this._end(); + this._runProgeny(); }; Test.prototype._end = function (err) { - var self = this; - if (this._progeny.length) { - var t = this._progeny.shift(); - t.on('end', function () { self._end() }); - t.run(); - return; - } - if (!this.ended) this.emit('end'); var pendingAsserts = this._pendingAsserts(); if (!this._planError && this._plan !== undefined && pendingAsserts) { @@ -177,6 +160,21 @@ Test.prototype._end = function (err) { this.ended = true; }; +Test.prototype._runProgeny = function () { + var self = this; + if (this._progeny.length) { + var t = this._progeny.shift(); + t.on('end', function () { self._runProgeny() }); + nextTick(function() { + t.run(); + }); + return; + } + if(this.calledEnd || this._plan) { + this._end(); + } +}; + Test.prototype._exit = function () { if (this._plan !== undefined && !this._planError && this.assertCount !== this._plan) { @@ -298,9 +296,7 @@ Test.prototype._assert = function assert (ok, opts) { if (extra.exiting) { self._end(); } else { - nextTick(function () { - self._end(); - }); + self._runProgeny(); } } diff --git a/test/add-subtest-async.js b/test/add-subtest-async.js index 51a9d0b6..efb89060 100644 --- a/test/add-subtest-async.js +++ b/test/add-subtest-async.js @@ -7,5 +7,6 @@ test('parent', function (t) { st.pass('child'); st.end(); }); + t.end(); }, 100); }) diff --git a/test/async_end.js b/test/async_end.js new file mode 100644 index 00000000..a5239a43 --- /dev/null +++ b/test/async_end.js @@ -0,0 +1,21 @@ +var test = require('../'); + +test('async end', function(t) { + setTimeout(function() { + t.assert(!t.ended, '!t.ended'); + t.end(); + }, 200); +}); + +test('async end with subtest', function(t) { + setTimeout(function() { + t.assert(!t.ended, '!t.ended'); + t.end(); + }, 200); + + t.test('subtest', function(g) { + g.assert(!t.ended, 'subtest !t.ended'); + g.end(); + }); +}); + diff --git a/test/nested-sync-noplan-noend.js b/test/nested-sync-noplan-noend.js index 42735a80..aeb920d1 100644 --- a/test/nested-sync-noplan-noend.js +++ b/test/nested-sync-noplan-noend.js @@ -1,25 +1,32 @@ var tape = require('../'); var tap = require('tap'); var concat = require('concat-stream'); +var stripFullStack = require('./common').stripFullStack; tap.test('nested sync test without plan or end', function (tt) { tt.plan(1); var test = tape.createHarness(); var tc = function (rows) { - tt.same(rows.toString('utf8'), [ + tt.same(stripFullStack(rows.toString('utf8')), [ 'TAP version 13', '# nested without plan or end', + 'not ok 1 test timed out after 100ms', + ' ---', + ' operator: fail', + ' stack: |-', + ' Error: test timed out after 100ms', + ' [... stack stripped ...]', + ' ...', '# first', - 'ok 1 should be truthy', - '# second', 'ok 2 should be truthy', + '# second', + 'ok 3 should be truthy', '', - '1..2', - '# tests 2', + '1..3', + '# tests 3', '# pass 2', - '', - '# ok' + '# fail 1', ].join('\n') + '\n'); }; @@ -38,6 +45,7 @@ tap.test('nested sync test without plan or end', function (tt) { q.end() }, 10); }); - }); + t.timeoutAfter(100); + }); }); diff --git a/test/nested_test_ordering.js b/test/nested_test_ordering.js new file mode 100644 index 00000000..5abd4c3f --- /dev/null +++ b/test/nested_test_ordering.js @@ -0,0 +1,98 @@ + +var tape = require('../'); +var tap = require('tap'); +var concat = require('concat-stream'); + +var stripFullStack = require('./common').stripFullStack; + +tap.test('plan vs end: plan', function (tt) { + tt.plan(1); + + var test = tape.createHarness(); + test.createStream().pipe(concat(function (rows) { + tt.same(rows.toString('utf8'), [ + 'TAP version 13', + '# first', + 'ok 1 first test', + 'ok 2 t not ended', + 'ok 3 t has progeny', + '# second', + 'ok 4 second test', + '# third', + 'ok 5 third test', + '', + '1..5', + '# tests 5', + '# pass 5', + '', + '# ok' + ].join('\n') + '\n'); + })); + + test('first', function (t) { + t.plan(4); + setTimeout(function () { + t.ok(1, 'first test'); + t.ok(!t.ended, 't not ended'); + t.ok(t._progeny.length, 't has progeny'); + }, 200); + + t.test('second', function (t) { + t.plan(1); + t.ok(1, 'second test'); + }); + }); + + test('third', function (t) { + t.plan(1); + setTimeout(function () { + t.ok(1, 'third test'); + }, 100); + }); +}); + +tap.test('plan vs end: end', function (tt) { + tt.plan(1); + + var test = tape.createHarness(); + test.createStream().pipe(concat(function (rows) { + tt.same(rows.toString('utf8'), [ + 'TAP version 13', + '# first', + 'ok 1 first test', + 'ok 2 t not ended', + 'ok 3 t has progeny', + '# second', + 'ok 4 second test', + '# third', + 'ok 5 third test', + '', + '1..5', + '# tests 5', + '# pass 5', + '', + '# ok' + ].join('\n') + '\n'); + })); + + test('first', function (t) { + setTimeout(function () { + t.ok(1, 'first test'); + t.ok(!t.ended, 't not ended'); + t.ok(t._progeny.length, 't has progeny'); + t.end(); + }, 200); + + t.test('second', function (t) { + t.ok(1, 'second test'); + t.end(); + }); + }); + + test('third', function (t) { + setTimeout(function () { + t.ok(1, 'third test'); + t.end(); + }, 100); + }); +});