ltest.js | |
|---|---|
/*
*
* Copyright (C) 2011, The Locker Project
* All rights reserved.
*
* Please see the LICENSE file for more information.
*
*/
var crypto = require("crypto");
var fs = require("fs");
var rimraf = require("rimraf"); // rm -rf helper, npm install rimraf
var util = require("util");
var events = require("events");
fs.exists = function(path) {
try {
if (fs.statSync(path)) return true;
} catch (E) {
return false;
}
}
exports.NotImplemented = function() {
this.name = "Error";
this.message = "Not yet implemented";
}
exports.NotImplemented.prototype = new Error;
exports.notImplemented = function() { throw new exports.NotImplemented; };
function TestDescription(description, cb) {
events.EventEmitter.call(this);
this.sync = true;
this.isolatedDir = false;
this.workingDirIsTemp = false;
this.description = description;
this.testCB = cb;
}
util.inherits(TestDescription, events.EventEmitter);
/**
* Flags the test to be ran in an isolated working directory.
*
* \param presetDir The directory name to use
*
* This should be used whenever you are doing filesystem operations so you can test them cleanly.
* If no preset directory name is specified a randomly named one is created.
*/
TestDescription.prototype.isolate = function(presetDir) {
if (presetDir) {
this.isolatedDir = presetDir;
return this;
}
var hash = crypto.createHash("sha1");
hash.update(this.description);
hash.update(Date.now()+'');
this.isolatedDir = hash.digest("hex");
this.workingDirIsTemp = true;
return this;
}
/**
* Flags the test to be ran synchronously
*
* The result is handled by exceptions rather than events
*/
TestDescription.prototype.async = function() {
this.sync = false;
return this;
}
exports.testSuite = {
tests : [],
testPosition : 0,
successes : 0,
failures : 0,
startTime : 0,
run : function(runner)
{
this.tests.forEach(function(test) { test.run(runner); });
},
passedTest : function(test)
{
var self = exports.testSuite;
self.successes++;
process.stdout.write("[\033[0;32mOK" + console.baseColor + "]\n");
self.cleanupTest(test);
},
failedTest : function(test, errorMessage)
{
var self = exports.testSuite;
self.failures++;
process.stdout.write("[\033[0;31mERR" + console.baseColor +"]\n\n" + errorMessage + "\n\n");
self.cleanupTest(test);
},
cleanupTest : function(test)
{
var self = exports.testSuite; | |
| Fix the working directory and remove temps | if (test.isolatedDir) {
process.chdir("..");
if (test.workingDirIsTemp) rimraf.sync(test.isolatedDir);
}
self.testPosition++;
process.nextTick(self.runTests);
},
runTests : function()
{
var self = exports.testSuite; |
| Initial setup things | if (self.testPosition === 0) {
self.startTime = new Date();
} |
| Cleanup and output | if (self.testPosition >= self.tests.length) {
var runTime = (Date.now() - self.startTime.getTime()) / 1000;
process.stdout.write("\nRan " + self.tests.length + " tests in " +
runTime + " seconds " + self.successes + " successes " + self.failures + " failures.\n");
return;
} |
| An object to pass state between tests if needed | var test = self.tests[self.testPosition];
var output = "Testing " + test.description;
var dots = 75 - output.length;
for (var i = 0; i < dots; ++i) output += "."; |
| Put this in a tmp directory | if (test.isolatedDir) {
if (!fs.exists(test.isolatedDir)) fs.mkdirSync(test.isolatedDir, 777);
process.chdir(test.isolatedDir);
}
process.stdout.write(output);
test.on("success", self.passedTest);
test.on("failure", self.failedTest);
try {
test.testCB(test);
if (test.sync) test.emit("success", test);
} catch (e) {
test.emit("failure", test, e);
}
}
}
exports.Test = function(description, cb) {
var testDescription = new TestDescription(description, cb);
exports.testSuite.tests.push(testDescription);
return testDescription;
}
|