Merge pull request #607 from embark-framework/features/test-display-gas-cost

Display gas cost in tests
This commit is contained in:
Iuri Matias 2018-07-07 00:23:14 +03:00 committed by GitHub
commit c8a32e45dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 181 additions and 6 deletions

View File

@ -188,12 +188,13 @@ class Cmd {
test() { test() {
program program
.command('test [file]') .command('test [file]')
.option('-d , --gasDetails', __('When set, will print the gas cost for each contract deploy'))
.option('--locale [locale]', __('language to use (default: en)')) .option('--locale [locale]', __('language to use (default: en)'))
.option('--loglevel [loglevel]', __('level of logging to display') + ' ["error", "warn", "info", "debug", "trace"]', /^(error|warn|info|debug|trace)$/i, 'warn') .option('--loglevel [loglevel]', __('level of logging to display') + ' ["error", "warn", "info", "debug", "trace"]', /^(error|warn|info|debug|trace)$/i, 'warn')
.description(__('run tests')) .description(__('run tests'))
.action(function (file, options) { .action(function (file, options) {
i18n.setOrDetectLocale(options.locale); i18n.setOrDetectLocale(options.locale);
embark.runTests({file, loglevel: options.loglevel}); embark.runTests({file, loglevel: options.loglevel, gasDetails: options.gasDetails});
}); });
} }

View File

@ -242,6 +242,7 @@ class ContractDeployer {
self.logger.info(contract.className.bold.cyan + " " + __("deployed at").green + " " + receipt.contractAddress.bold.cyan); self.logger.info(contract.className.bold.cyan + " " + __("deployed at").green + " " + receipt.contractAddress.bold.cyan);
contract.deployedAddress = receipt.contractAddress; contract.deployedAddress = receipt.contractAddress;
contract.transactionHash = receipt.transactionHash; contract.transactionHash = receipt.transactionHash;
receipt.className = contract.className;
self.events.emit("deploy:contract:receipt", receipt); self.events.emit("deploy:contract:receipt", receipt);
self.events.emit("deploy:contract:deployed", contract); self.events.emit("deploy:contract:deployed", contract);

View File

@ -14,7 +14,7 @@ function log(eventType, eventName) {
//console.log(eventType, eventName); //console.log(eventType, eventName);
} }
EventEmitter.prototype._maxListeners = 300; EventEmitter.prototype._maxListeners = 350;
const _on = EventEmitter.prototype.on; const _on = EventEmitter.prototype.on;
const _setHandler = EventEmitter.prototype.setHandler; const _setHandler = EventEmitter.prototype.setHandler;

145
lib/tests/reporter.js Normal file
View File

@ -0,0 +1,145 @@
const Base = require('mocha/lib/reporters/base');
const ms = require('mocha/lib/ms');
const color = Base.color;
class EmbarkSpec extends Base {
constructor(runner, options) {
super(runner, options);
const self = this;
self.embarkEvents = options.reporterOptions.events;
self.gasDetails = options.reporterOptions.gasDetails;
self.gasLimit = options.reporterOptions.gasLimit;
let indents = 0;
let n = 0;
self.stats.totalGasCost = 0;
self.stats.test = {};
self.stats.test.gasUsed = 0;
function onContractReceipt(receipt) {
const fmt = color('bright pass', ' ') +
color('suite', ' %s') +
color('light', ' deployed for ') +
color(self.getGasColor(receipt.gasUsed), '%s') +
color('light', ' gas');
console.log(fmt, receipt.className, receipt.gasUsed);
}
function onBlockHeader(blockHeader) {
self.stats.totalGasCost += blockHeader.gasUsed;
self.stats.test.gasUsed += blockHeader.gasUsed;
}
if (self.gasDetails) {
self.embarkEvents.on("deploy:contract:receipt", onContractReceipt);
}
self.embarkEvents.on("block:header", onBlockHeader);
function indent() {
return Array(indents).join(' ');
}
runner.on('start', function () {
console.log();
});
runner.on('suite', function (suite) {
++indents;
if (self.gasDetails) {
console.log();
}
console.log(color('suite', '%s%s'), indent(), suite.title);
});
runner.on('suite end', function () {
--indents;
if (indents === 1) {
console.log();
}
});
runner.on('pending', function (test) {
const fmt = indent() + color('pending', ' - %s');
console.log(fmt, test.title);
});
runner.on('test', function () {
self.stats.test.gasUsed = 0;
});
runner.on('pass', function (test) {
let fmt =
indent() +
color('checkmark', ' ' + Base.symbols.ok) +
color('pass', ' %s') +
color(test.speed, ' (%dms)') +
' - ' +
color(self.getGasColor(self.stats.test.gasUsed), '[%d gas]');
console.log(fmt, test.title, test.duration, self.stats.test.gasUsed);
});
runner.on('fail', function (test) {
console.log(indent() + color('fail', ' %d) %s') + ' - ' + color(self.getGasColor(self.stats.test.gasUsed), '[%d gas]'),
++n, test.title, self.stats.test.gasUsed);
});
runner.once('end', function () {
runner.removeAllListeners();
self.embarkEvents.removeListener("deploy:contract:receipt", onContractReceipt);
self.embarkEvents.removeListener("block:header", onBlockHeader);
self.epilogue();
});
}
getGasColor(gasCost) {
if (gasCost <= this.gasLimit / 10) {
return 'fast';
}
if (gasCost <= 3 * (this.gasLimit / 4)) {
return 'medium';
}
return 'slow';
}
epilogue() {
const stats = this.stats;
let fmt;
console.log();
// passes
fmt = color('bright pass', ' ') +
color('green', ' %d passing') +
color('light', ' (%s)') +
color('light', ' - [Total: %s gas]');
console.log(fmt,
stats.passes || 0,
ms(stats.duration),
stats.totalGasCost);
// pending
if (stats.pending) {
fmt = color('pending', ' ') +
color('pending', ' %d pending');
console.log(fmt, stats.pending);
}
// failures
if (stats.failures) {
fmt = color('fail', ' %d failing');
console.log(fmt, stats.failures);
Base.list(this.failures);
console.log();
}
console.log();
}
}
module.exports = EmbarkSpec;

View File

@ -4,6 +4,7 @@ const Mocha = require('mocha');
const path = require('path'); const path = require('path');
const assert = require('assert'); const assert = require('assert');
const Test = require('./test'); const Test = require('./test');
const EmbarkSpec = require('./reporter');
function getFilesFromDir(filePath, cb) { function getFilesFromDir(filePath, cb) {
fs.readdir(filePath, (err, files) => { fs.readdir(filePath, (err, files) => {
@ -100,6 +101,11 @@ module.exports = {
function executeForAllFiles(files, next) { function executeForAllFiles(files, next) {
async.eachLimit(files, 1, (file, eachCb) => { async.eachLimit(files, 1, (file, eachCb) => {
const mocha = new Mocha(); const mocha = new Mocha();
mocha.reporter(EmbarkSpec, {
events: global.embark.engine.events,
gasDetails: options.gasDetails,
gasLimit: global.embark.engine.deployManager.gasLimit
});
mocha.addFile(file); mocha.addFile(file);
mocha.suite.timeout(0); mocha.suite.timeout(0);
@ -112,6 +118,7 @@ module.exports = {
mocha.run(function (fails) { mocha.run(function (fails) {
failures += fails; failures += fails;
mocha.suite.removeAllListeners();
// Mocha prints the error already // Mocha prints the error already
eachCb(); eachCb();
}); });

View File

@ -39,11 +39,13 @@ class Test {
this.error = false; this.error = false;
this.builtContracts = {}; this.builtContracts = {};
this.compiledContracts = {}; this.compiledContracts = {};
this.logsSubscription = null;
this.web3 = new Web3(); this.web3 = new Web3();
} }
initWeb3Provider(callback) { initWeb3Provider(callback) {
const self = this;
if (this.provider) { if (this.provider) {
this.provider.stop(); this.provider.stop();
} }
@ -70,8 +72,14 @@ class Test {
return callback(err); return callback(err);
} }
this.provider = new Provider(providerOptions); self.provider = new Provider(providerOptions);
this.provider.startWeb3Provider(callback); return self.provider.startWeb3Provider((err) => {
if (err) {
return callback(err);
}
self.subscribeToPendingTransactions();
callback();
});
}); });
} }
@ -87,9 +95,22 @@ class Test {
this.sim = getSimulator(); this.sim = getSimulator();
} }
this.web3.setProvider(this.sim.provider(this.simOptions)); this.web3.setProvider(this.sim.provider(this.simOptions));
this.subscribeToPendingTransactions();
callback(); callback();
} }
subscribeToPendingTransactions() {
const self = this;
if (self.logsSubscription) {
self.logsSubscription.unsubscribe();
}
self.logsSubscription = self.web3.eth
.subscribe('newBlockHeaders')
.on("data", function (blockHeader) {
self.engine.events.emit('block:header', blockHeader);
});
}
initDeployServices() { initDeployServices() {
this.engine.startService("web3", { this.engine.startService("web3", {
web3: this.web3 web3: this.web3
@ -98,6 +119,8 @@ class Test {
trackContracts: false trackContracts: false
//ipcRole: 'client' // disabled for now due to issues with ipc file //ipcRole: 'client' // disabled for now due to issues with ipc file
}); });
this.engine.deployManager.gasLimit = 6000000;
this.engine.contractsManager.gasLimit = 6000000;
} }
init(callback) { init(callback) {
@ -282,8 +305,6 @@ class Test {
}); });
}, },
function deploy(accounts, next) { function deploy(accounts, next) {
self.engine.deployManager.gasLimit = 6000000;
self.engine.contractsManager.gasLimit = 6000000;
self.engine.deployManager.fatalErrors = true; self.engine.deployManager.fatalErrors = true;
self.engine.deployManager.deployOnlyOnConfig = true; self.engine.deployManager.deployOnlyOnConfig = true;
self.engine.events.request('deploy:contracts', () => { self.engine.events.request('deploy:contracts', () => {