Skip to content

Commit

Permalink
More work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
Avaq committed Sep 28, 2019
1 parent 8d01a55 commit 07cdd88
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 98 deletions.
83 changes: 46 additions & 37 deletions lib/doctest.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ function inferType(path) {

var rewriters = {coffee: rewrite$coffee, js: rewrite$js};

function evaluate(moduleType, source, path) {
return moduleType === 'commonjs' ?
commonjsEval (source, path) :
functionEval (source);
function evaluate(options, source, path) {
return options.moduleType === 'commonjs' ?
commonjsEval (options, source, path) :
functionEval (options, source);
}

module.exports = function(path, options) {
Expand Down Expand Up @@ -75,10 +75,10 @@ module.exports = function(path, options) {
console.log (source.replace (/\n$/, ''));
return Promise.resolve ([]);
} else if (options.silent) {
return evaluate (options.module, source, path);
return evaluate (options, source, path);
} else {
console.log ('running doctests in ' + path + '...');
return (evaluate (options.module, source, path))
return (evaluate (options, source, path))
.then (function(results) {
log (results);
return results;
Expand Down Expand Up @@ -171,14 +171,13 @@ var MATCH_LOG = /^\[([a-zA-Z]+)\]:/;

// normalizeTest :: { output :: { value :: String } } -> Undefined
function normalizeTest($test) {
var $output = $test[OUTPUT];
if ($output != null) {
$test[OUTPUT].forEach (function($output) {
var match = $output.value.match (/^![ ]?([^:]*)(?::[ ]?(.*))?$/);
$test['!'] = match != null;
if ($test['!']) {
$output.value = 'new ' + match[1] + '(' + quote (match[2] || '') + ')';
}
}
});
}

function processLine(
Expand All @@ -204,6 +203,7 @@ function processLine(
value = stripLeading (1, ' ', stripLeading (1, '>', trimmedLine));
accum.tests.push ($test = {});
$test[accum.state = INPUT] = {value: value};
$test[OUTPUT] = [];
input ($test);
} else if (accum.state === INPUT && trimmedLine.charAt (0) === '.') {
value = stripLeading (1, ' ', stripLeading (Infinity, '.', trimmedLine));
Expand All @@ -218,17 +218,17 @@ function processLine(
} else if (MATCH_LOG.test (trimmedLine)) {
value = stripLeading (1, ' ', trimmedLine.replace (MATCH_LOG, ''));
$test = accum.tests[accum.tests.length - 1];
($test[accum.state = OUTPUT] = $test[accum.state] || []).push ({
$test[accum.state = OUTPUT].push ({
channel: MATCH_LOG.exec (trimmedLine)[1],
value: value
});
if ($test[OUTPUT].length === 1) {
output ($test);
}
} else {
} else if (accum.state === INPUT) {
value = trimmedLine;
$test = accum.tests[accum.tests.length - 1];
($test[accum.state = OUTPUT] = $test[accum.state] || []).push ({
$test[accum.state = OUTPUT].push ({
channel: null,
value: value
});
Expand Down Expand Up @@ -351,18 +351,20 @@ function wrap$js(test, sourceType) {
'__doctest.enqueue({',
' type: "' + INPUT + '",',
' thunk: function() {',
' return ' + test[INPUT].value + ';',
' return (\n' + test[INPUT].value + '\n );',
' }',
'});'
].concat (test[OUTPUT] == null ? [] : [
].concat (test[OUTPUT].length === 0 ? [] : [
'__doctest.enqueue({',
' type: "' + OUTPUT + '",',
' ":": ' + test[OUTPUT].loc.start.line + ',',
' "!": ' + test['!'] + ',',
' thunk: function() {',
' return ' + test[OUTPUT].map (function(out) {
return '{channel: "' + out.channel + '", value: ' + out.value + '}';
}) + ';',
' return [\n' + test[OUTPUT].map (function(out) {
return ' ' +
'{channel: ' + JSON.stringify (out.channel) +
', value: (\n' + out.value + '\n )}';
}).join (',\n') + '\n ];',
' }',
'});'
]).join ('\n');
Expand All @@ -375,7 +377,7 @@ function wrap$coffee(test) {
' thunk: ->',
indentN (4, test[INPUT].value),
'}'
].concat (test[OUTPUT] == null ? [] : [
].concat (test[OUTPUT].length === 0 ? [] : [
'__doctest.enqueue {',
' type: "' + OUTPUT + '"',
' ":": ' + test[OUTPUT].loc.start.line,
Expand Down Expand Up @@ -437,10 +439,12 @@ function rewrite$js(options, input) {
var lineTests = transformComments (options, comments.Line);

var chunks = lineTests
.concat ([object ([[INPUT, bookend]])])
.concat ([object ([[INPUT, bookend], [OUTPUT, []]])])
.reduce (function(accum, test) {
accum.chunks.push (substring (input, accum.loc, test[INPUT].loc.start));
accum.loc = (test[OUTPUT] == null ? test[INPUT] : test[OUTPUT]).loc.end;
accum.loc = (test[OUTPUT].length === 0 ?
test[INPUT] :
test[OUTPUT]).loc.end;
return accum;
}, {chunks: [], loc: {line: 1, column: 0}})
.chunks;
Expand Down Expand Up @@ -527,7 +531,7 @@ function rewrite$coffee(options, input) {
);
}

function functionEval(source) {
function functionEval(options, source) {
// Functions created via the Function function are always run in the
// global context, which ensures that doctests can't access variables
// in _this_ context.
Expand All @@ -536,10 +540,10 @@ function functionEval(source) {
var evaluate = Function ('__doctest', source);
var queue = [];
evaluate ({enqueue: function(io) { queue.push (io); }});
return run (queue);
return run (options, queue);
}

function commonjsEval(source, path) {
function commonjsEval(options, source, path) {
var abspath =
(pathlib.resolve (path)).replace (/[.][^.]+$/, '-' + Date.now () + '.js');

Expand All @@ -550,10 +554,10 @@ function commonjsEval(source, path) {
} finally {
fs.unlinkSync (abspath);
}
return run (queue);
return run (queue, options);
}

function run(queue, optionalLogMediator) {
function run(options, queue, optionalLogMediator) {
var logMediator = optionalLogMediator || {};
return queue.reduce (function(p, io) {
return p.then (function(accum) {
Expand Down Expand Up @@ -585,12 +589,11 @@ function run(queue, optionalLogMediator) {

function awaitOutput() {
return Promise (function(res) {
// TODO: Pass down options to get logTimeout
var t = setTimeout (done, 100);
var t = setTimeout (done, options.logTimeout);
logMediator.emit = function(channel, value) {
outputs.push ({channel: channel, value: value});
clearTimeout (t);
t = setTimeout (done, 100);
t = setTimeout (done, options.logTimeout);
};
function done() {
logMediator.emit = function() {};
Expand All @@ -600,29 +603,32 @@ function run(queue, optionalLogMediator) {
});
}

// TODO: Don't care to await output if a log function was not provided
return (awaitOutput ()).then (verifyOutput);
return typeof options.logFunction === 'string' ?
(awaitOutput ()).then (verifyOutput) :
Promise.resolve (verifyOutput ());

function verifyOutput() {
if (outputs.length < expecteds.length) {
// Fail because not enough output was generated
// TODO: Fail because not enough output was generated
} else if (outputs.length > expecteds.length) {
// Fail because too much output was generated
// TODO: Fail because too much output was generated
} else {
outputs.forEach (function(output, idx) {
var expected = expecteds[idx];
var pass, repr;

if (output.channel !== expected.channel) {
// Fail because output was given on the wrong channel
// TODO: Fail because output was given on the wrong channel
} else if (errored && output.channel == null) {
var name = output.value.name;
var message = output.value.message;
pass = io['!'] &&
name === expected.value.name &&
message === expected.value.message.replace (/^$/, message);
message ===
expected.value.message.replace (/^$/, message);
repr = '! ' + name +
(expected.value.message && message.replace (/^(?!$)/, ': '));
(expected.value.message &&
message.replace (/^(?!$)/, ': '));
} else {
pass = !io['!'] && Z.equals (output.value, expected.value);
repr = show (output.value);
Expand All @@ -632,12 +638,15 @@ function run(queue, optionalLogMediator) {
pass,
repr,
io['!'] ?
'! ' + expected.name + expected.message.replace (/^(?!$)/, ': ') :
show (expected),
'! ' +
expected.value.name +
expected.value.message.replace (/^(?!$)/, ': ') :
show (expected.value),
io[':']
]);
});
}
return accum;
}

});
Expand Down
9 changes: 5 additions & 4 deletions lib/doctest.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ export default async function(path, options) {
console.log (source.replace (/\n$/, ''));
return [];
} else if (options.silent) {
return evaluate (source, path);
return evaluate (options, source, path);
} else {
console.log ('running doctests in ' + path + '...');
return (evaluate (source, path)).then (function(results) {
return (evaluate (options, source, path)).then (function(results) {
doctest.log (results);
return results;
});
Expand All @@ -56,7 +56,7 @@ function wrap(source, logFunction) {
]) : []) + (source);
}

function evaluate(source, path) {
function evaluate(options, source, path) {
const abspath =
(pathlib.resolve (path)).replace (/[.][^.]+$/, '-' + Date.now () + '.mjs');

Expand All @@ -71,7 +71,8 @@ function evaluate(source, path) {
return (util.promisify (fs.writeFile) (abspath, source))
.then (function() { return import (abspath); })
.then (function(module) {
return doctest.run (module.__doctest.queue,
return doctest.run (options,
module.__doctest.queue,
module.__doctest.logMediator);
})
.then (cleanup (Promise.resolve.bind (Promise)),
Expand Down
Loading

0 comments on commit 07cdd88

Please sign in to comment.