tinytap
Version:
Tiny TAP tester and test runner.
129 lines (107 loc) • 3.6 kB
JavaScript
// Executable to run other files.
var spawn = require('child_process').spawn;
var parse = require('shell-quote').parse;
var quote = require('shell-quote').quote;
var stream = require('stream');
var glob = require('glob');
var tinytap = require('../src/parser');
var args = process.argv.slice(2);
var list = [[null]];
var parallel = 1;
for (var i = 0; i < args.length; i++) {
if (args[i] == '-e' || args[i] == '--exec') {
i += 1;
list.push([args[i]]);
} else if (args[i].match(/^\-p(\d+)/)) {
parallel = parseInt(args[i].match(/^\-p(\d+)/)[1]);
} else if (args[i] == '-f' || args[i] == '--filter') {
i += 1;
list[list.length - 1].push('!' + args[i]);
} else {
list[list.length - 1].push(args[i]);
}
}
if (list[0].length > 1) {
throw new Error('Test ' + list[0][1] + 'specified without an executable.');
}
list.shift();
var total = 0, currentttest = 1, testsuccess = 0;
for (var i = 0; i < list.length; i++) {
var filter = []
var globbed = Array.prototype.concat.apply([], list[i].slice(1).map(function (match) {
if (match.charAt(0) == '!') {
filter = filter.concat(glob.sync(match.substr(1)));
return [];
} else {
return glob.sync(match);
}
})).filter(function (file) {
return filter.indexOf(file) == -1;
});
total += globbed.length;
list[i].splice(1, list[i].length - 1, globbed);
}
var groups = list;
process.stderr.setMaxListeners(0);
console.log('1..' + total);
(function nextgroup (group) {
var exe = group[0];
var files = group[1];
files.splice(0, parallel).forEach(function nextfile (file) {
var usedarg = false;
var spawncmd = parse(exe, process.env).map(function (arg) {
if (arg == '{}') {
usedarg = true;
return file;
}
return arg;
}).concat(usedarg ? [] : [file]);
var proc = spawn(spawncmd[0], spawncmd.slice(1));
var tap = proc.stdout.pipe(tinytap.parseStream());
function prefixStream () {
var transform = new stream.Transform()
var buf = '';
transform._transform = function (chunk, encoding, callback) {
buf += chunk.toString(encoding == 'buffer' ? null : encoding);
var pos;
while ((pos = buf.indexOf('\n')) > -1) {
this.push(' | ' + buf.slice(0, pos) + '\n');
buf = buf.slice(pos + 1);
}
callback();
}
return transform;
}
if (parseInt(process.env.TAP_VERBOSE) != 0 && parseInt(process.env.TAPV) != 0) {
proc.stdout.pipe(prefixStream()).pipe(process.stderr);
proc.stderr.pipe(prefixStream()).pipe(process.stderr);
}
var exited = false, completed = false, code = -1;
proc.on('exit', function (_code) {
code = _code;
exited = true;
exited && completed && procComplete();
})
tap.on('complete', function () {
completed = true;
exited && completed && procComplete();
});
function procComplete () {
console.log(tap.success && code == 0 ? 'ok' : 'not ok', currentttest++, '-', file + (code != 0 ? ' (exit code ' + code + ')' : ''));
tap.success && code == 0 && testsuccess++;
if (files.length) {
nextfile(files.shift());
} else if (groups.length) {
nextgroup(groups.shift());
} else if (currentttest > total) {
finish();
}
}
});
})(groups.shift());
function finish (code) {
var success = total - testsuccess == 0;
console.log(success ? 'ok' : 'not ok', currentttest++, '-', '(' + (success ? '' : 'not ') + 'all tests pass.)')
process.exit(total - testsuccess);
}