parent
a316160e5b
commit
1621558758
@ -1,133 +0,0 @@ |
||||
[![Gitter chat](https://badges.gitter.im/sc-forks/solidity-coverage.svg)][18] |
||||
![npm (tag)](https://img.shields.io/npm/v/solidity-coverage/latest) |
||||
[![CircleCI](https://circleci.com/gh/sc-forks/solidity-coverage.svg?style=svg)][20] |
||||
[![codecov](https://codecov.io/gh/sc-forks/solidity-coverage/branch/beta/graph/badge.svg)][21] |
||||
[![buidler](https://buidler.dev/buidler-plugin-badge.svg?1)][26] |
||||
|
||||
# solidity-coverage |
||||
|
||||
Solidity code coverage plugin for [buidler](http://getbuidler.com). |
||||
|
||||
## What |
||||
|
||||
![coverage example][22] |
||||
|
||||
+ For more details about how it works and potential limitations, see [the accompanying article][16]. |
||||
+ `solidity-coverage` is also [JoinColony/solcover][17] |
||||
|
||||
|
||||
## Installation |
||||
|
||||
```bash |
||||
$ npm install --save-dev solidity-coverage |
||||
``` |
||||
|
||||
And add the following to your `buidler.config.js`: |
||||
|
||||
```js |
||||
usePlugin("solidity-coverage"); |
||||
|
||||
module.exports = { |
||||
networks: { |
||||
coverage: { |
||||
url: 'http://localhost:8555' |
||||
} |
||||
}, |
||||
} |
||||
``` |
||||
|
||||
## Tasks |
||||
|
||||
This plugin implements a `coverage` task |
||||
|
||||
```bash |
||||
npx buidler coverage --network coverage [options] |
||||
``` |
||||
|
||||
| Option <img width=200/> | Example <img width=750/>| Description <img width=1000/> | |
||||
|--------------|------------------------------------|--------------------------------| |
||||
| testfiles | `--testfiles "test/registry/*.ts"` | Test file(s) to run. (Globs must be enclosed by quotes.)| |
||||
| solcoverjs | `--solcoverjs ./../.solcover.js` | Relative path from working directory to config. Useful for monorepo packages that share settings. (Path must be "./" prefixed) | |
||||
| network | `--network development` | Use network settings defined in the Buidler config | |
||||
|
||||
|
||||
## Configuration |
||||
|
||||
Options can be specified in a `.solcover.js` config file located in the root directory of your project. |
||||
|
||||
**Project Examples:** |
||||
|
||||
+ Simple: [buidler-metacoin][29] |
||||
+ More complex: [MolochDao/moloch][30] |
||||
|
||||
**Config Example:** |
||||
```javascript |
||||
module.exports = { |
||||
skipFiles: ['Routers/EtherRouter.sol'] |
||||
}; |
||||
``` |
||||
|
||||
| Option <img width=200/>| Type <img width=200/> | Default <img width=1300/> | Description <img width=800/> | |
||||
| ------ | ---- | ------- | ----------- | |
||||
| silent | *Boolean* | false | Suppress logging output | |
||||
| 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', 'json']` | [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] | |
||||
| onCompileComplete[<sup>*</sup>][14] | *Function* | | Hook run *after* compilation completes, *before* tests are run. Useful if you have secondary compilation steps or need to modify built artifacts. [More...][23]| |
||||
| onTestsComplete[<sup>*</sup>][14] | *Function* | | Hook run *after* the tests complete, *before* Istanbul reports are generated. [More...][23]| |
||||
| onIstanbulComplete[<sup>*</sup>][14] | *Function* | | Hook run *after* the Istanbul reports are generated, *before* the ganache server is shut down. Useful if you need to clean resources up. [More...][23]| |
||||
|
||||
[<sup>*</sup> Advanced use][14] |
||||
|
||||
## Usage |
||||
|
||||
+ Coverage runs tests a little more slowly. |
||||
+ Coverage launches its own in-process ganache server. |
||||
+ You can set [ganache options][1] using the `providerOptions` key in your `.solcover.js` [config][15]. |
||||
+ Coverage [distorts gas consumption][13]. Tests that check exact gas consumption should be [skipped][24]. |
||||
+ :warning: Contracts are compiled **without optimization**. Please report unexpected compilation faults to [issue 417][25] |
||||
|
||||
## Documentation |
||||
|
||||
More documentation, including FAQ and information about solidity-coverage's API [is available here][28]. |
||||
|
||||
|
||||
[1]: https://github.com/trufflesuite/ganache-core#options |
||||
[2]: https://istanbul.js.org/docs/advanced/alternative-reporters/ |
||||
[3]: https://mochajs.org/api/mocha |
||||
[4]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#running-out-of-gas |
||||
[5]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#running-out-of-memory |
||||
[6]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#running-out-of-time |
||||
[7]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#continuous-integration |
||||
[8]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#notes-on-branch-coverage |
||||
[9]: https://sc-forks.github.io/metacoin/ |
||||
[10]: https://coveralls.io/github/OpenZeppelin/openzeppelin-solidity?branch=master |
||||
[11]: https://github.com/sc-forks/solidity-coverage/tree/master/test/units |
||||
[12]: https://github.com/sc-forks/solidity-coverage/issues |
||||
[13]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#notes-on-gas-distortion |
||||
[14]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/advanced.md |
||||
[15]: #config-options |
||||
[16]: https://blog.colony.io/code-coverage-for-solidity-eecfa88668c2 |
||||
[17]: https://github.com/JoinColony/solcover |
||||
[18]: https://gitter.im/sc-forks/solidity-coverage?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge |
||||
[19]: https://badge.fury.io/js/solidity-coverage |
||||
[20]: https://circleci.com/gh/sc-forks/solidity-coverage |
||||
[21]: https://codecov.io/gh/sc-forks/solidity-coverage |
||||
[22]: https://cdn-images-1.medium.com/max/800/1*uum8t-31bUaa6dTRVVhj6w.png |
||||
[23]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/advanced.md#workflow-hooks |
||||
[24]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/advanced.md#skipping-tests |
||||
[25]: https://github.com/sc-forks/solidity-coverage/issues/417 |
||||
[26]: https://buidler.dev/ |
||||
[27]: https://www.trufflesuite.com/docs |
||||
[28]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/api.md |
||||
[29]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/upgrade.md#upgrading-from-06x-to-070 |
||||
[30]: https://github.com/sc-forks/solidity-coverage/tree/0.6.x-final#solidity-coverage |
||||
[31]: https://github.com/sc-forks/solidity-coverage/releases/tag/v0.7.0 |
||||
[32]: https://github.com/sc-forks/buidler-e2e/tree/coverage |
||||
[33]: https://github.com/sc-forks/moloch |
||||
[34]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/advanced.md#reducing-the-instrumentation-footprint |
||||
|
@ -1,93 +0,0 @@ |
||||
const assert = require('assert'); |
||||
const fs = require('fs'); |
||||
const path = require('path') |
||||
const pify = require('pify') |
||||
const shell = require('shelljs'); |
||||
const ganache = require('ganache-cli') |
||||
|
||||
const verify = require('../../util/verifiers') |
||||
const mock = require('../../util/integration'); |
||||
|
||||
// =======
|
||||
// Errors
|
||||
// =======
|
||||
|
||||
describe('Buidler Plugin: error cases', function() { |
||||
let buidlerConfig; |
||||
let solcoverConfig; |
||||
|
||||
beforeEach(() => { |
||||
mock.clean(); |
||||
|
||||
mock.loggerOutput.val = ''; |
||||
solcoverConfig = { skipFiles: ['Migrations.sol']}; |
||||
buidlerConfig = mock.getDefaultBuidlerConfig(); |
||||
verify.cleanInitialState(); |
||||
}) |
||||
|
||||
afterEach(() => { |
||||
mock.buidlerTearDownEnv(); |
||||
mock.clean(); |
||||
}); |
||||
|
||||
it('.solcover.js has incorrectly formatted option', async function(){ |
||||
solcoverConfig.port = "Antwerpen"; |
||||
|
||||
mock.install('Simple', 'simple.js', solcoverConfig); |
||||
mock.buidlerSetupEnv(this); |
||||
|
||||
try { |
||||
await this.env.run("coverage"); |
||||
assert.fail() |
||||
} catch (err) { |
||||
assert( |
||||
err.message.includes('config option'), |
||||
`Should error on incorrect config options: ${err.message}` |
||||
); |
||||
} |
||||
}); |
||||
|
||||
it('tries to launch with a port already in use', async function(){ |
||||
const server = ganache.server(); |
||||
|
||||
mock.install('Simple', 'simple.js', solcoverConfig); |
||||
mock.buidlerSetupEnv(this); |
||||
|
||||
await pify(server.listen)(8555); |
||||
|
||||
try { |
||||
await this.env.run("coverage"); |
||||
assert.fail(); |
||||
} catch(err){ |
||||
assert( |
||||
err.message.includes('is already in use') && |
||||
err.message.includes('lsof'), |
||||
`Should error on port-in-use with advice: ${err.message}` |
||||
) |
||||
} |
||||
|
||||
await pify(server.close)(); |
||||
}); |
||||
|
||||
it('uses an invalid istanbul reporter', async function() { |
||||
solcoverConfig = { |
||||
silent: process.env.SILENT ? true : false, |
||||
istanbulReporter: ['does-not-exist'] |
||||
}; |
||||
|
||||
mock.install('Simple', 'simple.js', solcoverConfig); |
||||
mock.buidlerSetupEnv(this); |
||||
|
||||
try { |
||||
await this.env.run("coverage"); |
||||
assert.fail(); |
||||
} catch(err){ |
||||
assert( |
||||
err.message.includes('does-not-exist') && |
||||
err.message.includes('coverage reports could not be generated'), |
||||
`Should error on invalid reporter: ${err.message}` |
||||
) |
||||
} |
||||
|
||||
}); |
||||
}) |
@ -1,114 +0,0 @@ |
||||
const assert = require('assert'); |
||||
const fs = require('fs'); |
||||
const path = require('path') |
||||
const shell = require('shelljs'); |
||||
|
||||
const verify = require('../../util/verifiers') |
||||
const mock = require('../../util/integration'); |
||||
|
||||
// =======================
|
||||
// CLI Options / Flags
|
||||
// =======================
|
||||
|
||||
describe('Buidler Plugin: command line options', function() { |
||||
let buidlerConfig; |
||||
let solcoverConfig; |
||||
|
||||
beforeEach(function(){ |
||||
mock.clean(); |
||||
|
||||
mock.loggerOutput.val = ''; |
||||
solcoverConfig = { |
||||
skipFiles: ['Migrations.sol'], |
||||
silent: process.env.SILENT ? true : false, |
||||
istanbulReporter: ['json-summary', 'text'] |
||||
}; |
||||
buidlerConfig = mock.getDefaultBuidlerConfig(); |
||||
verify.cleanInitialState(); |
||||
}) |
||||
|
||||
afterEach(async function (){ |
||||
mock.buidlerTearDownEnv(); |
||||
mock.clean(); |
||||
}); |
||||
|
||||
|
||||
it('--temp', async function(){ |
||||
const taskArgs = { |
||||
temp: 'special_folder' |
||||
} |
||||
|
||||
mock.install('Simple', 'simple.js', solcoverConfig); |
||||
mock.buidlerSetupEnv(this); |
||||
|
||||
await this.env.run("coverage", taskArgs); |
||||
|
||||
const expected = [{ |
||||
file: mock.pathToContract(buidlerConfig, 'Simple.sol'), |
||||
pct: 100 |
||||
}]; |
||||
|
||||
verify.lineCoverage(expected); |
||||
}); |
||||
|
||||
it('--network (declared port mismatches)', async function(){ |
||||
solcoverConfig.port = 8222; |
||||
|
||||
mock.install('Simple', 'simple.js', solcoverConfig); |
||||
mock.buidlerSetupEnv(this); |
||||
|
||||
this.env.buidlerArguments.network = "development"; |
||||
|
||||
await this.env.run("coverage"); |
||||
|
||||
assert( |
||||
mock.loggerOutput.val.includes("The 'port' values"), |
||||
`Should notify about mismatched port values: ${mock.loggerOutput.val}` |
||||
); |
||||
|
||||
assert( |
||||
mock.loggerOutput.val.includes("8545"), |
||||
`Should have used default coverage port 8545: ${mock.loggerOutput.val}` |
||||
); |
||||
|
||||
assert( |
||||
mock.loggerOutput.val.includes("development"), |
||||
`Should have used specified network name: ${mock.loggerOutput.val}` |
||||
); |
||||
|
||||
const expected = [{ |
||||
file: mock.pathToContract(buidlerConfig, 'Simple.sol'), |
||||
pct: 100 |
||||
}]; |
||||
|
||||
verify.lineCoverage(expected); |
||||
}); |
||||
|
||||
it('--config ../.solcover.js', async function() { |
||||
// Write solcoverjs to parent dir of sc_temp (where the test project is installed)
|
||||
fs.writeFileSync( |
||||
'.solcover.js', |
||||
`module.exports=${JSON.stringify(solcoverConfig)}` |
||||
); |
||||
|
||||
// This relative path has to be ./ prefixed (it's path.joined to buidler's paths.root)
|
||||
const taskArgs = { |
||||
solcoverjs: './../.solcover.js' |
||||
}; |
||||
|
||||
mock.install('Simple', 'simple.js'); |
||||
mock.buidlerSetupEnv(this); |
||||
|
||||
await this.env.run("coverage", taskArgs); |
||||
|
||||
// The relative solcoverjs uses the json-summary reporter
|
||||
const expected = [{ |
||||
file: mock.pathToContract(buidlerConfig, 'Simple.sol'), |
||||
pct: 100 |
||||
}]; |
||||
|
||||
verify.lineCoverage(expected); |
||||
shell.rm('.solcover.js'); |
||||
}); |
||||
}); |
||||
|
@ -1,85 +0,0 @@ |
||||
const assert = require('assert'); |
||||
const fs = require('fs'); |
||||
const path = require('path') |
||||
const shell = require('shelljs'); |
||||
|
||||
const verify = require('../../util/verifiers') |
||||
const mock = require('../../util/integration'); |
||||
|
||||
// =======================
|
||||
// Standard Use-case Tests
|
||||
// =======================
|
||||
|
||||
describe('Buidler Plugin: standard use cases', function() { |
||||
let buidlerConfig; |
||||
let solcoverConfig; |
||||
|
||||
beforeEach(() => { |
||||
mock.clean(); |
||||
|
||||
mock.loggerOutput.val = ''; |
||||
solcoverConfig = { skipFiles: ['Migrations.sol']}; |
||||
buidlerConfig = mock.getDefaultBuidlerConfig(); |
||||
verify.cleanInitialState(); |
||||
}) |
||||
|
||||
afterEach(() => { |
||||
mock.buidlerTearDownEnv(); |
||||
mock.clean(); |
||||
}); |
||||
|
||||
it('simple contract', async function(){ |
||||
mock.install('Simple', 'simple.js', solcoverConfig); |
||||
mock.buidlerSetupEnv(this); |
||||
|
||||
await this.env.run("coverage"); |
||||
|
||||
verify.coverageGenerated(buidlerConfig); |
||||
|
||||
const output = mock.getOutput(buidlerConfig); |
||||
const path = Object.keys(output)[0]; |
||||
|
||||
assert( |
||||
output[path].fnMap['1'].name === 'test', |
||||
'coverage.json missing "test"' |
||||
); |
||||
|
||||
assert( |
||||
output[path].fnMap['2'].name === 'getX', |
||||
'coverage.json missing "getX"' |
||||
); |
||||
}); |
||||
|
||||
it('default network ("buidlerevm")', async function(){ |
||||
mock.install('Simple', 'simple.js', solcoverConfig); |
||||
mock.buidlerSetupEnv(this); |
||||
|
||||
this.env.buidlerArguments.network = "buidlerevm" |
||||
|
||||
await this.env.run("coverage"); |
||||
|
||||
assert( |
||||
mock.loggerOutput.val.includes("8555"), |
||||
`Should have used default coverage port 8555: ${mock.loggerOutput.val}` |
||||
); |
||||
|
||||
assert( |
||||
mock.loggerOutput.val.includes("soliditycoverage"), |
||||
`Should have used specified network name: ${mock.loggerOutput.val}` |
||||
); |
||||
}); |
||||
|
||||
it('tests crash w/ syntax error', async function() { |
||||
mock.install('Simple', 'truffle-crash.js', solcoverConfig); |
||||
mock.buidlerSetupEnv(this); |
||||
|
||||
try { |
||||
await this.env.run("coverage"); |
||||
assert.fail() |
||||
} catch(err){ |
||||
assert(err.toString().includes('SyntaxError')); |
||||
} |
||||
|
||||
verify.coverageGenerated(buidlerConfig); |
||||
}); |
||||
}) |
Loading…
Reference in new issue