forked from cgewecke/eth-gas-reporter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
179 lines (155 loc) · 4.65 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
const mocha = require("mocha");
const Base = mocha.reporters.Base;
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");
const constants = require("mocha/lib/runner").constants;
const EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
const EVENT_RUN_END = constants.EVENT_RUN_END;
const EVENT_HOOK_END = constants.EVENT_HOOK_END;
const EVENT_TEST_BEGIN = constants.EVENT_TEST_BEGIN;
const EVENT_TEST_END = constants.EVENT_TEST_END;
const EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
const EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
const EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
/**
* Based on the Mocha 'JSON' 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) {
// JSON reporter
Base.call(this, runner, options);
// Initialize stats for Mocha 6+ epilogue
if (!runner.stats) {
mochaStats(runner);
this.stats = runner.stats;
}
const self = this;
const tests = [];
const pending = [];
const failures = [];
const passes = [];
// 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(EVENT_RUN_BEGIN, () => {
watch.data.initialize(config);
});
runner.on(EVENT_TEST_END, function(test) {
tests.push(test);
});
runner.on(EVENT_TEST_PENDING, test => {
pending.push(test);
});
runner.on(EVENT_TEST_BEGIN, () => {
if (!config.provider) {
watch.beforeStartBlock = sync.blockNumber();
}
watch.data.resetAddressCache();
});
runner.on(EVENT_HOOK_END, hook => {
if (hook.title.includes("before each") && !config.provider) {
watch.itStartBlock = sync.blockNumber() + 1;
}
});
runner.on(EVENT_TEST_PASS, test => {
passes.push(test);
});
runner.on(EVENT_TEST_FAIL, test => {
failures.push(test);
});
runner.on(EVENT_RUN_END, () => {
const report = table.saveCodeChecksData(watch.data);
const obj = {
stats: self.stats,
tests: tests.map(clean),
pending: pending.map(clean),
failures: failures.map(clean),
passes: passes.map(clean),
gasReport: report
};
runner.testResults = obj;
const json = JSON.stringify(obj, null, 2);
process.stdout.write(json);
self.epilogue();
});
}
/**
* Return a plain-object representation of `test`
* free of cyclic properties etc.
*
* @private
* @param {Object} test
* @return {Object}
*/
function clean(test) {
var err = test.err || {};
if (err instanceof Error) {
err = errorJSON(err);
}
return {
title: test.title,
fullTitle: test.fullTitle(),
file: test.file,
duration: test.duration,
currentRetry: test.currentRetry(),
speed: test.speed,
err: cleanCycles(err)
};
}
/**
* Replaces any circular references inside `obj` with '[object Object]'
*
* @private
* @param {Object} obj
* @return {Object}
*/
function cleanCycles(obj) {
var cache = [];
return JSON.parse(
JSON.stringify(obj, function(key, value) {
if (typeof value === "object" && value !== null) {
if (cache.indexOf(value) !== -1) {
// Instead of going in a circle, we'll print [object Object]
return "" + value;
}
cache.push(value);
}
return value;
})
);
}
/**
* Transform an Error object into a JSON object.
*
* @private
* @param {Error} err
* @return {Object}
*/
function errorJSON(err) {
var res = {};
Object.getOwnPropertyNames(err).forEach(function(key) {
res[key] = err[key];
}, err);
return res;
}
module.exports = Gas;