Skip to content

Commit 367ef81

Browse files
authored
Prepare API for parallelization (#436)
1 parent ce2e6c3 commit 367ef81

File tree

25 files changed

+310
-50
lines changed

25 files changed

+310
-50
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ module.exports = {
6868
| client | *Object* | `require("ganache-core")` | Useful if you need a specific ganache version. |
6969
| providerOptions | *Object* | `{ }` | [ganache-core options][1] |
7070
| skipFiles | *Array* | `['Migrations.sol']` | Array of contracts or folders (with paths expressed relative to the `contracts` directory) that should be skipped when doing instrumentation. |
71+
| istanbulFolder | *String* | `./coverage` | Folder location for Istanbul coverage reports. |
7172
| istanbulReporter | *Array* | `['html', 'lcov', 'text']` | [Istanbul coverage reporters][2] |
7273
| mocha | *Object* | `{ }` | [Mocha options][3] to merge into existing mocha config. `grep` and `invert` are useful for skipping certain tests under coverage using tags in the test descriptions.|
7374
| onServerReady[<sup>*</sup>][14] | *Function* | | Hook run *after* server is launched, *before* the tests execute. Useful if you need to use the Oraclize bridge or have setup scripts which rely on the server's availability. [More...][23] |

dist/plugin-assets/buidler.ui.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const UI = require('./../../lib/ui').UI;
22

33
/**
4-
* Truffle Plugin logging
4+
* Buidler Plugin logging
55
*/
66
class PluginUI extends UI {
77
constructor(log){

dist/plugin-assets/buidler.utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const { createProvider } = require("@nomiclabs/buidler/internal/core/providers/c
77

88

99
// =============================
10-
// Buidler Specific Plugin Utils
10+
// Buidler Plugin Utils
1111
// =============================
1212

1313
/**

dist/plugin-assets/plugin.utils.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
/**
22
* A collection of utilities for common tasks plugins will need in the course
33
* of composing a workflow using the solidity-coverage API
4-
*
5-
* TODO: Sweep back through here and make all `config.truffle_variable` plugin
6-
* platform neutral...
74
*/
85

96
const PluginUI = require('./truffle.ui');

lib/api.js

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const istanbul = require('istanbul');
66
const util = require('util');
77
const assert = require('assert');
88
const detect = require('detect-port');
9+
const _ = require('lodash/lang');
910

1011
const ConfigValidator = require('./validator');
1112
const Instrumenter = require('./instrumenter');
@@ -17,7 +18,7 @@ const AppUI = require('./ui').AppUI;
1718
* Coverage Runner
1819
*/
1920
class API {
20-
constructor(config) {
21+
constructor(config={}) {
2122
this.coverage = new Coverage();
2223
this.instrumenter = new Instrumenter();
2324
this.validator = new ConfigValidator()
@@ -54,7 +55,8 @@ class API {
5455
this.gasLimitString = "0xfffffffffff"; // block gas limit for ganache (higher than "gas sent")
5556
this.gasPrice = 0x01;
5657

57-
this.istanbulReporter = config.istanbulReporter || ['html', 'lcov', 'text'];
58+
this.istanbulFolder = config.istanbulFolder || false;
59+
this.istanbulReporter = config.istanbulReporter || ['html', 'lcov', 'text', 'json'];
5860

5961
this.setLoggingLevel(config.silent);
6062
this.ui = new AppUI(this.log);
@@ -65,21 +67,12 @@ class API {
6567
* Instruments a set of sources to prepare them for running under coverage
6668
* @param {Object[]} targets (see below)
6769
* @return {Object[]} (see below)
68-
* @example:
69-
*
70-
* targets:
71-
* [{
72-
* canonicalPath: <absolute-path>
73-
* relativePath: <relative-path>
74-
* source: <source-file>
75-
*
76-
* },...]
77-
*
78-
* outputs:
79-
* [{
80-
* canonicalPath: <path>
81-
* source: <instrumented-source-file>
82-
* }...]
70+
* @example of input/output array:
71+
* [{
72+
* source: (required) <solidity-source>,
73+
* canonicalPath: (required) <absolute path to source file>
74+
* relativePath: (optional) <rel path to source file for logging>
75+
* }]
8376
*/
8477
instrument(targets=[]) {
8578
let currentFile; // Keep track of filename in case we crash...
@@ -95,7 +88,7 @@ class API {
9588
this.ui.report('instr-start');
9689
}
9790

98-
this.ui.report('instr-item', [target.relativePath]);
91+
this.ui.report('instr-item', [currentFile]);
9992

10093
const instrumented = this.instrumenter.instrument(
10194
target.source,
@@ -119,22 +112,35 @@ class API {
119112
return outputs;
120113
}
121114

115+
/**
116+
* Returns a copy of the hit map created during instrumentation.
117+
* Useful if you'd like to delegate coverage collection to multiple processes.
118+
* @return {Object} instrumentationData
119+
*/
120+
getInstrumentationData(){
121+
return _.cloneDeep(this.instrumenter.instrumentationData)
122+
}
123+
124+
/**
125+
* Sets the hit map object generated during instrumentation. Useful if you'd like
126+
* to collect data for a pre-existing instrumentation.
127+
* @param {Object} data
128+
*/
129+
setInstrumentationData(data={}){
130+
this.instrumenter.instrumentationData = _.cloneDeep(data);
131+
}
132+
122133
/**
123134
* Launches an in-process ethereum client server, hooking the DataCollector to its VM.
124135
* @param {Object} client ganache client
125136
* @return {String} address of server to connect to
126137
*/
127138
async ganache(client){
128-
let retry = false;
129-
let address = `http://${this.host}:${this.port}`;
130-
131139
// Check for port-in-use
132140
if (await detect(this.port) !== this.port){
133141
throw new Error(this.ui.generate('server-fail', [this.port]))
134142
}
135143

136-
if(!this.client) this.client = client; // Prefer client from options
137-
138144
this.collector = new DataCollector(this.instrumenter.instrumentationData);
139145

140146
this.providerOptions.gasLimit = this.gasLimitString;
@@ -143,16 +149,17 @@ class API {
143149
// Launch server and attach to vm step of supplied client
144150
try {
145151
if (this.config.forceBackupServer) throw new Error()
146-
await this.attachToVM()
152+
await this.attachToVM(client)
147153
}
148154

149-
// Fallback to ganache-core-sc (eq: ganache-core 2.7.0)
155+
// Fallback to ganache-cli)
150156
catch(err) {
151-
this.ui.report('vm-fail', []);
152-
this.client = require('ganache-core-sc');
153-
await this.attachToVM();
157+
const _ganache = require('ganache-cli');
158+
this.ui.report('vm-fail', [_ganache.version]);
159+
await this.attachToVM(_ganache);
154160
}
155161

162+
const address = `http://${this.host}:${this.port}`;
156163
this.ui.report('server', [address]);
157164
return address;
158165
}
@@ -162,7 +169,7 @@ class API {
162169
*/
163170
async report() {
164171
const collector = new istanbul.Collector();
165-
const reporter = new istanbul.Reporter();
172+
const reporter = new istanbul.Reporter(false, this.istanbulFolder);
166173

167174
return new Promise((resolve, reject) => {
168175
try {
@@ -177,7 +184,8 @@ class API {
177184

178185
// Pify doesn't like this one...
179186
reporter.write(collector, true, (err) => {
180-
if (err) throw err;
187+
if (err) return reject(err);
188+
181189
this.ui.report('istanbul');
182190
resolve();
183191
});
@@ -204,9 +212,11 @@ class API {
204212
// ========
205213
// Provider
206214
// ========
207-
async attachToVM(){
215+
async attachToVM(client){
208216
const self = this;
209217

218+
// Prefer client from options
219+
if(!this.client) this.client = client;
210220
this.server = this.client.server(this.providerOptions);
211221

212222
this.assertHasBlockchain(this.server.provider);
@@ -225,7 +235,6 @@ class API {
225235
return vm;
226236
}
227237

228-
// NB: EADDRINUSE errors are uncatch-able?
229238
await pify(this.server.listen)(this.port);
230239
}
231240

lib/ui.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ class AppUI extends UI {
5656
const w = ":warning:";
5757

5858
const kinds = {
59-
'vm-fail': `${w} ${c.red('There was a problem attaching to the ganache-core VM.')} `+
60-
`${c.red('Check the provider option syntax in solidity-coverage docs.')}\n`+
61-
`${w} ${c.red('Using ganache-core-sc (eq. core v2.7.0) instead.')}\n`,
59+
'vm-fail': `${w} ${c.red('There was a problem attaching to the ganache VM.')}\n` +
60+
`${w} ${c.red('For help, see the "client" & "providerOptions" syntax in solidity-coverage docs.')}\n`+
61+
`${w} ${c.red(`Using ganache-cli (v${args[0]}) instead.`)}\n`,
6262

6363

6464
'instr-start': `\n${c.bold('Instrumenting for coverage...')}` +

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
},
1313
"scripts": {
1414
"nyc": "SILENT=true nyc --exclude '**/sc_temp/**' --exclude '**/test/**'",
15-
"test": "npm run nyc -- mocha test/units/* --timeout 100000 --no-warnings --exit",
16-
"test:ci": "SILENT=true node --max-old-space-size=3072 ./node_modules/.bin/nyc --reporter=lcov --exclude '**/sc_temp/**' --exclude '**/test/**/' -- mocha test/units/* --timeout 100000 --no-warnings --exit",
17-
"test:debug": "mocha test/units/* --timeout 100000 --no-warnings --exit"
15+
"test": "SILENT=true node --max-old-space-size=4096 ./node_modules/.bin/nyc -- mocha test/units/* --timeout 100000 --no-warnings --exit",
16+
"test:ci": "SILENT=true node --max-old-space-size=4096 ./node_modules/.bin/nyc --reporter=lcov --exclude '**/sc_temp/**' --exclude '**/test/**/' -- mocha test/units/* --timeout 100000 --no-warnings --exit",
17+
"test:debug": "node --max-old-space-size=4096 ./node_modules/.bin/mocha test/units/* --timeout 100000 --no-warnings --exit"
1818
},
1919
"homepage": "https://github.com/sc-forks/solidity-coverage",
2020
"repository": {
@@ -35,6 +35,7 @@
3535
"globby": "^10.0.1",
3636
"istanbul": "^0.4.5",
3737
"jsonschema": "^1.2.4",
38+
"lodash": "^4.17.15",
3839
"node-dir": "^0.1.17",
3940
"node-emoji": "^1.10.0",
4041
"pify": "^4.0.1",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
!node_modules
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
client: require('ganache-cli'),
3+
silent: process.env.SILENT ? true : false,
4+
istanbulReporter: ['json-summary', 'text'],
5+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const { loadPluginFile } = require("@nomiclabs/buidler/plugins-testing");
2+
loadPluginFile(__dirname + "/../dist/buidler.plugin");
3+
usePlugin("@nomiclabs/buidler-truffle5");
4+
5+
module.exports={
6+
defaultNetwork: "buidlerevm",
7+
logger: process.env.SILENT ? { log: () => {} } : console,
8+
};

0 commit comments

Comments
 (0)