diff --git a/lib/ace/test/asyncjs/assert.js b/lib/ace/test/asyncjs/assert.js new file mode 100644 index 00000000..0972a8d1 --- /dev/null +++ b/lib/ace/test/asyncjs/assert.js @@ -0,0 +1,306 @@ +define(function(require, exports, module) { + +// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 +// +// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! +// +// Originally from narwhal.js (http://narwhaljs.org) +// Copyright (c) 2009 Thomas Robinson <280north.com> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the 'Software'), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// UTILITY +var oop = require('pilot/oop'); +var pSlice = Array.prototype.slice; + +// 1. The assert module provides functions that throw +// AssertionError's when particular conditions are not met. The +// assert module must conform to the following interface. + +var assert = exports; + +// 2. The AssertionError is defined in assert. +// new assert.AssertionError({ message: message, +// actual: actual, +// expected: expected }) + +assert.AssertionError = function AssertionError(options) { + this.name = 'AssertionError'; + this.message = options.message; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + var stackStartFunction = options.stackStartFunction || fail; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } +}; +oop.inherits(assert.AssertionError, Error); + +assert.AssertionError.prototype.toString = function() { + if (this.message) { + return [this.name + ':', this.message].join(' '); + } else { + return [this.name + ':', + JSON.stringify(this.expected), + this.operator, + JSON.stringify(this.actual)].join(' '); + } +}; + +// assert.AssertionError instanceof Error + +assert.AssertionError.__proto__ = Error.prototype; + +// At present only the three keys mentioned above are used and +// understood by the spec. Implementations or sub modules can pass +// other keys to the AssertionError's constructor - they will be +// ignored. + +// 3. All of the following functions must throw an AssertionError +// when a corresponding condition is not met, with a message that +// may be undefined if not provided. All assertion methods provide +// both the actual and expected values to the assertion error for +// display purposes. + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); +} + +// EXTENSION! allows for well behaved errors defined elsewhere. +assert.fail = fail; + +// 4. Pure assertion tests whether a value is truthy, as determined +// by !!guard. +// assert.ok(guard, message_opt); +// This statement is equivalent to assert.equal(true, guard, +// message_opt);. To test strictly for the value true, use +// assert.strictEqual(true, guard, message_opt);. + +assert.ok = function ok(value, message) { + if (!!!value) fail(value, true, message, '==', assert.ok); +}; + +// 5. The equality assertion tests shallow, coercive equality with +// ==. +// assert.equal(actual, expected, message_opt); + +assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, '==', assert.equal); +}; + +// 6. The non-equality assertion tests for whether two objects are not equal +// with != assert.notEqual(actual, expected, message_opt); + +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, '!=', assert.notEqual); + } +}; + +// 7. The equivalence assertion tests a deep equality relation. +// assert.deepEqual(actual, expected, message_opt); + +assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected)) { + fail(actual, expected, message, 'deepEqual', assert.deepEqual); + } +}; + +function _deepEqual(actual, expected) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + + } else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) { + if (actual.length != expected.length) return false; + + for (var i = 0; i < actual.length; i++) { + if (actual[i] !== expected[i]) return false; + } + + return true; + + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (actual instanceof Date && expected instanceof Date) { + return actual.getTime() === expected.getTime(); + + // 7.3. Other pairs that do not both pass typeof value == 'object', + // equivalence is determined by ==. + } else if (typeof actual != 'object' && typeof expected != 'object') { + return actual == expected; + + // 7.4. For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical 'prototype' property. Note: this + // accounts for both named and indexed properties on Arrays. + } else { + return objEquiv(actual, expected); + } +} + +function isUndefinedOrNull(value) { + return value === null || value === undefined; +} + +function isArguments(object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; +} + +function objEquiv(a, b) { + if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) + return false; + // an identical 'prototype' property. + if (a.prototype !== b.prototype) return false; + //~~~I've managed to break Object.keys through screwy arguments passing. + // Converting to array solves the problem. + if (isArguments(a)) { + if (!isArguments(b)) { + return false; + } + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b); + } + try { + var ka = Object.keys(a), + kb = Object.keys(b), + key, i; + } catch (e) {//happens when one is a string literal and the other isn't + return false; + } + // having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length != kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] != kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!_deepEqual(a[key], b[key])) return false; + } + return true; +} + +// 8. The non-equivalence assertion tests for any deep inequality. +// assert.notDeepEqual(actual, expected, message_opt); + +assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected)) { + fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); + } +}; + +// 9. The strict equality assertion tests strict equality, as determined by ===. +// assert.strictEqual(actual, expected, message_opt); + +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, '===', assert.strictEqual); + } +}; + +// 10. The strict non-equality assertion tests for strict inequality, as +// determined by !==. assert.notStrictEqual(actual, expected, message_opt); + +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, '!==', assert.notStrictEqual); + } +}; + +function expectedException(actual, expected) { + if (!actual || !expected) { + return false; + } + + if (expected instanceof RegExp) { + return expected.test(actual); + } else if (actual instanceof expected) { + return true; + } else if (expected.call({}, actual) === true) { + return true; + } + + return false; +} + +function _throws(shouldThrow, block, expected, message) { + var actual; + + if (typeof expected === 'string') { + message = expected; + expected = null; + } + + try { + block(); + } catch (e) { + actual = e; + } + + message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + + (message ? ' ' + message : '.'); + + if (shouldThrow && !actual) { + fail('Missing expected exception' + message); + } + + if (!shouldThrow && expectedException(actual, expected)) { + fail('Got unwanted exception' + message); + } + + if ((shouldThrow && actual && expected && + !expectedException(actual, expected)) || (!shouldThrow && actual)) { + throw actual; + } +} + +// 11. Expected to throw an error: +// assert.throws(block, Error_opt, message_opt); + +assert.throws = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [true].concat(pSlice.call(arguments))); +}; + +// EXTENSION! This is annoying to write outside this module. +assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [false].concat(pSlice.call(arguments))); +}; + +assert.ifError = function(err) { if (err) {throw err;}}; + +}); \ No newline at end of file diff --git a/lib/ace/test/asyncjs/async.js b/lib/ace/test/asyncjs/async.js new file mode 100644 index 00000000..89e49955 --- /dev/null +++ b/lib/ace/test/asyncjs/async.js @@ -0,0 +1,489 @@ +/*! + * async.js + * Copyright(c) 2010 Fabian Jakobs + * MIT Licensed + */ + +define(function(require, exports, module) { + +var STOP = exports.STOP = {} + +exports.Generator = function(source) { + if (typeof source == "function") + this.source = { + next: source + } + else + this.source = source +} + +;(function() { + this.next = function(callback) { + this.source.next(callback) + } + + this.map = function(mapper) { + if (!mapper) + return this + + mapper = makeAsync(1, mapper) + + var source = this.source + this.next = function(callback) { + source.next(function(err, value) { + if (err) + callback(err) + else { + mapper(value, function(err, value) { + if (err) + callback(err) + else + callback(null, value) + }) + } + }) + } + return new this.constructor(this) + } + + this.filter = function(filter) { + if (!filter) + return this + + filter = makeAsync(1, filter) + + var source = this.source + this.next = function(callback) { + source.next(function handler(err, value) { + if (err) + callback(err) + else { + filter(value, function(err, takeIt) { + if (err) + callback(err) + else if (takeIt) + callback(null, value) + else + source.next(handler) + }) + } + }) + } + return new this.constructor(this) + } + + this.slice = function(begin, end) { + var count = -1 + if (!end || end < 0) + var end = Infinity + + var source = this.source + this.next = function(callback) { + source.next(function handler(err, value) { + count++ + if (err) + callback(err) + else if (count >= begin && count < end) + callback(null, value) + else if (count >= end) + callback(STOP) + else + source.next(handler) + }) + } + return new this.constructor(this) + } + + this.reduce = function(reduce, initialValue) { + reduce = makeAsync(3, reduce) + + var index = 0 + var done = false + var previousValue = initialValue + + var source = this.source + this.next = function(callback) { + if (done) + return callback(STOP) + + if (initialValue === undefined) { + source.next(function(err, currentValue) { + if (err) + return callback(err, previousValue) + + previousValue = currentValue + reduceAll() + }) + } + else + reduceAll() + + function reduceAll() { + source.next(function handler(err, currentValue) { + if (err) { + done = true + if (err == STOP) + return callback(null, previousValue) + else + return(err) + } + reduce(previousValue, currentValue, index++, function(err, value) { + previousValue = value + source.next(handler) + }) + }) + } + } + return new this.constructor(this) + } + + this.forEach = + this.each = function(fn) { + fn = makeAsync(1, fn) + + var source = this.source + this.next = function(callback) { + source.next(function handler(err, value) { + if (err) + callback(err) + else { + fn(value, function(err) { + callback(err, value) + }) + } + }) + } + return new this.constructor(this) + } + + this.some = function(condition) { + condition = makeAsync(1, condition) + + var source = this.source + var done = false + this.next = function(callback) { + if (done) + return callback(STOP) + + source.next(function handler(err, value) { + if (err) + return callback(err) + + condition(value, function(err, result) { + if (err) { + done = true + if (err == STOP) + callback(null, false) + else + callback(err) + } + else if (result) { + done = true + callback(null, true) + } + else + source.next(handler) + }) + }) + } + return new this.constructor(this) + } + + this.every = function(condition) { + condition = makeAsync(1, condition) + + var source = this.source + var done = false + this.next = function(callback) { + if (done) + return callback(STOP) + + source.next(function handler(err, value) { + if (err) + return callback(err) + + condition(value, function(err, result) { + if (err) { + done = true + if (err == STOP) + callback(null, true) + else + callback(err) + } + else if (!result) { + done = true + callback(null, false) + } + else + source.next(handler) + }) + }) + } + return new this.constructor(this) + } + + this.call = function(context) { + var source = this.source + return this.map(function(fn, next) { + fn = makeAsync(0, fn, context) + fn.call(context, function(err, value) { + next(err, value) + }) + }) + } + + this.concat = function(generator) { + var generators = [this] + generators.push.apply(generators, arguments) + var index = 0 + var source = generators[index++] + + return new this.constructor(function(callback) { + source.next(function handler(err, value) { + if (err) { + if (err == STOP) { + source = generators[index++] + if (!source) + return callback(STOP) + else + return source.next(handler) + } + else + return callback(err) + } + else + return callback(null, value) + }) + }) + } + + this.zip = function(generator) { + var generators = [this] + generators.push.apply(generators, arguments) + + return new this.constructor(function(callback) { + exports.list(generators) + .map(function(gen, next) { + gen.next(next) + }) + .toArray(callback) + }) + } + + this.sort = function(compare) { + var self = this + var arrGen + this.next = function(callback) { + if (arrGen) + return arrGen.next(callback) + + self.toArray(function(err, arr) { + if (err) + callback(err) + else { + arrGen = exports.list(arr.sort(compare)) + arrGen.next(callback) + } + }) + } + return new this.constructor(this) + } + + this.join = function(separator) { + return this.$arrayOp(Array.prototype.join, separator !== undefined ? [separator] : null) + } + + this.reverse = function() { + return this.$arrayOp(Array.prototype.reverse) + } + + this.$arrayOp = function(arrayMethod, args) { + var self = this + var i = 0 + this.next = function(callback) { + if (i++ > 0) + return callback(STOP) + + self.toArray(function(err, arr) { + if (err) + callback(err, "") + else { + if (args) + callback(null, arrayMethod.apply(arr, args)) + else + callback(null, arrayMethod.call(arr)) + } + }) + } + return new this.constructor(this) + + } + + this.end = function(breakOnError, callback) { + if (!callback) { + callback = arguments[0] + breakOnError = true + } + + var source = this.source + var last + var lastError + source.next(function handler(err, value) { + if (err) { + if (err == STOP) + callback && callback(lastError, last) + else if (!breakOnError) { + lastError = err + source.next(handler) + } + else + callback && callback(err, value) + } + else { + last = value + source.next(handler) + } + }) + } + + this.toArray = function(breakOnError, callback) { + if (!callback) { + callback = arguments[0] + breakOnError = true + } + + var values = [] + var errors = [] + var source = this.source + + source.next(function handler(err, value) { + if (err) { + if (err == STOP) { + if (breakOnError) + return callback(null, values) + else { + errors.length = values.length + return callback(errors, values) + } + } + else { + if (breakOnError) + return callback(err) + else + errors[values.length] = err + } + } + + values.push(value) + source.next(handler) + }) + } + +}).call(exports.Generator.prototype) + +var makeAsync = exports.makeAsync = function(args, fn, context) { + if (fn.length > args) + return fn + else { + return function() { + var value + var next = arguments[args] + try { + value = fn.apply(context || this, arguments) + } catch(e) { + return next(e) + } + next(null, value) + } + } +} + +exports.list = function(arr, construct) { + var construct = construct || exports.Generator + var i = 0 + var len = arr.length + + return new construct(function(callback) { + if (i < len) + callback(null, arr[i++]) + else + callback(STOP) + }) +} + +exports.values = function(map, construct) { + var values = [] + for (var key in map) + values.push(map[key]) + + return exports.list(values, construct) +} + +exports.keys = function(map, construct) { + var keys = [] + for (var key in map) + keys.push(key) + + return exports.list(keys, construct) +} + +/** + * range([start,] stop[, step]) -> generator of integers + * + * Return a generator containing an arithmetic progression of integers. + * range(i, j) returns [i, i+1, i+2, ..., j-1] start (!) defaults to 0. + * When step is given, it specifies the increment (or decrement). + */ +exports.range = function(start, stop, step, construct) { + var construct = construct || exports.Generator + start = start || 0 + step = step || 1 + + if (stop === undefined || stop === null) + stop = step > 0 ? Infinity : -Infinity + + var value = start + + return new construct(function(callback) { + if (step > 0 && value >= stop || step < 0 && value <= stop) + callback(STOP) + else { + var current = value + value += step + callback(null, current) + } + }) +} + +exports.concat = function(first, varargs) { + if (arguments.length > 1) + return first.concat.apply(first, Array.prototype.slice.call(arguments, 1)) + else + return first +} + +exports.zip = function(first, varargs) { + if (arguments.length > 1) + return first.zip.apply(first, Array.prototype.slice.call(arguments, 1)) + else + return first.map(function(item, next) { + next(null, [item]) + }) +} + + +exports.plugin = function(members, constructors) { + if (members) { + for (var key in members) { + exports.Generator.prototype[key] = members[key] + } + } + + if (constructors) { + for (var key in constructors) { + exports[key] = constructors[key] + } + } +} + +}) \ No newline at end of file diff --git a/lib/ace/test/asyncjs/index.js b/lib/ace/test/asyncjs/index.js new file mode 100644 index 00000000..6903fff4 --- /dev/null +++ b/lib/ace/test/asyncjs/index.js @@ -0,0 +1,12 @@ +/*! + * async.js + * Copyright(c) 2010 Fabian Jakobs + * MIT Licensed + */ + +define(function(require, exports, module) { + +module.exports = require("./async") +require("./utils") + +}) \ No newline at end of file diff --git a/lib/ace/test/asyncjs/test.js b/lib/ace/test/asyncjs/test.js new file mode 100644 index 00000000..d1f68b06 --- /dev/null +++ b/lib/ace/test/asyncjs/test.js @@ -0,0 +1,195 @@ +/*! + * async.js + * Copyright(c) 2010 Fabian Jakobs + * MIT Licensed + */ + +define(function(require, exports, module) { + +var oop = require("pilot/oop") +var async = require("asyncjs/async") +require("asyncjs/utils") + +exports.TestGenerator = function(source) { + async.Generator.call(this, source) +} + +oop.inherits(exports.TestGenerator, async.Generator) + +;(function() { + + this.exec = function() { + this.run().report().summary(function(err, passed) { + console.log("DONE") + }) + } + + this.run = function() { + return this.setupTest() + .each(function(test, next) { + if (test.setUpSuite) + test.setUpSuite(next) + else + next() + }) + .each(function(test, next) { + test.test(function(err, passed) { + test.err = err + test.passed = passed + next() + }) + }) + .each(function(test, next) { + if (test.tearDownSuite) + test.tearDownSuite(next) + else + next() + }) + } + + this.report = function() { + return this.each(function(test, next) { + var color = test.passed ? "\x1b[32m" : "\x1b[31m" + var name = test.name + if (test.suiteName) + name = test.suiteName + ": " + test.name + console.log(color + "[" + test.count + "/" + test.index + "] " + name + " " + (test.passed ? "OK" : "FAIL") + "\x1b[0m") + if (!test.passed) + if (test.err.stack) + console.log(test.err.stack) + else + console.log(test.err) + + next() + }) + } + + this.summary = function(callback) { + var passed = 0 + var failed = 0 + + this.each(function(test) { + if (test.passed) + passed += 1 + else + failed += 1 + }).end(function() { + console.log("") + console.log("Summary:") + console.log("") + console.log( "Total number of tests: " + (passed + failed)) + passed && console.log("\x1b[32mPassed tests: " + passed + "\x1b[0m") + failed && console.log("\x1b[31mFailed tests: " + failed + "\x1b[0m") + console.log("") + callback(null, failed == 0) + }) + } + + this.setupTest = function() { + return this.each(function(test, next) { + var empty = function(next) { next() } + var context = test.context || this + + if (test.setUp) + var setUp = async.makeAsync(0, test.setUp, context) + else + setUp = empty + + tearDownCalled = false + if (test.tearDown) + var tearDownInner = async.makeAsync(0, test.tearDown, context) + else + tearDownInner = empty + + function tearDown(next) { + tearDownCalled = true + tearDownInner.call(test.context, next) + } + + var testFn = async.makeAsync(0, test.fn, context) + + test.test = function(callback) { + var called + function errorListener(e) { + if (called) + return + called = true + //process.removeListener('uncaughtException', errorListener) + if (!tearDownCalled) { + async.list([tearDown]) + .call() + .timeout(test.timeout) + .end(function() { + callback(e, false) + }) } + else + callback(e, false) + } + //process.addListener('uncaughtException', errorListener) + + async.list([setUp, testFn, tearDown]) + .delay(0) + .call(context) + .timeout(test.timeout) + .toArray(false, function(errors, values) { + if (called) + return + called = true + var err = errors[1] + //process.removeListener('uncaughtException', errorListener) + callback(err, !err) + }) + } + + next() + }) + } + +}).call(exports.TestGenerator.prototype) + +exports.testcase = function(testcase, suiteName, timeout) { + var methods = [] + for (var method in testcase) + methods.push(method) + + var setUp = testcase.setUp || null + var tearDown = testcase.tearDown || null + + var single + methods.forEach(function(name) { + if (name.charAt(0) == '>') + single = name + }) + if (single) + methods = [single] + + var testNames = methods.filter(function(method) { + return method.match(/^>?test/) && typeof(testcase[method]) == "function" + }) + var count = testNames.length + var i=1 + tests = testNames.map(function(name) { + return { + suiteName: suiteName || testcase.name || "", + name: name, + setUp: setUp, + tearDown: tearDown, + context: testcase, + timeout: timeout === undefined ? 3000 : timeout, + fn: testcase[name], + count: count, + index: i++ + } + }) + + if (testcase.setUpSuite) { + tests[0].setUpSuite = async.makeAsync(0, testcase.setUpSuite, testcase) + } + if (testcase.tearDownSuite) { + tests[tests.length-1].tearDownSuite = async.makeAsync(0, testcase.tearDownSuite, testcase) + } + + return async.list(tests, exports.TestGenerator) +} + +}) \ No newline at end of file diff --git a/lib/ace/test/asyncjs/utils.js b/lib/ace/test/asyncjs/utils.js new file mode 100644 index 00000000..604ef1d6 --- /dev/null +++ b/lib/ace/test/asyncjs/utils.js @@ -0,0 +1,63 @@ +/*! + * async.js + * Copyright(c) 2010 Fabian Jakobs + * MIT Licensed + */ + +define(function(require, exports, module) { + +var async = require("asyncjs/async") + +async.plugin({ + delay: function(delay) { + return this.each(function(item, next) { + setTimeout(next, delay) + }) + }, + + timeout: function(timeout) { + timeout = timeout || 0 + var source = this.source + + this.next = function(callback) { + var called + var id = setTimeout(function() { + called = true + callback("Source did not respond after " + timeout + "ms!") + }, timeout) + + source.next(function(err, value) { + if (called) + return + + called = true + clearTimeout(id) + + callback(err, value) + }) + } + return new this.constructor(this) + }, + + get: function(key) { + return this.map(function(value, next) { + next(null, value[key]) + }) + }, + + inspect: function() { + return this.each(function(item, next) { + console.log(JSON.stringify(item)) + next() + }) + }, + + print: function() { + return this.each(function(item, next) { + console.log(item) + next() + }) + } +}) + +}) \ No newline at end of file