Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
mhhf committed Aug 16, 2016
1 parent f709413 commit 64f64ca
Show file tree
Hide file tree
Showing 7 changed files with 620 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
39 changes: 39 additions & 0 deletions lib/cli_out.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use strict';

var clc = require('cli-color-tty')(true);
var path = require('path');
var through = require('through2');

// Takes the output of the test and test_summarizer streams
// and emits it to stdout and stderr (as appropriate) before
// passing it on to the next stream in the pipeline.
var first = true;
module.exports = function () {
var lastDirectory;

return through.obj(function (file, enc, cb) {
this.push(file);

if (!/\.(stderr|stdout)$/.test(file.path)) {
return cb();
}

var out = console.log;

if (/\.stderr$/.test(file.path)) {
out = console.error;
}

if (first) {
out();
first = false;
}

if (lastDirectory !== path.dirname(file.path)) {
lastDirectory = path.dirname(file.path);
out(clc.bold(lastDirectory));
}
out(' ' + String(file.contents));
cb();
});
};
167 changes: 167 additions & 0 deletions lib/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
'use strict';

var _ = require('lodash');
var BigNumber = require('bignumber.js');
var clc = require('cli-color-tty')(true);
var Contract = require('../contract');
var deasync = require('deasync');
var File = require('vinyl');
var LogTranslator = require('../logtranslator');
var path = require('path');
var fs = require('fs');
var through = require('through2');
var VMTest = require('../vmtest');

// This stream takes the output of either the build stream or pipeline (they
// produce the same output) and returns a stream of files containing the output
// of running each `Test` contract. A special, non-standard `error` flag is set
// on File objects representing failed tests. This allows the `cli_out` stream
// to push the content of those files to `stderr` instead of `stdout`.

function runTests (stream, className, vmTest, logTranslator) {
var testCount = vmTest.testCount();
var remaining = testCount;
var deployFailure = false;

function testResultHandler (err, result) {
if (deployFailure || stream.isPaused()) return;

if (err) {
stream.push(new File({
path: path.join(className,
'Deployment failure.stderr'),
contents: new Buffer(String(err))
}));
deployFailure = true;
return;
}

var color = clc.green;

if (result.failed) {
color = clc.red;
}

// TODO: Clean this up. We want it to be
// easy to have special log formatting for
// particular types of events, and this is
// a discreet logical chunk that belongs in
// its own function or class somewhere.
var output = result.title + '\n';
var logPrefix = ' | ';
var report = '';
for (let entry of result.logs) {
if (entry.event === '__startBlockE') {
report += '```{' + entry.args.what + '}\n';
} else if (entry.event === '__stopBlockE') {
report += '```\n';
} else if (entry.event.indexOf('log_id_') > -1 && LogTranslator.logs[entry.event].type === 'doc') {
report += LogTranslator.format(entry) + '\n';
} else if (entry.event.indexOf('_named_') > -1) {
var key = toAscii(entry.args.key) + ': ';
var val = entry.args.decimals
? toDecimal(entry.args.val, entry.args.decimals)
: entry.args.val;
output += logPrefix + key + val + '\n';
} else if (entry.event.indexOf('log_id_') > -1) {
output += ' ' + LogTranslator.format(entry) + '\n';
} else if (entry.event === 'log_bytes32') {
output += logPrefix + toAscii(entry.args.val) + '\n';
} else {
output += logPrefix + entry.event + '\n';

for (let arg of _.pairs(entry.args)) {
output += logPrefix + ' ' +
arg[0] + ': ' + arg[1] + '\n';
}
}
}
output += ' ' + color(result.message) + '\n';
// TODO refactor to write report stream file
if (result.reporterPath) {
fs.appendFileSync(result.reporterPath, report);
}

var file = new File({
path: path.join(
className,
result.title + (result.failed ? '.stderr' : '.stdout')),
contents: new Buffer(output)
});
stream.push(file);

remaining = remaining - 1;
}

// Run all the tests in parallel.
for (var i = 0; i < testCount; i++) {
vmTest.runTest(i, testResultHandler);
}

// Wait until all the tests have been run.
deasync.loopWhile(() => remaining !== 0 && !deployFailure);
}

module.exports = function (opts) {
return through.obj(function (file, enc, cb) {
var classes = JSON.parse(String(file.contents));

// Skip if Test contract isn't found
if (!('Test' in classes)) return cb();

// Load the Test contract
try {
var testContract = new Contract(classes['Test']);
} catch (err) {
return cb(err);
}

// TODO - export this to pipeline setup as different streams have to be able to communicate with the chain
var web3 = opts.web3;

// var libraryAddressMap = {};
// const DEFAULT_GAS = 900000000; // 900 million
var className;

for (className in classes) {
// Filter classNames if a filter is present if a filter is present
if (opts.nameFilter && !opts.nameFilter.test(className)) {
continue;
}

try {
var contract = new Contract(classes[className]);
} catch (err) {
return cb(err);
}

// way to determine if the class is a test,
// iff it has implemented the Test interface
if (_.intersection(contract.signatures, testContract.signatures)
.length !== testContract.signatures.length) {
continue;
}
let translator = opts.logTranslator || new LogTranslator(contract.abi);
var vmTest = opts.vmTest || new VMTest(web3, contract, translator);
let stream = opts.stream || this;
runTests(stream, className, vmTest, translator);
}
vmTest.stop();
cb();
});
};

function toAscii (hex) {
hex = hex.replace(/^0x/, '');
var result = '';
for (var i = 0; i < hex.length - 1; i += 2) {
result += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
}
return result.replace(/\0/g, '');
}

function toDecimal (value, decimals) {
return new BigNumber(value)
.div(new BigNumber(10).pow(decimals))
.toFixed(decimals);
}
51 changes: 51 additions & 0 deletions lib/test_summarizer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
'use strict';

var clc = require('cli-color-tty')(true);
var File = require('vinyl');
var path = require('path');
var through = require('through2');

// Takes the output of the test stream and summarizes the test results.
// Outputs one file object if all the tests passed, and two if some
// tests failed. One file object has a string describing the overall
// test status, and the other has a list of all failing tests.
module.exports = function () {
var totalTests = 0;
var failingTests = [];

return through.obj(function (file, enc, cb) {
if (/\.stderr$/.test(file.path)) {
failingTests.push(file);
}
totalTests += 1;
cb();
}, function (cb) {
var ext = '.stdout';
var output = clc.green('Passed all ' + totalTests + ' tests!');

if (failingTests.length > 0) {
ext = '.stderr';
output = clc.red(
'Failed ' + failingTests.length +
' out of ' + totalTests + ' tests.');

var failedOutput = '';
for (let failingTest of failingTests) {
failedOutput += clc.red(path.dirname(failingTest.path) +
': ' + path.basename(failingTest.path, ext)) + '\n ';
}

this.push(new File({
path: path.join('Failing Tests', 'summary' + ext),
contents: new Buffer(failedOutput)
}));
}

this.push(new File({
path: path.join('Summary', 'summary' + ext),
contents: new Buffer(output)
}));

cb();
});
};
12 changes: 8 additions & 4 deletions lib/testpipeline.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
"use strict";

var test = require('./test.js');
var cli_out = require('./cli_out.js');
var test_summarizer = require('./test_summarizer.js');

var Web3Interface = require('dapple-core/web3Interface.js');

var TestPipeline = function (opts) {
Expand All @@ -14,10 +18,10 @@ var TestPipeline = function (opts) {

return Combine(
// streams.linkLibraries(opts),
streams.test(opts),
streams.cli_out(),
streams.test_summarizer(),
streams.cli_out()
test(opts),
cli_out(),
test_summarizer(),
cli_out()
);
};

Expand Down
Loading

0 comments on commit 64f64ca

Please sign in to comment.