From 9f8de10b072d7dcc8be1c40170250de7e151cfb0 Mon Sep 17 00:00:00 2001 From: visionmedia Date: Fri, 28 May 2010 10:02:07 -0700 Subject: [PATCH] Initial commit --- .gitignore | 2 + .gitmodules | 3 + History.md | 0 Makefile | 27 +++++ Readme.md | 0 bin/expresso | 283 ++++++++++++++++++++++++++++++++++++++++++++++++ deps/jscoverage | 1 + lib/a.js | 26 +++++ lib/b.js | 12 ++ test/a.test.js | 34 ++++++ test/b.test.js | 16 +++ 11 files changed, 404 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 History.md create mode 100644 Makefile create mode 100644 Readme.md create mode 100755 bin/expresso create mode 160000 deps/jscoverage create mode 100644 lib/a.js create mode 100644 lib/b.js create mode 100644 test/a.test.js create mode 100644 test/b.test.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f78790b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +lib-cov \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..191ddeb --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "deps/jscoverage"] + path = deps/jscoverage + url = git://github.com/visionmedia/node-jscoverage.git diff --git a/History.md b/History.md new file mode 100644 index 0000000..e69de29 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8ef8e15 --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ + +BIN = bin/expresso +PREFIX = /usr/local +JSCOV = deps/jscoverage/node-jscoverage + +test: $(BIN) + @./$(BIN) -I lib test/*.test.js + +test-cov: $(BIN) $(JSCOV) lib-cov + @./$(BIN) -I lib-cov test/*.test.js + +lib-cov: + @./$(JSCOV) lib lib-cov + +install: $(JSCOV) install-expresso + install $(JSCOV) $(PREFIX)/bin + +install-expresso: + install $(BIN) $(PREFIX)/bin + +$(JSCOV): + @cd deps/jscoverage && ./configure && make && mv jscoverage node-jscoverage + +clean: + @cd deps/jscoverage && git clean -fd + +.PHONY: test test-cov install install-expresso clean \ No newline at end of file diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..e69de29 diff --git a/bin/expresso b/bin/expresso new file mode 100755 index 0000000..89e217c --- /dev/null +++ b/bin/expresso @@ -0,0 +1,283 @@ +#!/usr/bin/env node + +/*! + * Expresso + * Copyright(c) TJ Holowaychuk + * (MIT Licensed) + */ + +/** + * Module dependencies. + */ + +var assert = require('assert'), + childProcess = require('child_process'), + path = require('path'), + sys = require('sys'), + cwd = process.cwd(), + fs = require('fs'); + +/** + * Expresso version. + */ + +var version = '0.0.1'; + +/** + * Usage documentation. + */ + +var usage = [ + 'usage: expresso [-h|--help] [-v|--version] [-i|-I|--include ]', + ' [-r|--require ] ' +].join('\n') + +// Parse arguments + +var files = [], + args = process.argv.slice(2); + +while (args.length) { + var arg = args.shift(); + switch (arg) { + case '-h': + case '--help': + sys.puts(usage); + process.exit(1); + break; + case '-v': + case '--version': + sys.puts(version); + process.exit(1); + break; + case '-i': + case '-I': + case '--include': + if (arg = args.shift()) { + require.paths.unshift(arg); + } else { + throw new Error('--include requires a path'); + } + break; + case '-r': + case '--require': + if (arg = args.shift()) { + require(arg); + } else { + throw new Error('--require requires a path'); + } + break; + default: + files.push(arg); + break; + } +} + +// Alias deepEqual as eql for complex equality + +assert.eql = assert.deepEqual; + +// Report test coverage when available + +process.addListener('exit', function(){ + sys.puts(''); + if (typeof _$jscoverage === 'object') { + reportCoverage(_$jscoverage); + } +}); + +/** + * Pad the given string to the maximum width provided. + * + * @param {String} str + * @param {Number} width + * @return {String} + * @api private + */ + +function lpad(str, width) { + str = String(str); + var n = width - str.length; + if (n < 1) return str; + while (n--) str = ' ' + str; + return str; +} + +/** + * Pad the given string to the maximum width provided. + * + * @param {String} str + * @param {Number} width + * @return {String} + * @api private + */ + +function rpad(str, width) { + str = String(str); + var n = width - str.length; + if (n < 1) return str; + while (n--) str = str + ' '; + return str; +} + +/** + * Report test coverage. + * + * @param {Object} cov + * @api private + */ + +function reportCoverage(cov) { + populateCoverage(cov); + sys.puts('\n \x1B[1mTest Coverage\x1B[0m'); + var sep = ' +--------------------------------+----------+------+------+--------+', + lastSep = ' +----------+------+------+--------+'; + sys.puts(sep); + sys.puts(' | filename | coverage | LOC | SLOC | missed |'); + sys.puts(sep); + for (var name in cov) { + var file = cov[name]; + if (file instanceof Array) { + sys.print(' | ' + rpad(name, 30)); + sys.print(' | ' + lpad(file.coverage.toFixed(2), 8)); + sys.print(' | ' + lpad(file.LOC, 4)); + sys.print(' | ' + lpad(file.SLOC, 4)); + sys.print(' | ' + lpad(file.totalMisses, 6)); + sys.print(' |\n'); + } + } + sys.puts(sep); + sys.print(' ' + rpad('', 30)); + sys.print(' | ' + lpad(cov.coverage.toFixed(2), 8)); + sys.print(' | ' + lpad(cov.LOC, 4)); + sys.print(' | ' + lpad(cov.SLOC, 4)); + sys.print(' | ' + lpad(cov.totalMisses, 6)); + sys.print(' |\n'); + sys.puts(lastSep); +} + +/** + * Populate code coverage data. + * + * @param {Object} cov + * @api private + */ + +function populateCoverage(cov) { + cov.LOC = + cov.SLOC = + cov.totalFiles = + cov.totalHits = + cov.totalMisses = + cov.coverage = 0; + for (var name in cov) { + var file = cov[name]; + if (file instanceof Array) { + ++cov.totalFiles; + cov.totalHits += file.totalHits = coverage(file, true); + cov.totalMisses += file.totalMisses = coverage(file, false); + file.totalLines = file.totalHits + file.totalMisses; + cov.SLOC += file.SLOC = file.totalLines; + cov.LOC += file.LOC = file.source.length; + file.coverage = (file.totalHits / file.totalLines) * 100; + } + } + cov.coverage = (cov.totalHits / cov.SLOC) * 100; +}; + +/** + * Total coverage for the given file data. + * + * @param {Array} data + * @return {Type} + * @api private + */ + +function coverage(data, val) { + var n = 0; + for (var i = 0, len = data.length; i < len; ++i) { + if (data[i] !== undefined && data[i] == val) ++n; + } + return n; +}; + +/** + * Assert that the given array-like object + * is empty (length of 0). + * + * @param {Array} arr + * @api public + */ + +assert.empty = function(arr, msg) { + assert.equal(arr.length, 0, msg); +}; + +/** + * Run all test files. + * + * @param {Array} files + * @api public + */ + +function run(files) { + if (files.length) { + files.forEach(function(file){ + var title = path.basename(file, '.js') + runSuite(title, require(cwd + '/' + file.replace('.js', ''))); + }); + } +} + +/** + * Run the given tests. + * + * @param {String} title + * @param {Object} tests + * @api private + */ + +function runSuite(title, tests) { + var title = ' \x1B[1m' + title + '\x1B[0m ', + keys = Object.keys(tests), + total = keys.length, + start = new Date, + failures = [], + current = 0; + (function next(){ + if (keys.length) { + var percent = Math.round(++current / (total + 1) * 100), + key = keys.shift(), + test = tests[key], + async = test.length === 2; + sys.print('\x1B[K %' + percent + ' ' + title + ' ' + key + '\r'); + try { + if (async) { + var n = test(assert, function(){ + if (!--n) next(); + }) || 1; + } else { + test(assert); + next(); + } + } catch (err) { + err.test = key; + failures.push(err); + next(); + } + } else { + var duration = ((new Date - start) / 1000).toFixed(3), + passes = total - failures.length; + sys.print('\x1B[K %100' + title + '\x1B[32m' + passes + '\x1B[0m' + + ' \x1B[31m' + failures.length + '\x1B[0m' + + ' in ' + duration + ' seconds\n'); + failures.forEach(function(assertion, i){ + sys.puts('\n \x1B[1m' + i + ')\x1B[0m ' + assertion.stack); + }); + } + })(); +} + +// Run test files + +run(files); diff --git a/deps/jscoverage b/deps/jscoverage new file mode 160000 index 0000000..0d4608a --- /dev/null +++ b/deps/jscoverage @@ -0,0 +1 @@ +Subproject commit 0d4608a6b4275b020ba665389aa75897d5d4a584 diff --git a/lib/a.js b/lib/a.js new file mode 100644 index 0000000..4dcadfa --- /dev/null +++ b/lib/a.js @@ -0,0 +1,26 @@ + +exports.upper = function(str){ + if (typeof str === 'string') { + return str.toUpperCase(); + } else { + return ''; + } +} + +exports.lower = function(str){ + if (typeof str === 'string') { + return str.toLowerCase(); + } else { + return ''; + } +} + +exports.lowerAsync = function(str, fn){ + process.nextTick(function(){ + if (typeof str === 'string') { + fn(str.toLowerCase()); + } else { + fn(''); + } + }) +} \ No newline at end of file diff --git a/lib/b.js b/lib/b.js new file mode 100644 index 0000000..c5cfdd4 --- /dev/null +++ b/lib/b.js @@ -0,0 +1,12 @@ + +exports.selectAsync = function(arr, fn, callback){ + process.nextTick(function(){ + var selected = []; + for (var i = 0, len = arr.length; i < len; ++i) { + if (fn.call(arr, arr[i], i, arr)) { + selected.push(arr[i]); + } + } + callback(selected); + }) +} \ No newline at end of file diff --git a/test/a.test.js b/test/a.test.js new file mode 100644 index 0000000..04d356e --- /dev/null +++ b/test/a.test.js @@ -0,0 +1,34 @@ + +var a = require('a') + +module.exports = { + 'test upper()': function(assert){ + assert.equal('FOO', a.upper('foo')); + assert.equal('', a.upper({})); + }, + + 'test lower()': function(assert){ + assert.equal('foo', a.lower('FOO')); + // we dont test the other path here, + // run `make test-cov` to see the coverage + }, + + 'test lowerAsync()': function(assert, done){ + // when the second argument is accepted, + // the test is assumed to be async + a.lowerAsync('FOO', function(str){ + assert.equal('foo', str); + done(); + }); + + // full coverage + a.lowerAsync({}, function(str){ + assert.equal('', str); + done(); + }); + + // return the number of expected calls + // to done(), defaults to 1. + return 2; + } +} \ No newline at end of file diff --git a/test/b.test.js b/test/b.test.js new file mode 100644 index 0000000..2873b87 --- /dev/null +++ b/test/b.test.js @@ -0,0 +1,16 @@ + +var b = require('b'), + arr = [], + n = 5000; + +while (n--) arr.push(n); + +exports['test select()'] = function(assert, done){ + b.selectAsync(arr, function(val){ + return val > 500; + }, function(arr){ + assert.equal(4999, arr[0]); + assert.equal(501, arr[arr.length-1]); + done(); + }) +} \ No newline at end of file