UNPKG

eth-gasreportr

Version:

Mocha reporter which shows gas used per unit test.

156 lines (131 loc) 4.2 kB
const mocha = require("mocha"); const inherits = require("util").inherits; const Base = mocha.reporters.Base; const color = Base.color; const log = console.log; const utils = require("./lib/utils"); const Config = require("./lib/config"); const TransactionWatcher = require("./lib/transactionWatcher"); const GasTable = require("./lib/gasTable"); const SyncRequest = require("./lib/syncRequest"); const mochaStats = require("./lib/mochaStats"); /** * Based on the Mocha 'Spec' reporter. Watches an Ethereum test suite run * and collects data about method & deployments gas usage. Mocha executes the hooks * in this reporter synchronously so any client calls here should be executed * via low-level RPC interface using sync-request. (see /lib/syncRequest) * An exception is made for fetching gas & currency price data from coinmarketcap and * ethgasstation (we hope that single call will complete by the time the tests finish running) * * @param {Object} runner mocha's runner * @param {Object} options reporter.options (see README example usage) */ function Gas(runner, options) { // Spec reporter Base.call(this, runner, options); // Initialize stats for Mocha 6+ epilogue if (!runner.stats) { mochaStats(runner); this.stats = runner.stats; } const self = this; let indents = 0; let n = 0; let failed = false; let indent = () => Array(indents).join(" "); // Gas reporter setup const config = new Config(options.reporterOptions); const sync = new SyncRequest(config.url); const watch = new TransactionWatcher(config); const table = new GasTable(config); // Expose internal methods to plugins if (typeof options.attachments === "object") { options.attachments.recordTransaction = watch.transaction.bind(watch); } // These call the cloud, start running them. utils.setGasAndPriceRates(config); // ------------------------------------ Runners ------------------------------------------------- runner.on("start", () => { watch.data.initialize(config); }); runner.on("suite", suite => { ++indents; log(color("suite", "%s%s"), indent(), suite.title); }); runner.on("suite end", () => { --indents; if (indents === 1) { log(); } }); runner.on("pending", test => { let fmt = indent() + color("pending", " - %s"); log(fmt, test.title); }); runner.on("test", () => { if (!config.provider) { watch.beforeStartBlock = sync.blockNumber(); } watch.data.resetAddressCache(); }); runner.on("hook end", hook => { if (hook.title.includes("before each") && !config.provider) { watch.itStartBlock = sync.blockNumber() + 1; } }); runner.on("pass", test => { let fmt; let fmtArgs; let gasUsedString; let consumptionString; let timeSpentString = color(test.speed, "%dms"); let gasUsed; if (!config.provider) { gasUsed = watch.blocks(); } if (gasUsed) { gasUsedString = color("checkmark", "%d gas"); if (config.showTimeSpent) { consumptionString = " (" + timeSpentString + ", " + gasUsedString + ")"; fmtArgs = [test.title, test.duration, gasUsed]; } else { consumptionString = " (" + gasUsedString + ")"; fmtArgs = [test.title, gasUsed]; } fmt = indent() + color("checkmark", " " + Base.symbols.ok) + color("pass", " %s") + consumptionString; } else { if (config.showTimeSpent) { consumptionString = " (" + timeSpentString + ")"; fmtArgs = [test.title, test.duration]; } else { consumptionString = ""; fmtArgs = [test.title]; } fmt = indent() + color("checkmark", " " + Base.symbols.ok) + color("pass", " %s") + consumptionString; } log.apply(null, [fmt, ...fmtArgs]); }); runner.on("fail", test => { failed = true; let fmt = indent() + color("fail", " %d) %s"); log(); log(fmt, ++n, test.title); }); runner.on("end", () => { table.generate(watch.data); self.epilogue(); }); } /** * Inherit from `Base.prototype`. */ inherits(Gas, Base); module.exports = Gas;