Skip to content

Commit 4d481a3

Browse files
committed
Handle buffered tests much more simply
This also updates to make buffered child bailouts less clunky
1 parent 1b2caa4 commit 4d481a3

File tree

2 files changed

+120
-101
lines changed

2 files changed

+120
-101
lines changed

lib/test.js

+72-68
Original file line numberDiff line numberDiff line change
@@ -566,28 +566,17 @@ Test.prototype._runChild = function runChild (child, name, extra, cb) {
566566
self._currentChild = null
567567
var time = results ? '# time=' + results.time + 'ms' : ''
568568
if (extra.buffered) {
569-
var bail = false
570-
if (self._bail && !child._ok) {
571-
bail = true
572-
self._bail = false
573-
}
574-
575569
var endThis = false
576570
if (self._plan === self._count + 1) {
577571
endThis = true
578572
self._ending = true
579573
}
580574

581-
self.ok(child._ok, child._name + ' {', { diagnostic: false })
582-
var b = child._buffer.trim().split('\n').join('\n ') + '\n'
583-
b = b.split('\n \n').join('\n\n')
584-
if (time) {
585-
b += ' ' + time + '\n'
586-
}
587-
self.push(' ' + b + '}\n')
588-
if (bail) {
589-
self.bailout(child._name)
590-
} else if (endThis) {
575+
self.ok(child._ok, child._name, {
576+
_tapChildBuffer: child._buffer
577+
})
578+
579+
if (endThis) {
591580
self._ending = false
592581
self.end(IMPLICIT)
593582
}
@@ -598,16 +587,16 @@ Test.prototype._runChild = function runChild (child, name, extra, cb) {
598587
self.push('\n')
599588
}
600589
})
590+
601591
child.on('bailout', function (message) {
602592
if (extra.buffered) {
593+
self._currentChild = null
603594
self._bail = false
604-
if (!ended) {
605-
child.emit('end')
606-
}
607-
self.bailout(message)
608-
} else {
609-
rootBail(self, message)
595+
self.fail(child._name, {
596+
_tapChildBuffer: child._buffer
597+
})
610598
}
599+
rootBail(self, message)
611600
})
612601

613602
// still need try/catch for synchronous errors
@@ -709,7 +698,7 @@ Test.prototype.stdin = function (name, extra, deferred) {
709698
self._processQueue()
710699
})
711700

712-
parser.on('bailout', function (message) {
701+
parser.once('bailout', function (message) {
713702
rootBail(self, message)
714703
})
715704

@@ -732,14 +721,17 @@ function pruneFailures (res) {
732721
function rootBail (self, message) {
733722
var p = self
734723
while (p._parent) {
724+
// end any buffered tests along the way
725+
if (p._buffered) {
726+
p.emit('end')
727+
}
735728
p._bailedOut = true
736729
p = p._parent
737730
}
738731
p.bailout(message)
739732
}
740733

741734
function childStream (self, child, parser) {
742-
var bailedOut = false
743735
var emitter = parser || child
744736

745737
if (parser) {
@@ -754,17 +746,14 @@ function childStream (self, child, parser) {
754746
rootBail(self, reason)
755747
})
756748

757-
emitter.on('line', function (line) {
758-
if (line.match(/^TAP version \d+\n$/)) {
749+
var sawVersion = false
750+
emitter.on('line', function onEmitterLine (line) {
751+
if (!sawVersion && line.match(/^TAP version \d+\n$/)) {
752+
sawVersion = true
759753
return
760754
}
761755

762-
if (line.trim()) {
763-
line = ' ' + line
764-
} else {
765-
line = '\n'
766-
}
767-
756+
line = ' ' + line
768757
self.push(line)
769758
})
770759
}
@@ -879,9 +868,11 @@ Test.prototype.spawn = function spawnTest (cmd, args, options, name, extra, defe
879868
childStream(self, child.stdout, parser)
880869
} else {
881870
var buffer = ''
871+
var sawVersion = false
882872
child.stdout.pipe(parser)
883873
parser.on('line', function (line) {
884-
if (line.match(/TAP version \d+\n$/)) {
874+
if (!sawVersion && line.match(/^TAP version \d+\n$/)) {
875+
sawVersion = true
885876
return
886877
}
887878
buffer += line
@@ -943,40 +934,27 @@ Test.prototype.spawn = function spawnTest (cmd, args, options, name, extra, defe
943934

944935
var ok = results.ok && !code && !signal
945936
if (extra.buffered) {
946-
if (extra.skip) {
947-
if (typeof extra.skip === 'string') {
948-
extra.skip += ' {'
949-
} else {
950-
extra.skip = ' {'
951-
}
952-
} else if (extra.todo) {
953-
if (typeof extra.todo === 'string') {
954-
extra.todo += ' {'
955-
} else {
956-
extra.todo = ' {'
957-
}
958-
} else {
959-
name += ' {'
960-
}
961-
extra.diagnostic = false
962-
963-
self.ok(ok, name, extra)
964-
var b = buffer.trim().split('\n').join('\n ') + '\n'
965-
b = b.split('\n \n').join('\n\n')
966-
b += ' ' + time + '\n'
967-
self.push(' ' + b + '}\n')
968-
} else {
969-
self.ok(ok, name, extra)
937+
extra._tapChildBuffer = buffer
970938
}
939+
self.ok(ok, name, extra)
971940
if (!self._ended) {
972941
self.push('\n')
973942
}
943+
if (child._bailedOut) {
944+
rootBail(self, child._bailedOut || name)
945+
}
974946
deferred.resolve(self)
975947
self._processQueue()
976948
})
977949

978-
parser.on('bailout', function (message) {
979-
child.kill('SIGTERM')
950+
parser.once('bailout', function (message) {
951+
// bailouts have 1 second to quit before being forcibly killed.
952+
var timer = setTimeout(function () {
953+
child.kill('SIGKILL')
954+
}, 1000)
955+
timer.unref()
956+
child.once('close', clearTimeout.bind(null, timer))
957+
child._bailedOut = message
980958
})
981959

982960
if (child.stderr) {
@@ -1033,9 +1011,9 @@ function bailOnFail (self, stream, parser, root) {
10331011
})
10341012

10351013
parser.on('assert', function (res) {
1036-
if (!res.todo && !res.skip && !res.ok) {
1014+
if (!res.todo && !res.skip && !res.ok && res.name.slice(-1) !== '{') {
10371015
var ind = new Array(parser.level + 1).join(' ')
1038-
var line = ind + 'Bail out! # ' + res.name + '\n'
1016+
var line = ind + 'Bail out! # ' + res.name.trim() + '\n'
10391017
root.buffer = ''
10401018
root.write(line)
10411019
}
@@ -1376,6 +1354,12 @@ Test.prototype.printResult = function printResult (ok, message, extra) {
13761354
return
13771355
}
13781356

1357+
var buffer
1358+
if (extra._tapChildBuffer) {
1359+
buffer = extra._tapChildBuffer
1360+
delete extra._tapChildBuffer
1361+
}
1362+
13791363
var res = { ok: ok, message: message, extra: extra }
13801364

13811365
if (extra.todo) {
@@ -1404,6 +1388,10 @@ Test.prototype.printResult = function printResult (ok, message, extra) {
14041388

14051389
message = message.replace(/[\n\r]/g, ' ').replace(/\t/g, ' ')
14061390

1391+
if (buffer) {
1392+
message += ' {'
1393+
}
1394+
14071395
this.push(util.format('%s %d%s', ok ? 'ok' : 'not ok', n, message))
14081396

14091397
var ending = false
@@ -1431,11 +1419,30 @@ Test.prototype.printResult = function printResult (ok, message, extra) {
14311419
this.push('\n')
14321420

14331421
// If we're skipping, no need for diags.
1434-
if (!ok && !extra.skip || extra.diagnostic) {
1435-
this.writeDiags(extra)
1422+
// Also, never print diags if there's a child buffer to show
1423+
if (buffer) {
1424+
var b = buffer.trim().split('\n').join('\n ') + '\n'
1425+
this.push(' ' + b + '}\n')
1426+
} else {
1427+
var diagnostic = process.env.TAP_DIAG === '1'
1428+
if (!ok) {
1429+
diagnostic = true
1430+
}
1431+
if (extra.skip) {
1432+
diagnostic = false
1433+
}
1434+
if (process.env.TAP_DIAG === '0') {
1435+
diagnostic = false
1436+
}
1437+
if (typeof extra.diagnostic === 'boolean') {
1438+
diagnostic = extra.diagnostic
1439+
}
1440+
if (diagnostic) {
1441+
this.writeDiags(extra)
1442+
}
14361443
}
14371444

1438-
if (this._bail && !ok && !extra.skip && !extra.todo) {
1445+
if (this._bail && !ok && !extra.skip && !extra.todo && message.slice(-1) !== '{') {
14391446
this.bailout(message.replace(/^ - /, ''))
14401447
}
14411448

@@ -1634,9 +1641,6 @@ Test.prototype.push = function (c, e) {
16341641
return
16351642
}
16361643

1637-
if (!line.trim())
1638-
line = ''
1639-
16401644
this.emit('line', line + '\n')
16411645
}, this)
16421646

@@ -1662,7 +1666,7 @@ Test.prototype.bailout = function (message) {
16621666
this._bailedOut = true
16631667
this._ok = false
16641668
this.emit('bailout', message)
1665-
this.push(null)
1669+
this.end(IMPLICIT)
16661670
}
16671671

16681672
Test.prototype._addMoment = function (which, fn) {

test/segv.js

+48-33
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ var fs = require('fs')
99
var spawn = require('child_process').spawn
1010

1111
var segv =
12-
'int main (void) {\n' +
12+
'int main (void) {\n' +
1313
' char *s = "hello world";\n' +
1414
" *s = 'H';\n" +
1515
'}\n'
@@ -18,34 +18,41 @@ var segv =
1818
// we slice the actual output later to just compare the fronts
1919
// also sort the lines, because Node v0.8 sets keys on objects
2020
// in different order.
21-
var expect =
22-
('TAP version 13\n' +
23-
'# Subtest: ./segv\n' +
24-
' 1..0\n' +
25-
'not ok 1 - ./segv # time=\n' +
26-
' ---\n' +
27-
' at:\n' +
28-
' file: test/segv.js\n' +
29-
' line: \n' +
30-
' column: \n' +
31-
' results:\n' +
32-
' ok: false\n' +
33-
' count: 0\n' +
34-
' pass: 0\n' +
35-
' plan:\n' +
36-
' start: 1\n' +
37-
' end: 0\n' +
38-
' skipAll: true\n' +
39-
' signal: SIG\n' +
40-
' command: ./segv\n' +
41-
' arguments: []\n' +
42-
' source: |\n' +
43-
" tt.spawn('./segv')\n" +
44-
' ...\n' +
45-
'\n' +
46-
'1..1\n' +
47-
'# failed 1 of 1 tests\n' +
48-
'# time=\n').trim().split('\n')
21+
var expect = [
22+
'TAP version 13',
23+
'# Subtest: ./segv ',
24+
' 1..0',
25+
'not ok 1 - ./segv # time=',
26+
' ---',
27+
' at:',
28+
' line: ',
29+
' column: ',
30+
' file: test/segv.js',
31+
' results:',
32+
' ok: false',
33+
' count: 0',
34+
' pass: 0',
35+
' fail: 0',
36+
' bailout: false',
37+
' todo: 0',
38+
' skip: 0',
39+
' plan:',
40+
' start: 1',
41+
' end: 0',
42+
' skipAll: true',
43+
' skipReason: \'\'',
44+
' comment: \'\'',
45+
' signal: SIG',
46+
' command: ./segv',
47+
' arguments: []',
48+
' source: |',
49+
' tt.spawn(\'./segv\')',
50+
' ...',
51+
'',
52+
'1..1',
53+
'# failed 1 of 1 tests',
54+
'# time='
55+
]
4956

5057
test('setup', function (t) {
5158
fs.writeFile('segv.c', segv, 'utf8', function (er) {
@@ -72,21 +79,29 @@ test('segv', function (t) {
7279
res += c
7380
})
7481
tt.on('end', function () {
82+
// TODO: parse the yaml and test it against an object pattern
83+
// much more future-proof than this string monkeybusiness.
7584
res = res.trim().split('\n')
7685
res = res.sort()
7786
expect = expect.sort()
7887
var ok = true
79-
expect.forEach(function (line, i) {
80-
if (ok) {
81-
ok = t.equal(res[i].substr(0, line.length), line)
88+
for (var e = 0, r = 0;
89+
e < expect.length && r < res.length && ok;
90+
e++, r++) {
91+
// skip the type: global that newer nodes add to the error.
92+
if (res[r].match(/^\s*type: global$/)) {
93+
e --
94+
continue
8295
}
83-
})
96+
ok = t.equal(res[r].substr(0, expect[e].length), expect[e])
97+
}
8498
t.end()
8599
})
86100
tt.end()
87101
})
88102

89103
test('cleanup', function (t) {
104+
return t.end()
90105
t.plan(2)
91106
fs.unlink('segv', function (er) {
92107
t.ifError(er, 'clean up segv')

0 commit comments

Comments
 (0)