-
Notifications
You must be signed in to change notification settings - Fork 165
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
cloudhead
committed
May 2, 2010
1 parent
051bb40
commit 1a64009
Showing
1 changed file
with
180 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
#!/usr/bin/env node | ||
|
||
var path = require('path'), | ||
sys = require('sys'), | ||
fs = require('fs'), | ||
exec = require('child_process').exec, | ||
Script = process.binding('evals').Script; | ||
|
||
require.paths.unshift(path.join(__dirname, '..', 'lib')); | ||
|
||
var argv = [], options = { | ||
matcher: /.*/, | ||
brief: false | ||
}; | ||
|
||
// | ||
// Parse command-line parameters | ||
// | ||
for (var i = 0, arg; i < process.argv.length; i++) { | ||
arg = process.argv[i]; | ||
|
||
if (arg === __filename) { continue } | ||
|
||
if (arg[0] !== '-') { | ||
argv.push(arg); | ||
} else { | ||
arg = arg.match(/^--?(.*)/)[1]; | ||
|
||
if (arg[0] === 'R') { | ||
options.matcher = new(RegExp)(arg.slice(1)); | ||
} else if (arg in options) { | ||
options[arg] = true; | ||
} | ||
} | ||
} | ||
|
||
// Get rid of process runner | ||
// ('node' in most cases) | ||
argv = argv.slice(1); | ||
|
||
var vows = require('vows').config(options); | ||
|
||
if (argv.length > 0) { | ||
argv.forEach(function (arg) { | ||
runTest(arg); | ||
}); | ||
} else { | ||
// | ||
// Watch mode | ||
// | ||
vows.options.brief = true; | ||
|
||
(function () { | ||
var clock = [ | ||
'. ', '.. ', '... ', ' ...', | ||
' ..', ' .', ' .', ' ..', | ||
'... ', '.. ', '. ' | ||
]; | ||
var current = 0, | ||
status = 0, | ||
runningTests = false, | ||
statusText = '', | ||
colors = ['\033[32m', '\033[33m', '\033[91m'], | ||
timer = setInterval(tick, 100); | ||
|
||
cursorHide(); | ||
|
||
// Run every 100ms | ||
function tick() { | ||
cursorSave(); | ||
eraseLine(); | ||
print(colors[status]); | ||
print(clock[current]); | ||
|
||
if (runningTests) { | ||
print(' \033[39mrunning tests...'); | ||
} else { | ||
print(' ' + statusText); | ||
} | ||
|
||
print('\033[39m'); | ||
cursorRestore(); | ||
current = (current == clock.length - 1) ? 0 : current + 1; | ||
} | ||
|
||
// | ||
// Utility functions | ||
// | ||
function print(str) { process.stdout.write(str) } | ||
function eraseLine() { print("\033[2K") } | ||
function cursorSave() { print("\033[s") } | ||
function cursorRestore() { print("\033[u") } | ||
function cursorHide() { print("\033[?25l") } | ||
function cursorShow() { print("\033[?25h") } | ||
function cleanup() { cursorShow(), print('\n') } | ||
|
||
// | ||
// Called when a file has been modified. | ||
// Run the matching tests and change the status. | ||
// | ||
function changed(file) { | ||
runningTests = true; | ||
runTest("test/" + file + "-test.js").addListener('end', function (h, b, e) { | ||
runningTests = false; | ||
statusText = h + " honored, " + | ||
b + " broken, " + | ||
e + " errored"; | ||
|
||
if (b || e) { | ||
eraseLine(); | ||
status = e ? 2 : 1; | ||
} else { | ||
status = 0; | ||
} | ||
}); | ||
} | ||
// | ||
// Recursively traverse a hierarchy, returning | ||
// a list of all relevant .js files. | ||
// | ||
function paths(dir) { | ||
var paths = []; | ||
|
||
try { fs.statSync(dir) } | ||
catch (e) { return [] } | ||
|
||
(function traverse(dir, stack) { | ||
stack.push(dir); | ||
fs.readdirSync(stack.join('/')).forEach(function (file) { | ||
var path = stack.concat([file]).join('/'), | ||
stat = fs.statSync(path); | ||
|
||
if (file[0] == '.' || file === 'vendor') { | ||
return; | ||
} else if (stat.isFile() && /\.js$/.test(file)) { | ||
paths.push(path); | ||
} else if (stat.isDirectory()) { | ||
traverse(file, stack); | ||
} | ||
}); | ||
stack.pop(); | ||
})(dir || '.', []); | ||
|
||
return paths; | ||
} | ||
|
||
// | ||
// Watch all relevant files in lib/ and src/, | ||
// and call `changed()` on change. | ||
// | ||
paths('lib').concat(paths('src')).forEach(function (p) { | ||
fs.watchFile(p, function (current, previous) { | ||
if (new(Date)(current.mtime).valueOf() === | ||
new(Date)(previous.mtime).valueOf()) { return } | ||
else { | ||
changed(path.basename(p, '.js')); | ||
} | ||
}); | ||
}); | ||
|
||
process.addListener('exit', cleanup); | ||
process.addListener('SIGINT', function () { | ||
cleanup(); | ||
process.exit(); | ||
}); | ||
process.addListener('SIGQUIT', function () { | ||
changed(); | ||
}); | ||
})(); | ||
} | ||
|
||
function runTest(file) { | ||
var code = (function (require, __filename, __dirname) { | ||
/* content */ | ||
}).toString().replace('/* content */', fs.readFileSync(file)); | ||
|
||
return Script.runInThisContext('(' + code + ')', file) | ||
.call(global, require, file, path.dirname(file)); | ||
} | ||
|