Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ module.exports = {
| client | *Object* | `require("ganache-core")` | Useful if you need a specific ganache version. |
| providerOptions | *Object* | `{ }` | [ganache-core options][1] |
| skipFiles | *Array* | `['Migrations.sol']` | Array of contracts or folders (with paths expressed relative to the `contracts` directory) that should be skipped when doing instrumentation. |
| istanbulFolder | *String* | `./coverage` | Folder location for Istanbul coverage reports. |
| istanbulReporter | *Array* | `['html', 'lcov', 'text']` | [Istanbul coverage reporters][2] |
| 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.|
| 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] |
Expand Down
2 changes: 1 addition & 1 deletion dist/plugin-assets/buidler.ui.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const UI = require('./../../lib/ui').UI;

/**
* Truffle Plugin logging
* Buidler Plugin logging
*/
class PluginUI extends UI {
constructor(log){
Expand Down
2 changes: 1 addition & 1 deletion dist/plugin-assets/buidler.utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const { createProvider } = require("@nomiclabs/buidler/internal/core/providers/c


// =============================
// Buidler Specific Plugin Utils
// Buidler Plugin Utils
// =============================

/**
Expand Down
3 changes: 0 additions & 3 deletions dist/plugin-assets/plugin.utils.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
/**
* A collection of utilities for common tasks plugins will need in the course
* of composing a workflow using the solidity-coverage API
*
* TODO: Sweep back through here and make all `config.truffle_variable` plugin
* platform neutral...
*/

const PluginUI = require('./truffle.ui');
Expand Down
73 changes: 41 additions & 32 deletions lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const istanbul = require('istanbul');
const util = require('util');
const assert = require('assert');
const detect = require('detect-port');
const _ = require('lodash/lang');

const ConfigValidator = require('./validator');
const Instrumenter = require('./instrumenter');
Expand All @@ -17,7 +18,7 @@ const AppUI = require('./ui').AppUI;
* Coverage Runner
*/
class API {
constructor(config) {
constructor(config={}) {
this.coverage = new Coverage();
this.instrumenter = new Instrumenter();
this.validator = new ConfigValidator()
Expand Down Expand Up @@ -54,7 +55,8 @@ class API {
this.gasLimitString = "0xfffffffffff"; // block gas limit for ganache (higher than "gas sent")
this.gasPrice = 0x01;

this.istanbulReporter = config.istanbulReporter || ['html', 'lcov', 'text'];
this.istanbulFolder = config.istanbulFolder || false;
this.istanbulReporter = config.istanbulReporter || ['html', 'lcov', 'text', 'json'];

this.setLoggingLevel(config.silent);
this.ui = new AppUI(this.log);
Expand All @@ -65,21 +67,12 @@ class API {
* Instruments a set of sources to prepare them for running under coverage
* @param {Object[]} targets (see below)
* @return {Object[]} (see below)
* @example:
*
* targets:
* [{
* canonicalPath: <absolute-path>
* relativePath: <relative-path>
* source: <source-file>
*
* },...]
*
* outputs:
* [{
* canonicalPath: <path>
* source: <instrumented-source-file>
* }...]
* @example of input/output array:
* [{
* source: (required) <solidity-source>,
* canonicalPath: (required) <absolute path to source file>
* relativePath: (optional) <rel path to source file for logging>
* }]
*/
instrument(targets=[]) {
let currentFile; // Keep track of filename in case we crash...
Expand All @@ -95,7 +88,7 @@ class API {
this.ui.report('instr-start');
}

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

const instrumented = this.instrumenter.instrument(
target.source,
Expand All @@ -119,22 +112,35 @@ class API {
return outputs;
}

/**
* Returns a copy of the hit map created during instrumentation.
* Useful if you'd like to delegate coverage collection to multiple processes.
* @return {Object} instrumentationData
*/
getInstrumentationData(){
return _.cloneDeep(this.instrumenter.instrumentationData)
}

/**
* Sets the hit map object generated during instrumentation. Useful if you'd like
* to collect data for a pre-existing instrumentation.
* @param {Object} data
*/
setInstrumentationData(data={}){
this.instrumenter.instrumentationData = _.cloneDeep(data);
}

/**
* Launches an in-process ethereum client server, hooking the DataCollector to its VM.
* @param {Object} client ganache client
* @return {String} address of server to connect to
*/
async ganache(client){
let retry = false;
let address = `http://${this.host}:${this.port}`;

// Check for port-in-use
if (await detect(this.port) !== this.port){
throw new Error(this.ui.generate('server-fail', [this.port]))
}

if(!this.client) this.client = client; // Prefer client from options

this.collector = new DataCollector(this.instrumenter.instrumentationData);

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

// Fallback to ganache-core-sc (eq: ganache-core 2.7.0)
// Fallback to ganache-cli)
catch(err) {
this.ui.report('vm-fail', []);
this.client = require('ganache-core-sc');
await this.attachToVM();
const _ganache = require('ganache-cli');
this.ui.report('vm-fail', [_ganache.version]);
await this.attachToVM(_ganache);
}

const address = `http://${this.host}:${this.port}`;
this.ui.report('server', [address]);
return address;
}
Expand All @@ -162,7 +169,7 @@ class API {
*/
async report() {
const collector = new istanbul.Collector();
const reporter = new istanbul.Reporter();
const reporter = new istanbul.Reporter(false, this.istanbulFolder);

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

// Pify doesn't like this one...
reporter.write(collector, true, (err) => {
if (err) throw err;
if (err) return reject(err);

this.ui.report('istanbul');
resolve();
});
Expand All @@ -204,9 +212,11 @@ class API {
// ========
// Provider
// ========
async attachToVM(){
async attachToVM(client){
const self = this;

// Prefer client from options
if(!this.client) this.client = client;
this.server = this.client.server(this.providerOptions);

this.assertHasBlockchain(this.server.provider);
Expand All @@ -225,7 +235,6 @@ class API {
return vm;
}

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

Expand Down
6 changes: 3 additions & 3 deletions lib/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ class AppUI extends UI {
const w = ":warning:";

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


'instr-start': `\n${c.bold('Instrumenting for coverage...')}` +
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
},
"scripts": {
"nyc": "SILENT=true nyc --exclude '**/sc_temp/**' --exclude '**/test/**'",
"test": "npm run nyc -- mocha test/units/* --timeout 100000 --no-warnings --exit",
"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",
"test:debug": "mocha test/units/* --timeout 100000 --no-warnings --exit"
"test": "SILENT=true node --max-old-space-size=4096 ./node_modules/.bin/nyc -- mocha test/units/* --timeout 100000 --no-warnings --exit",
"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",
"test:debug": "node --max-old-space-size=4096 ./node_modules/.bin/mocha test/units/* --timeout 100000 --no-warnings --exit"
},
"homepage": "https://github.com/sc-forks/solidity-coverage",
"repository": {
Expand All @@ -35,6 +35,7 @@
"globby": "^10.0.1",
"istanbul": "^0.4.5",
"jsonschema": "^1.2.4",
"lodash": "^4.17.15",
"node-dir": "^0.1.17",
"node-emoji": "^1.10.0",
"pify": "^4.0.1",
Expand Down
1 change: 1 addition & 0 deletions test/integration/projects/ganache-solcoverjs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!node_modules
5 changes: 5 additions & 0 deletions test/integration/projects/ganache-solcoverjs/.solcover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
client: require('ganache-cli'),
silent: process.env.SILENT ? true : false,
istanbulReporter: ['json-summary', 'text'],
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const { loadPluginFile } = require("@nomiclabs/buidler/plugins-testing");
loadPluginFile(__dirname + "/../dist/buidler.plugin");
usePlugin("@nomiclabs/buidler-truffle5");

module.exports={
defaultNetwork: "buidlerevm",
logger: process.env.SILENT ? { log: () => {} } : console,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
pragma solidity ^0.5.0;


contract ContractA {
uint x;
constructor() public {
}

function sendFn() public {
x = 5;
}

function callFn() public pure returns (uint){
uint y = 5;
return y;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
pragma solidity ^0.5.0;


contract ContractB {
uint x;
constructor() public {
}

function sendFn() public {
x = 5;
}

function callFn() public pure returns (uint){
uint y = 5;
return y;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
pragma solidity ^0.5.0;


contract ContractC {
uint x;
constructor() public {
}

function sendFn() public {
x = 5;
}

function callFn() public pure returns (uint){
uint y = 5;
return y;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
pragma solidity >=0.4.21 <0.6.0;

contract Migrations {
address public owner;
uint public last_completed_migration;

constructor() public {
owner = msg.sender;
}

modifier restricted() {
if (msg.sender == owner) _;
}

function setCompleted(uint completed) public restricted {
last_completed_migration = completed;
}

function upgrade(address new_address) public restricted {
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
}
15 changes: 15 additions & 0 deletions test/integration/projects/ganache-solcoverjs/test/contracta.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const ContractA = artifacts.require("ContractA");

contract("contracta", function(accounts) {
let instance;

before(async () => instance = await ContractA.new())

it('sends [ @skipForCoverage ]', async function(){
await instance.sendFn();
});

it('calls [ @skipForCoverage ]', async function(){
await instance.callFn();
})
});
15 changes: 15 additions & 0 deletions test/integration/projects/ganache-solcoverjs/test/contractb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const ContractB = artifacts.require("ContractB");

contract("contractB [ @skipForCoverage ]", function(accounts) {
let instance;

before(async () => instance = await ContractB.new())

it('sends', async function(){
await instance.sendFn();
});

it('calls', async function(){
await instance.callFn();
})
});
20 changes: 20 additions & 0 deletions test/integration/projects/ganache-solcoverjs/test/contractc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const ContractC = artifacts.require("ContractC");

contract("contractc", function(accounts) {
let instance;

before(async () => instance = await ContractC.new())

it('sends', async function(){
await instance.sendFn();
});

it('calls', async function(){
await instance.callFn();
})

it('sends', async function(){
await instance.sendFn();
});

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
networks: {},
mocha: {},
compilers: {
solc: {}
}
}
Loading