tap-parser
Advanced tools
Comparing version 1.3.2 to 2.0.0
@@ -19,2 +19,5 @@ #!/usr/bin/env node | ||
if (arg === '-t' || arg === '--tap') | ||
json = 'tap' | ||
if (arg === '-h' || arg === '--help') | ||
@@ -27,3 +30,3 @@ usage() | ||
Usage: | ||
tap-parser [-j [<indent>] | --json[=indent]] | ||
tap-parser [-j [<indent>] | --json[=indent] | -t | --tap] | ||
@@ -39,2 +42,6 @@ Parses TAP data from stdin, and outputs an object representing | ||
of spaces to use as the indent (default=2). | ||
If you pass -t or --tap as an argument, then the output will be a | ||
re-imagined synthesized purified idealized manufactured TAP stream, | ||
rather than JSON or util.format. | ||
*/}.toString().split('\n').slice(1, -1).join('\n')) | ||
@@ -48,4 +55,52 @@ | ||
var yaml = require('js-yaml') | ||
function tapFormat (msg, indent) { | ||
return indent + msg.map(function (item) { | ||
switch (item[0]) { | ||
case 'child': | ||
return tapFormat(item[1], ' ') | ||
case 'version': | ||
return 'TAP version ' + item[1] + '\n' | ||
case 'plan': | ||
var p = item[1].start + '..' + item[1].end | ||
if (item[1].comment) | ||
p += ' # ' + item[1].comment | ||
return p + '\n' | ||
case 'pragma': | ||
return 'pragma ' + (item[2] ? '+' : '-') + item[1] + '\n' | ||
case 'bailout': | ||
var r = item[1] === true ? '' : item[1] | ||
return 'Bail out!' + r + '\n' | ||
case 'assert': | ||
var res = item[1] | ||
return (res.ok ? '' : 'not ') + 'ok ' + res.id + | ||
(res.name ? ' - ' + res.name : '') + | ||
(res.skip ? ' # SKIP' + | ||
(res.skip === true ? '' : ' ' + res.skip) : '') + | ||
(res.todo ? ' # TODO' + | ||
(res.todo === true ? '' : ' ' + res.todo) : '') + | ||
(res.time ? ' # time=' + res.time + 's' : '') + | ||
'\n' + | ||
(res.diags ? | ||
' ---\n ' + | ||
yaml.safeDump(res.diags).split('\n').join('\n ').trim() + | ||
'\n ...\n' | ||
: '') | ||
case 'extra': | ||
case 'comment': | ||
return item[1] | ||
} | ||
}).join('').split('\n').join('\n' + indent).trim() + '\n' | ||
} | ||
function format (msg) { | ||
if (json !== null) | ||
if (json === 'tap') | ||
return tapFormat(msg, '') | ||
else if (json !== null) | ||
return JSON.stringify(msg, null, +json) | ||
@@ -52,0 +107,0 @@ else |
613
index.js
// Transforms a stream of TAP into a stream of result objects | ||
// and string comments. Emits "results" event with summary. | ||
var Writable = require('stream').Writable | ||
/* istanbul ignore if */ | ||
if (!Writable) { | ||
@@ -21,4 +22,26 @@ try { | ||
var testPointRE = /^(not )?ok(?: ([0-9]+))?(?:(?: - )?(.*))?\n$/ | ||
// every line outside of a yaml block is one of these things, or | ||
// a comment, or garbage. | ||
var lineTypes = { | ||
testPoint: /^(not )?ok(?: ([0-9]+))?(?:(?: -)?( .*))?\n$/, | ||
pragma: /^pragma ([+-])([a-z]+)\n$/, | ||
bailout: /^bail out!(.*)\n$/i, | ||
version: /^TAP version ([0-9]+)\n$/i, | ||
plan: /^([0-9]+)\.\.([0-9]+)(?:\s+(?:#\s*(.*)))?\n$/, | ||
subtest: /^# Subtest(?:: (.*))?\n$/, | ||
subtestIndent: /^ # Subtest(?:: (.*))?\n$/, | ||
comment: /^\s*#.*\n$/ | ||
} | ||
var lineTypeNames = Object.keys(lineTypes) | ||
function lineType (line) { | ||
for (var t in lineTypes) { | ||
var match = line.match(lineTypes[t]) | ||
if (match) | ||
return [t, match] | ||
} | ||
return null | ||
} | ||
function parseDirective (line) { | ||
@@ -29,4 +52,8 @@ line = line.trim() | ||
var n = +time[1] | ||
if (time[2] === 's') | ||
n *= 1000 | ||
if (time[2] === 's') { | ||
// JS does weird things with floats. Round it off a bit. | ||
n *= 1000000 | ||
n = Math.round(n) | ||
n /= 1000 | ||
} | ||
return [ 'time', n ] | ||
@@ -42,6 +69,3 @@ } | ||
function Result (line, count) { | ||
var parsed = line.match(testPointRE) | ||
assert(parsed, 'invalid line to Result') | ||
function Result (parsed, count) { | ||
var ok = !parsed[1] | ||
@@ -52,12 +76,2 @@ var id = +(parsed[2] || count + 1) | ||
var src = line | ||
Object.defineProperty(this, 'src', { | ||
value: line, | ||
writable: true, | ||
enumerable: false, | ||
configurable: false | ||
}) | ||
this.src = line | ||
var rest = parsed[3] || '' | ||
@@ -82,11 +96,2 @@ var name | ||
Object.defineProperty(Result.prototype, 'toString', { | ||
value: function () { | ||
return this.src | ||
}, | ||
enumerable: false, | ||
writable: true, | ||
configurable: true | ||
}) | ||
function Parser (options, onComplete) { | ||
@@ -107,3 +112,2 @@ if (typeof options === 'function') { | ||
this.failures = [] | ||
this.indent = options.indent || '' | ||
this.level = options.level || 0 | ||
@@ -120,3 +124,5 @@ Writable.call(this) | ||
this.current = null | ||
this.commentQueue = [] | ||
this.maybeSubtest = null | ||
this.extraQueue = [] | ||
this.buffered = options.buffered || null | ||
@@ -131,2 +137,3 @@ this.count = 0 | ||
this.strict = false | ||
this.pragmas = { strict: false } | ||
@@ -136,8 +143,39 @@ this.postPlan = false | ||
Parser.prototype.createResult = function (line) { | ||
if (!testPointRE.test(line)) | ||
return null | ||
Parser.prototype.tapError = function (error) { | ||
this.ok = false | ||
this.fail ++ | ||
if (typeof error === 'string') { | ||
error = { | ||
tapError: error | ||
} | ||
} | ||
this.failures.push(error) | ||
} | ||
Parser.prototype.parseTestPoint = function (testPoint) { | ||
this.emitResult() | ||
return new Result(line, this.count) | ||
var res = new Result(testPoint, this.count) | ||
if (this.planStart !== -1) { | ||
var lessThanStart = +res.id < this.planStart | ||
var greaterThanEnd = +res.id > this.planEnd | ||
if (lessThanStart || greaterThanEnd) { | ||
if (lessThanStart) | ||
res.tapError = 'id less than plan start' | ||
else | ||
res.tapError = 'id greater than plan end' | ||
this.tapError(res) | ||
} | ||
} | ||
this.sawValidTap = true | ||
if (res.id) { | ||
if (!this.first || res.id < this.first) | ||
this.first = res.id | ||
if (!this.last || res.id > this.last) | ||
this.last = res.id | ||
} | ||
// hold onto it, because we might get yamlish diagnostics | ||
this.current = res | ||
} | ||
@@ -147,21 +185,73 @@ | ||
if (this.strict) { | ||
this.failures.push({ | ||
this.tapError({ | ||
tapError: 'Non-TAP data encountered in strict mode', | ||
data: data | ||
}) | ||
this.ok = false | ||
} | ||
this.emit('extra', data) | ||
if (this.current || this.extraQueue.length) | ||
this.extraQueue.push(['extra', data]) | ||
else | ||
this.emit('extra', data) | ||
} | ||
Parser.prototype.processYamlish = function () { | ||
var yamlish = this.yamlish | ||
this.yamlish = '' | ||
this.yind = '' | ||
Parser.prototype.plan = function (start, end, comment, line) { | ||
// not allowed to have more than one plan | ||
if (this.planStart !== -1) { | ||
this.nonTap(line) | ||
return | ||
} | ||
if (!this.current) { | ||
this.nonTap(yamlish) | ||
// can't put a plan in a child. | ||
if (this.child || this.yind) { | ||
this.nonTap(line) | ||
return | ||
} | ||
this.sawValidTap = true | ||
this.emitResult() | ||
this.planStart = start | ||
this.planEnd = end | ||
var p = { start: start, end: end } | ||
if (comment) | ||
this.planComment = p.comment = comment | ||
// This means that the plan is coming at the END of all the tests | ||
// Plans MUST be either at the beginning or the very end. We treat | ||
// plans like '1..0' the same, since they indicate that no tests | ||
// will be coming. | ||
if (this.count !== 0 || this.planEnd === 0) | ||
this.postPlan = true | ||
this.emit('plan', p) | ||
} | ||
Parser.prototype.resetYamlish = function () { | ||
this.yind = '' | ||
this.yamlish = '' | ||
} | ||
// that moment when you realize it's not what you thought it was | ||
Parser.prototype.yamlGarbage = function () { | ||
var yamlGarbage = this.yind + '---\n' + this.yamlish | ||
this.emitResult() | ||
this.nonTap(yamlGarbage) | ||
} | ||
Parser.prototype.yamlishLine = function (line) { | ||
if (line === this.yind + '...\n') { | ||
// end the yaml block | ||
this.processYamlish() | ||
} else if (lineTypes.comment.test(line)) { | ||
this.emitComment(line) | ||
} else { | ||
this.yamlish += line | ||
} | ||
} | ||
Parser.prototype.processYamlish = function () { | ||
var yamlish = this.yamlish | ||
this.resetYamlish() | ||
try { | ||
@@ -174,3 +264,2 @@ var diags = yaml.safeLoad(yamlish) | ||
this.current.src += yamlish | ||
this.current.diag = diags | ||
@@ -234,15 +323,22 @@ this.emitResult() | ||
if (this.planEnd === 0 && this.planStart === 1) { | ||
this.ok = true | ||
skipAll = true | ||
} else if (this.count !== (this.planEnd - this.planStart + 1)) | ||
this.ok = false | ||
if (this.count === 0) { | ||
this.ok = true | ||
} else { | ||
this.tapError('Plan of 1..0, but test points encountered') | ||
} | ||
} else if (!this.bailedOut && this.planStart === -1) { | ||
this.tapError('no plan') | ||
} else if (this.ok && this.count !== (this.planEnd - this.planStart + 1)) { | ||
this.tapError('incorrect number of tests') | ||
} | ||
if (this.ok && !skipAll && this.first !== this.planStart) { | ||
this.tapError('first test id does not match plan start') | ||
} | ||
if (this.ok && !skipAll && this.first !== this.planStart) | ||
this.ok = false | ||
if (this.ok && !skipAll && this.last !== this.planEnd) { | ||
this.tapError('last test id does not match plan end') | ||
} | ||
if (this.ok && !skipAll && this.last !== this.planEnd) | ||
this.ok = false | ||
var final = { | ||
@@ -282,4 +378,7 @@ ok: this.ok, | ||
if (this.failures.length) | ||
if (this.failures.length) { | ||
final.failures = this.failures | ||
} else { | ||
final.failures = [] | ||
} | ||
@@ -291,3 +390,29 @@ this.emit('complete', final) | ||
Parser.prototype.version = function (version, line) { | ||
// If version is specified, must be at the very beginning. | ||
if (version >= 13 && this.planStart === -1 && this.count === 0) | ||
this.emit('version', version) | ||
else | ||
this.nonTap(line) | ||
} | ||
Parser.prototype.pragma = function (key, value, line) { | ||
// can't put a pragma in a child or yaml block | ||
if (this.child) { | ||
this.nonTap(line) | ||
return | ||
} | ||
this.emitResult() | ||
// only the 'strict' pragma is currently relevant | ||
if (key === 'strict') { | ||
this.strict = value | ||
} | ||
this.pragmas[key] = value | ||
this.emit('pragma', key, value) | ||
} | ||
Parser.prototype.bailout = function (reason) { | ||
this.sawValidTap = true | ||
this.emitResult() | ||
this.bailedOut = reason || true | ||
@@ -298,10 +423,10 @@ this.ok = false | ||
Parser.prototype.clearCommentQueue = function () { | ||
for (var c = 0; c < this.commentQueue.length; c++) { | ||
this.emit('comment', this.commentQueue[c]) | ||
Parser.prototype.clearExtraQueue = function () { | ||
for (var c = 0; c < this.extraQueue.length; c++) { | ||
this.emit(this.extraQueue[c][0], this.extraQueue[c][1]) | ||
} | ||
this.commentQueue.length = 0 | ||
this.extraQueue.length = 0 | ||
} | ||
Parser.prototype.emitResult = function () { | ||
Parser.prototype.endChild = function () { | ||
if (this.child) { | ||
@@ -311,8 +436,10 @@ this.child.end() | ||
} | ||
} | ||
this.yamlish = '' | ||
this.yind = '' | ||
Parser.prototype.emitResult = function () { | ||
this.endChild() | ||
this.resetYamlish() | ||
if (!this.current) | ||
return this.clearCommentQueue() | ||
return this.clearExtraQueue() | ||
@@ -340,20 +467,29 @@ var res = this.current | ||
this.emit('assert', res) | ||
this.clearCommentQueue() | ||
this.clearExtraQueue() | ||
} | ||
Parser.prototype.startChild = function (indent, line) { | ||
if (!line.substr(indent.length).match(/^# Subtest:/)) { | ||
this.nonTap(line) | ||
return | ||
} | ||
// TODO: We COULD say that any "relevant tap" line that's indented | ||
// by 4 spaces starts a child test, and just call it 'unnamed' if | ||
// it does not have a prefix comment. In that case, any number of | ||
// 4-space indents can be plucked off to try to find a relevant | ||
// TAP line type, and if so, start the unnamed child. | ||
Parser.prototype.startChild = function (line) { | ||
var maybeBuffered = this.current && this.current.name && this.current.name.substr(-1) === '{' | ||
var unindentStream = !maybeBuffered && this.maybeChild | ||
var indentStream = !maybeBuffered && !unindentStream && | ||
lineTypes.subtestIndent.test(line) | ||
var unnamed = !maybeBuffered && !unindentStream && !indentStream | ||
this.emitResult() | ||
// If we have any other result waiting in the wings, we need to emit | ||
// that now. A buffered test emits its test point at the *end* of | ||
// the child subtest block, so as to match streamed test semantics. | ||
if (!maybeBuffered) | ||
this.emitResult() | ||
this.child = new Parser({ | ||
indent: indent, | ||
parent: this, | ||
level: this.level + 1 | ||
level: this.level + 1, | ||
buffered: maybeBuffered | ||
}) | ||
this.emit('child', this.child) | ||
this.child.on('bailout', this.bailout.bind(this)) | ||
@@ -365,8 +501,33 @@ var self = this | ||
}) | ||
this.child.write(line.substr(indent.length)) | ||
// Canonicalize the parsing result of any kind of subtest | ||
// if it's a buffered subtest or a non-indented Subtest directive, | ||
// then synthetically emit the Subtest comment | ||
line = line.substr(4) | ||
var subtestComment | ||
if (indentStream) { | ||
subtestComment = line | ||
line = null | ||
} else if (maybeBuffered) { | ||
var n = this.current.name.trim().replace(/{$/, '').trim() | ||
subtestComment = '# Subtest: ' + n + '\n' | ||
} else { | ||
subtestComment = this.maybeChild || '# Subtest: (anonymous)\n' | ||
} | ||
this.maybeChild = null | ||
// at some point, we may wish to move 100% to preferring | ||
// the Subtest comment on the parent level. If so, uncomment | ||
// this line, and remove the child.emitComment below. | ||
// this.emit('comment', subtestComment) | ||
this.emit('child', this.child) | ||
this.child.emitComment(subtestComment) | ||
if (line) | ||
this.child._parse(line) | ||
} | ||
Parser.prototype.emitComment = function (line) { | ||
if (this.current || this.commentQueue.length) | ||
this.commentQueue.push(line) | ||
if (this.current || this.extraQueue.length) | ||
this.extraQueue.push(['comment', line]) | ||
else | ||
@@ -380,69 +541,42 @@ this.emit('comment', line) | ||
// ignore empty lines, except if they are (or could be) part of yaml | ||
// >\nfoo\n\nbar\n is yaml for `"foo\nbar"` | ||
// >\nfoo\nbar\n is yaml for `"foo bar"` | ||
if (line === '\n' || line.trim() === '') { | ||
if (this.child) { | ||
this.child.write('\n') | ||
} else if (this.yind) { | ||
this.yamlish += '\n' | ||
} | ||
return | ||
// sometimes empty lines get trimmed, but are still part of | ||
// a subtest or a yaml block. Otherwise, nothing to parse! | ||
if (line === '\n') { | ||
if (this.child) | ||
line = ' ' + line | ||
else if (this.yind) | ||
line = this.yind + line | ||
else | ||
return | ||
} | ||
// After a bailout, everything is ignored | ||
if (this.bailedOut) | ||
return | ||
// this is a line we are processing, so emit it, but don't | ||
// emit all the whitespace, because that's just annoyingly noisy. | ||
if (line.trim()) | ||
this.emit('line', line) | ||
this.emit('line', line) | ||
// The only Pragma supported is strict. Others may be added. | ||
var pragma = line.match(/^pragma ([+-])strict\n$/) | ||
if (pragma) { | ||
this.strict = pragma[1] === '+' | ||
this.emit('pragma', { strict: this.strict }) | ||
// check to see if the line is indented. | ||
// if it is, then it's either a subtest, yaml, or garbage. | ||
var indent = line.match(/^[ \t]*/)[0] | ||
if (indent) { | ||
this.parseIndent(line, indent) | ||
return | ||
} | ||
var bailout = line.match(/^bail out!(.*)\n$/i) | ||
if (bailout) { | ||
this.sawValidTap = true | ||
var reason = bailout[1].trim() | ||
this.bailout(reason) | ||
// buffered subtests must end with a } | ||
if (this.child && this.child.buffered && line === '}\n') { | ||
this.current.name = this.current.name.replace(/{$/, '').trim() | ||
this.emitResult() | ||
return | ||
} | ||
// If version is specified, must be at the very beginning. | ||
var version = line.match(/^TAP version ([0-9]+)\n$/i) | ||
if (version) { | ||
version = parseInt(version[1], 10) | ||
if (version >= 13 && this.planStart === -1 && this.count === 0) | ||
this.emit('version', version) | ||
else | ||
this.nonTap(line) | ||
// now we know it's not indented, so if it's either valid tap | ||
// or garbage. Get the type of line. | ||
var type = lineType(line) | ||
if (!type) { | ||
this.nonTap(line) | ||
return | ||
} | ||
// still belongs to the child. | ||
if (this.child) { | ||
if (line.indexOf(this.child.indent) === 0) { | ||
line = line.substr(this.child.indent.length) | ||
this.child.write(line) | ||
return | ||
} | ||
if (line.trim().charAt(0) === '#') { | ||
this.emitComment(line) | ||
return | ||
} | ||
// a child test can only end when we get an test point line. | ||
// anything else is extra. | ||
if (this.child.sawValidTap && !/^(not )?ok/.test(line)) { | ||
this.nonTap(line) | ||
return | ||
} | ||
} | ||
// comment, but let "# Subtest:" comments start a child | ||
var c = line.match(/^(\s+)?#(.*)/) | ||
if (c && !(c[1] && /^ Subtest: /.test(c[2]))) { | ||
if (type[0] === 'comment') { | ||
this.emitComment(line) | ||
@@ -452,4 +586,17 @@ return | ||
// if we got a plan at the end, or a 1..0 plan, then we can't | ||
// have any more results, yamlish, or child sets. | ||
// if we have any yamlish, it's garbage now. We tolerate non-TAP and | ||
// comments in the midst of yaml (though, perhaps, that's questionable | ||
// behavior), but any actual TAP means that the yaml block was just | ||
// not valid. | ||
if (this.yind) | ||
this.yamlGarbage() | ||
// If it's anything other than a comment or garbage, then any | ||
// maybeChild is just an unsatisfied promise. | ||
if (this.maybeChild) { | ||
this.emitComment(this.maybeChild) | ||
this.maybeChild = null | ||
} | ||
// nothing but comments can come after a trailing plan | ||
if (this.postPlan) { | ||
@@ -460,116 +607,140 @@ this.nonTap(line) | ||
var indent = line.match(/^[ \t]+/) | ||
if (indent) { | ||
indent = indent[0] | ||
// ok, now it's maybe a thing | ||
if (type[0] === 'bailout') { | ||
this.bailout(type[1][1].trim()) | ||
return | ||
} | ||
// if we don't have a current res, then it can't be yamlish. | ||
// If it is a subtest command, then it's a child test. | ||
if (!this.current) { | ||
this.startChild(indent, line) | ||
return | ||
} | ||
if (type[0] === 'pragma') { | ||
var pragma = type[1] | ||
this.pragma(pragma[2], pragma[1] === '+', line) | ||
return | ||
} | ||
// if we are not currently processing yamlish, then it has to | ||
// be either the start of a child, or the start of yamlish. | ||
if (!this.yind) { | ||
// either this starts yamlish, or it is a child. | ||
if (line === indent + '---\n') | ||
this.yind = indent | ||
else | ||
this.startChild(indent, line) | ||
return | ||
} | ||
if (type[0] === 'version') { | ||
var version = type[1] | ||
this.version(parseInt(version[1], 10), line) | ||
return | ||
} | ||
// now we know it is yamlish | ||
if (type[0] === 'plan') { | ||
var plan = type[1] | ||
this.plan(+plan[1], +plan[2], (plan[3] || '').trim(), line) | ||
return | ||
} | ||
// if it's not as indented, then it's broken. | ||
// The whole yamlish chunk is garbage. | ||
if (indent.indexOf(this.yind) !== 0) { | ||
// oops! was not actually yamlish, I guess. | ||
// treat as garbage | ||
this.nonTap(this.yamlish + line) | ||
this.emitResult() | ||
return | ||
} | ||
// yamlish ends with "...\n" | ||
if (line === this.yind + '...\n') { | ||
this.processYamlish() | ||
return | ||
} | ||
// streamed subtests will end when this test point is emitted | ||
if (type[0] === 'testPoint') { | ||
// note: it's weird, but possible, to have a testpoint ending in | ||
// { before a streamed subtest which ends with a test point | ||
// instead of a }. In this case, the parser gets confused, but | ||
// also, even beginning to handle that means doing a much more | ||
// involved multi-line parse. By that point, the subtest block | ||
// has already been emitted as a 'child' event, so it's too late | ||
// to really do the optimal thing. The only way around would be | ||
// to buffer up everything and do a multi-line parse. This is | ||
// rare and weird, and a multi-line parse would be a bigger | ||
// rewrite, so I'm allowing it as it currently is. | ||
this.parseTestPoint(type[1]) | ||
return | ||
} | ||
// ok! it is valid yamlish indentation, and not the ... | ||
// save it to parse later. | ||
this.yamlish += line | ||
// We already detected nontap up above, so the only case left | ||
// should be a `# Subtest:` comment. Ignore for coverage, but | ||
// include the error here just for good measure. | ||
/* istanbul ignore else */ | ||
if (type[0] === 'subtest') { | ||
// this is potentially a subtest. Not indented. | ||
// hold until later. | ||
this.maybeChild = line | ||
} else { | ||
throw new Error('Unhandled case: ' + type[0]) | ||
} | ||
} | ||
Parser.prototype.parseIndent = function (line, indent) { | ||
// still belongs to the child, so pass it along. | ||
if (this.child && line.substr(0, 4) === ' ') { | ||
line = line.substr(4) | ||
this.child.write(line) | ||
return | ||
} | ||
// not indented. if we were doing yamlish, then it didn't go good | ||
// one of: | ||
// - continuing yaml block | ||
// - starting yaml block | ||
// - ending yaml block | ||
// - body of a new child subtest that was previously introduced | ||
// - An indented subtest directive | ||
// - A comment, or garbage | ||
// continuing/ending yaml block | ||
if (this.yind) { | ||
this.nonTap(this.yamlish) | ||
this.yamlish = '' | ||
this.yind = '' | ||
} | ||
var plan = line.match(/^([0-9]+)\.\.([0-9]+)(?:\s+(?:#\s*(.*)))?\n$/) | ||
if (plan) { | ||
if (this.planStart !== -1) { | ||
// this is not valid tap, just garbage | ||
this.nonTap(line) | ||
if (line.indexOf(this.yind) === 0) { | ||
this.yamlishLine(line) | ||
return | ||
} else { | ||
// oops! that was not actually yamlish, I guess. | ||
// this is a case where the indent is shortened mid-yamlish block | ||
// treat existing yaml as garbage, continue parsing this line | ||
this.yamlGarbage() | ||
} | ||
} | ||
this.sawValidTap = true | ||
this.emitResult() | ||
var start = +(plan[1]) | ||
var end = +(plan[2]) | ||
var comment = plan[3] | ||
this.planStart = start | ||
this.planEnd = end | ||
var p = { start: start, end: end } | ||
if (comment) | ||
this.planComment = p.comment = comment | ||
this.emit('plan', p) | ||
// This means that the plan is coming at the END of all the tests | ||
// Plans MUST be either at the beginning or the very end. We treat | ||
// plans like '1..0' the same, since they indicate that no tests | ||
// will be coming. | ||
if (this.count !== 0 || this.planEnd === 0) | ||
this.postPlan = true | ||
// start a yaml block under a test point | ||
if (this.current && !this.yind && line === indent + '---\n') { | ||
this.yind = indent | ||
return | ||
} | ||
var res = this.createResult(line) | ||
if (!res) { | ||
this.nonTap(line) | ||
return | ||
} | ||
// at this point, not yamlish, and not an existing child test. | ||
// We may have already seen an unindented Subtest directive, or | ||
// a test point that ended in { indicating a buffered subtest | ||
// Child tests are always indented 4 spaces. | ||
if (line.substr(0, 4) === ' ') { | ||
if (this.maybeChild || | ||
this.current && this.current.name && this.current.name.substr(-1) === '{' || | ||
lineTypes.subtestIndent.test(line)) { | ||
this.startChild(line) | ||
return | ||
} | ||
if (this.planStart !== -1) { | ||
var lessThanStart = +res.id < this.planStart | ||
var greaterThanEnd = +res.id > this.planEnd | ||
if (lessThanStart || greaterThanEnd) { | ||
this.ok = false | ||
if (lessThanStart) | ||
res.tapError = 'id less than plan start' | ||
else | ||
res.tapError = 'id greater than plan end' | ||
this.failures.push(res) | ||
// It's _something_ indented, if the indentation is divisible by | ||
// 4 spaces, and the result is actual TAP of some sort, then do | ||
// a child subtest for it as well. | ||
// | ||
// This will lead to some ambiguity in cases where there are multiple | ||
// levels of non-signaled subtests, but a Subtest comment in the | ||
// middle of them, which may or may not be considered "indented" | ||
// See the subtest-no-comment-mid-comment fixture for an example | ||
// of this. As it happens, the preference is towards an indented | ||
// Subtest comment as the interpretation, which is the only possible | ||
// way to resolve this, since otherwise there's no way to distinguish | ||
// between an anonymous subtest with a non-indented Subtest comment, | ||
// and an indented Subtest comment. | ||
var s = line.match(/( {4})+(.*\n)$/) | ||
if (s[2].charAt(0) !== ' ') { | ||
// integer number of indentations. | ||
var type = lineType(s[2]) | ||
if (type) { | ||
if (type[0] === 'comment') { | ||
this.emitComment(line) | ||
} else { | ||
// it's relevant! start as an "unnamed" child subtest | ||
this.startChild(line) | ||
} | ||
return | ||
} | ||
} | ||
} | ||
this.sawValidTap = true | ||
if (res.id) { | ||
if (!this.first || res.id < this.first) | ||
this.first = res.id | ||
if (!this.last || res.id > this.last) | ||
this.last = res.id | ||
// at this point, it's either a non-subtest comment, or garbage. | ||
if (lineTypes.comment.test(line)) { | ||
this.emitComment(line) | ||
return | ||
} | ||
// hold onto it, because we might get yamlish diagnostics | ||
this.current = res | ||
this.nonTap(line) | ||
} |
{ | ||
"name": "tap-parser", | ||
"version": "1.3.2", | ||
"version": "2.0.0", | ||
"description": "parse the test anything protocol", | ||
@@ -11,12 +11,11 @@ "main": "index.js", | ||
"events-to-array": "^1.0.1", | ||
"inherits": "~2.0.1", | ||
"js-yaml": "^3.2.7" | ||
}, | ||
"devDependencies": { | ||
"glob": "^5.0.2", | ||
"tap": "^1.2.0", | ||
"tape": "^3.5.0" | ||
"glob": "^7.0.5", | ||
"tap": "^6.3.0" | ||
}, | ||
"scripts": { | ||
"test": "tap test/*.js" | ||
"test": "tap test/*.js --cov", | ||
"regen-fixtures": "node scripts/generate-test.js test/fixtures/*.tap" | ||
}, | ||
@@ -23,0 +22,0 @@ "testling": { |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
28893
3
2
706
183
- Removedinherits@~2.0.1