Ox.doc: change test object from {statement, result} to {statement, expected}, plus some refactoring; Ox.test: allow source, file, files, docObject or docObjects as first argument, make it testable and add a test, plus some refactoring; Ox.tokenize: add error type, continue lexing on error

This commit is contained in:
rolux 2012-05-29 16:18:05 +02:00
parent 38016aa06b
commit 5dc654ad6d

View file

@ -2,7 +2,7 @@
/*@
Ox.doc <f> Generates documentation for annotated JavaScript
(source) -> <[o]> Array of doc objects
(source[, callback]) -> <[o]> Array of doc objects
(file, callback) -> <u> undefined
(files, callback) -> <u> undefined
source <s> JavaScript source code
@ -35,6 +35,8 @@ Ox.doc <f> Generates documentation for annotated JavaScript
Present if the <code>type</code> of the item is
<code>"function"</code>.
tests <[o]> Tests (array of test objects)
expected <s> Expected result
statement <s> Statement
type <s> Type of the item
<script>
Ox.test.doc = Ox.doc(
@ -46,10 +48,14 @@ Ox.doc <f> Generates documentation for annotated JavaScript
' Bar per baz is a good indicator of an item\'s foo-ness.\n' +
' (item) -> <n> Bar per baz, or NaN\n' +
' item <o> Any item\n' +
' > My.foo({bar: 1, baz: 10})\n' +
' 0.1\n' +
' > My.foo({})\n' +
' NaN\n' +
'@*' + '/\n' +
'My.foo = function(item) {\n' +
' return item.bar / item.baz;\n' +
'}'
'};'
);
</script>
> Ox.test.doc[0].name
@ -64,6 +70,8 @@ Ox.doc <f> Generates documentation for annotated JavaScript
['number']
> Ox.test.doc[1].usage[0].summary
'Bar per baz, or NaN'
> Ox.test.doc[1].tests[1]
{expected: 'NaN', statement: 'My.foo({})'}
@*/
Ox.doc = (function() {
var re = {
@ -210,7 +218,7 @@ Ox.doc = (function() {
var lines = decodeLinebreaks(string).split('\n ');
return {
statement: lines[0].slice(2),
result: lines[1].trim()
expected: lines[1].trim()
};
}
function parseTokens(tokens, includeLeadingLinebreaks) {
@ -311,18 +319,22 @@ Ox.doc = (function() {
}
return ret;
}
return function(/* source | file, callback | files, callback*/) {
var source = arguments.length == 1 ? arguments[0] : void 0,
files = arguments.length == 2 ? Ox.makeArray(arguments[0]) : void 0,
callback = arguments[1],
counter = 0, items = [];
files && files.forEach(function(file) {
Ox.get(file, function(source) {
items = items.concat(parseSource(source, file.split('?')[0]));
++counter == files.length && callback(items);
});
});
return source ? parseSource(source) : void 0;
return function(argument, callback) {
var counter = 0, items = [], ret;
if (arguments.length == 1) {
ret = parseSource(argument);
} else {
argument = Ox.makeArray(argument);
argument.forEach(function(file) {
Ox.get(file, function(source) {
items = items.concat(
parseSource(source, file.split('?')[0])
);
++counter == argument.length && callback(items);
});
})
}
return ret;
}
}());
@ -567,91 +579,128 @@ Ox.minify = function() {
/*@
Ox.test <f> Takes JavaScript, runs inline tests, returns results
(source, callback) -> <[o]> Array of results
(file, callback) -> <u> undefined
(files, callback) -> <u> undefines
file <s> Path to JavaScript file
files <[s]> List of paths to J
(files, callback) -> <u> undefined
(doc, callback) -> <u> undefined
(docs, callback) -> <u> undefined
source <s> JavaScript source
file <s> JavaScript file
files <[s]> Array of JavaScript files
doc <o> Documentation object (as returned by Ox.doc)
docs <[o]> Array of documentation objects (as returned by Ox.doc)
callback <f> Callback function
results [<o>] Array of results
results <[o]> Array of results
actual <s> Actual result
expected <s> Expected result
name <s> Item name
section <s> Section in the file
section <s|u> Section in the file
statement <s> Test statement
passed <b> True if actual result and expected result are equal
<script>
Ox.test.foo = function(item) {
return item.bar / item.baz;
};
Ox.test.source =
'/*@\n'+
'Ox.test.foo <f> Returns an items\'s bar per baz\n' +
' Bar per baz is a good indicator of an item\'s foo-ness.\n' +
' (item) -> <n> Bar per baz, or NaN\n' +
' item <o> Any item\n' +
' > Ox.test.foo({bar: 1, baz: 10})\n' +
' 0.1\n' +
' > Ox.test.foo({})\n' +
' NaN\n' +
'@*' + '/\n' +
'Ox.test.foo = function(item) {\n' +
' return item.bar / item.baz;\n' +
'};';
</script>
> Ox.test(Ox.test.source, function(r) { Ox.test(r[0].passed, true); })
undefined
@*/
Ox.test = function(file, callback) {
var regexp = /(Ox\.test\()/;
if (arguments.length == 2) {
Ox.doc(file, function(items) {
var results = [];
file = file.split('?')[0];
Ox.test.data[file] = {
callback: callback,
done: false,
results: [],
tests: {}
};
items.forEach(function(item) {
item.tests && item.tests.some(function(test) {
return test.result;
}) && item.tests.forEach(function(test) {
var actual, isAsync = regexp.test(test.statement);
if (isAsync) {
Ox.test.data[file].tests[item.name] = {
section: item.section,
statement: test.statement
};
test.statement = test.statement.replace(
regexp, '$1\'' + item.name + '\', '
);
}
if (test.result || test.statement.match(/Ox\.test/)) {
// don't eval script tags without assignment to Ox.test.foo
actual = eval(test.statement);
Ox.Log('TEST', test.statement);
}
if (!isAsync && test.result) {
Ox.test.data[file].results.push({
actual: JSON.stringify(actual),
expected: test.result,
name: item.name,
section: item.section,
statement: test.statement,
passed: Ox.isEqual(eval(
'(' + test.result + ')'
), actual)
});
}
});
Ox.test = function(argument, callback) {
Ox.print('Ox.test', argument, '----------------------------------')
function runTests(items) {
var id = Ox.uid(), regexp = /(.+Ox\.test\()/, results = [];
Ox.test.data[id] = {
callback: callback,
done: false,
results: results,
tests: {}
};
items.forEach(function(item) {
item.tests && item.tests.some(function(test) {
return test.expected;
}) && item.tests.forEach(function(test) {
var actual, isAsync = regexp.test(test.statement);
if (isAsync) {
Ox.test.data[id].tests[test.statement] = {
name: item.name,
section: item.section
};
Ox.Log('TEST', 'XXX', test.statement);
test.statement = test.statement.replace(
regexp,
"$1'" + test.statement.replace(/'/g, "\\'") + "', "
);
}
if (test.expected || test.statement.match(/Ox\.test\./)) {
// don't eval script tags without assignment to Ox.test.foo
Ox.Log('TEST', test.statement);
actual = eval(test.statement);
}
if (!isAsync && test.expected) {
Ox.test.data[id].results.push({
actual: JSON.stringify(actual),
expected: test.expected,
name: item.name,
section: item.section,
statement: test.statement,
passed: Ox.isEqual(
actual, eval('(' + test.expected + ')')
)
});
}
});
Ox.test.data[file].done = true;
if (Ox.isEmpty(Ox.test.data[file].tests)) {
callback(Ox.test.data[file].results);
}
});
Ox.test.data[id].done = true;
if (Ox.isEmpty(Ox.test.data[id].tests)) {
callback(Ox.test.data[id].results);
}
}
if (arguments.length == 2) {
if (Ox.typeOf(argument) == 'string' && Ox.contains(argument, '\n')) {
runTests(Ox.doc(argument))
} else {
argument = Ox.makeArray(argument);
if (Ox.typeOf(argument[0]) == 'string') {
Ox.doc(argument, runTests);
} else {
runTests(argument);
}
}
} else {
var name = arguments[0],
var statement = arguments[0],
result = arguments[1],
expected = arguments[2];
file = null;
expected = arguments[2],
id, test;
Ox.forEach(Ox.test.data, function(v, k) {
if (v.tests[name]) {
file = k;
if (v.tests[statement]) {
id = k;
test = v.tests[statement];
Ox.Break();
}
});
Ox.test.data[file].results.push({
Ox.test.data[id].results.push(Ox.extend(test, {
actual: result,
expected: expected,
name: name,
section: Ox.test.data[file].tests[name].section,
statement: Ox.test.data[file].tests[name].statement,
statement: statement,
passed: Ox.isEqual(result, expected)
});
delete Ox.test.data[file].tests[name];
if (Ox.test.data[file].done && Ox.isEmpty(Ox.test.data[file].tests)) {
Ox.test.data[file].callback(Ox.test.data[file].results);
}));
delete Ox.test.data[id].tests[statement];
if (Ox.test.data[id].done && Ox.isEmpty(Ox.test.data[id].tests)) {
Ox.test.data[id].callback(Ox.test.data[id].results);
}
}
};
@ -663,10 +712,11 @@ Ox.tokenize <f> Tokenizes JavaScript
column <n> Column of the token
line <n> Line of the token
type <s> Type of the token
Type can be <code>"comment"</code>, <code>"identifier"</code>,
<code>"linebreak"</code>, <code>"number"</code>,
<code>"operator"</code>, <code>"regexp"</code>,
<code>"string"</code> or <code>"whitespace"</code>
Type can be <code>"comment"</code>, <code>"error"</code>,
<code>"identifier"</code>, <code>"linebreak"</code>,
<code>"number"</code>, <code>"operator"</code>,
<code>"regexp"</code>, <code>"string"</code> or
<code>"whitespace"</code>
value <s> Value of the token
source <s> JavaScript source code
> Ox.tokenize('// comment\nvar foo = bar / baz;').length
@ -790,10 +840,20 @@ Ox.tokenize = (function() {
type = 'whitespace';
while (whitespace.indexOf(source[++cursor]) > -1) {}
} else {
break;
type = 'error';
++cursor;
}
value = source.slice(start, cursor);
tokens.push({column: column, line: line, type: type, value: value});
if (
type == 'error' && tokens.length
&& tokens[tokens.length - 1].type == 'error'
) {
tokens[tokens.length - 1].value += value;
} else {
tokens.push(
{column: column, line: line, type: type, value: value}
);
}
if (type == 'comment') {
lines = value.split('\n');
column = lines[lines.length - 1].length;