Skip to content

Commit 3899f6d

Browse files
authored
Make statement and function coverage measurement optional (sc-forks#538)
1 parent 2bfee04 commit 3899f6d

File tree

12 files changed

+117
-27
lines changed

12 files changed

+117
-27
lines changed

BUIDLER_README.md

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ npx buidler coverage --network coverage [options]
4646

4747
| Option <img width=200/> | Example <img width=750/>| Description <img width=1000/> |
4848
|--------------|------------------------------------|--------------------------------|
49-
| testfiles | `--testfiles="test/registry/*.ts"` | Test file(s) to run. (Globs must be enclosed by quotes.)|
49+
| testfiles | `--testfiles "test/registry/*.ts"` | Test file(s) to run. (Globs must be enclosed by quotes.)|
5050
| solcoverjs | `--solcoverjs ./../.solcover.js` | Relative path from working directory to config. Useful for monorepo packages that share settings. (Path must be "./" prefixed) |
5151
| network | `--network development` | Use network settings defined in the Buidler config |
5252

@@ -99,17 +99,17 @@ More documentation, including FAQ and information about solidity-coverage's API
9999
[1]: https://github.com/trufflesuite/ganache-core#options
100100
[2]: https://istanbul.js.org/docs/advanced/alternative-reporters/
101101
[3]: https://mochajs.org/api/mocha
102-
[4]: https://github.com/sc-forks/solidity-coverage/blob/beta/docs/faq.md#running-out-of-gas
103-
[5]: https://github.com/sc-forks/solidity-coverage/blob/beta/docs/faq.md#running-out-of-memory
104-
[6]: https://github.com/sc-forks/solidity-coverage/blob/beta/docs/faq.md#running-out-of-time
105-
[7]: https://github.com/sc-forks/solidity-coverage/blob/beta/docs/faq.md#continuous-integration
106-
[8]: https://github.com/sc-forks/solidity-coverage/blob/beta/docs/faq.md#notes-on-branch-coverage
102+
[4]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#running-out-of-gas
103+
[5]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#running-out-of-memory
104+
[6]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#running-out-of-time
105+
[7]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#continuous-integration
106+
[8]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#notes-on-branch-coverage
107107
[9]: https://sc-forks.github.io/metacoin/
108108
[10]: https://coveralls.io/github/OpenZeppelin/openzeppelin-solidity?branch=master
109-
[11]: https://github.com/sc-forks/solidity-coverage/tree/beta/test/units
109+
[11]: https://github.com/sc-forks/solidity-coverage/tree/master/test/units
110110
[12]: https://github.com/sc-forks/solidity-coverage/issues
111-
[13]: https://github.com/sc-forks/solidity-coverage/blob/beta/docs/faq.md#notes-on-gas-distortion
112-
[14]: https://github.com/sc-forks/solidity-coverage/blob/beta/docs/advanced.md
111+
[13]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#notes-on-gas-distortion
112+
[14]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/advanced.md
113113
[15]: #config-options
114114
[16]: https://blog.colony.io/code-coverage-for-solidity-eecfa88668c2
115115
[17]: https://github.com/JoinColony/solcover
@@ -118,12 +118,16 @@ More documentation, including FAQ and information about solidity-coverage's API
118118
[20]: https://circleci.com/gh/sc-forks/solidity-coverage
119119
[21]: https://codecov.io/gh/sc-forks/solidity-coverage
120120
[22]: https://cdn-images-1.medium.com/max/800/1*uum8t-31bUaa6dTRVVhj6w.png
121-
[23]: https://github.com/sc-forks/solidity-coverage/blob/beta/docs/advanced.md#workflow-hooks
122-
[24]: https://github.com/sc-forks/solidity-coverage/blob/beta/docs/advanced.md#skipping-tests
121+
[23]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/advanced.md#workflow-hooks
122+
[24]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/advanced.md#skipping-tests
123123
[25]: https://github.com/sc-forks/solidity-coverage/issues/417
124124
[26]: https://buidler.dev/
125125
[27]: https://www.trufflesuite.com/docs
126-
[28]: https://github.com/sc-forks/solidity-coverage/blob/beta/README.md
127-
[29]: https://github.com/sc-forks/buidler-e2e/tree/coverage
128-
[30]: https://github.com/sc-forks/moloch
126+
[28]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/api.md
127+
[29]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/upgrade.md#upgrading-from-06x-to-070
128+
[30]: https://github.com/sc-forks/solidity-coverage/tree/0.6.x-final#solidity-coverage
129+
[31]: https://github.com/sc-forks/solidity-coverage/releases/tag/v0.7.0
130+
[32]: https://github.com/sc-forks/buidler-e2e/tree/coverage
131+
[33]: https://github.com/sc-forks/moloch
132+
[34]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/advanced.md#reducing-the-instrumentation-footprint
129133

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ npx buidler coverage --network coverage [command-options]
7474
| Option <img width=200/> | Example <img width=750/>| Description <img width=1000/> |
7575
|--------------|------------------------------------|--------------------------------|
7676
| file | `--file="test/registry/*.js"` | (Truffle) Filename or glob describing a subset of tests to run. (Globs must be enclosed by quotes.)|
77-
| testfiles | `--testfiles="test/registry/*.ts"` | (Buidler) Test file(s) to run. (Globs must be enclosed by quotes.)|
77+
| testfiles | `--testfiles "test/registry/*.ts"` | (Buidler) Test file(s) to run. (Globs must be enclosed by quotes.)|
7878
| solcoverjs | `--solcoverjs ./../.solcover.js` | Relative path from working directory to config. Useful for monorepo packages that share settings. (Path must be "./" prefixed) |
7979
| network | `--network development` | Use network settings defined in the Truffle or Buidler config |
8080
| temp[<sup>*</sup>][14] | `--temp build` | :warning: **Caution** :warning: Path to a *disposable* folder to store compilation artifacts in. Useful when your test setup scripts include hard-coded paths to a build directory. [More...][14] |
@@ -99,6 +99,8 @@ module.exports = {
9999
| client | *Object* | `require("ganache-core")` | Useful if you need a specific ganache version. |
100100
| providerOptions | *Object* | `{ }` | [ganache-core options][1] |
101101
| skipFiles | *Array* | `['Migrations.sol']` | Array of contracts or folders (with paths expressed relative to the `contracts` directory) that should be skipped when doing instrumentation. |
102+
| measureStatementCoverage | *boolean* | `true` | Computes statement (in addition to line) coverage. [More...][34] |
103+
| measureFunctionCoverage | *boolean* | `true` | Computes function coverage. [More...][34] |
102104
| istanbulFolder | *String* | `./coverage` | Folder location for Istanbul coverage reports. |
103105
| istanbulReporter | *Array* | `['html', 'lcov', 'text', 'json']` | [Istanbul coverage reporters][2] |
104106
| 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.|
@@ -204,3 +206,5 @@ $ yarn
204206
[31]: https://github.com/sc-forks/solidity-coverage/releases/tag/v0.7.0
205207
[32]: https://github.com/sc-forks/buidler-e2e/tree/coverage
206208
[33]: https://github.com/sc-forks/moloch
209+
[34]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/advanced.md#reducing-the-instrumentation-footprint
210+

docs/advanced.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Skipping tests
44

5-
Sometimes it's convenient to skip specific tests when running coverage. You can do this by
5+
Sometimes it's convenient to skip specific tests when running coverage. You can do this by
66
tagging your test descriptions and setting appropriate filters in the `.solcover.js` mocha options.
77

88
**Example**
@@ -79,10 +79,21 @@ $ truffle run coverage --temp build
7979

8080
By default this folder is called `.coverage_artifacts`. If you already have
8181
preparatory scripts which run between compilation and the tests, you'll probably
82-
find it inconvenient to modify them to handle an alternate path.
82+
find it inconvenient to modify them to handle an alternate path.
8383

84-
This option allows you to avoid that but it's important to realise that the temp
85-
folder is **automatically deleted** when coverage completes. You shouldn't use it if your preferred
84+
This option allows you to avoid that but it's important to realise that the temp
85+
folder is **automatically deleted** when coverage completes. You shouldn't use it if your preferred
8686
build target contains information you want to preserve between test runs.
8787

88+
## Reducing the instrumentation footprint
89+
90+
If your project is very large or if you have logic that's gas sensitive, it can be useful to
91+
minimize the amount of instrumentation the coverage tool adds to your Solidity code.
92+
93+
Usually you're only interested in line and branch coverage but Istanbul also collects data for individual
94+
statements and "functions" (e.g - whether every declared function has been called).
95+
96+
Setting the `measureStatementCoverage` and/or `measureFunctionCoverage` options to `false` can
97+
improve performance, lower the cost of execution and minimize complications that arise from `solc`'s
98+
limits on how large the compilation payload can be.
8899

lib/api.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ const AppUI = require('./ui').AppUI;
1818
*/
1919
class API {
2020
constructor(config={}) {
21-
this.coverage = new Coverage();
22-
this.instrumenter = new Instrumenter();
2321
this.validator = new ConfigValidator()
2422
this.config = config || {};
2523

2624
// Validate
2725
this.validator.validate(this.config);
26+
this.coverage = new Coverage();
27+
this.instrumenter = new Instrumenter(this.config);
2828

2929
// Options
3030
this.testsErrored = false;

lib/injector.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class Injector {
2222
}
2323

2424
_getMethodIdentifier(id){
25-
return `coverage_${web3Utils.keccak256(id).slice(0,10)}`
25+
return `c_${web3Utils.keccak256(id).slice(0,10)}`
2626
}
2727

2828
_getInjectionComponents(contract, injectionPoint, id, type){

lib/instrumenter.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ const parse = require('./parse');
1212
*/
1313
class Instrumenter {
1414

15-
constructor(){
15+
constructor(config={}){
1616
this.instrumentationData = {};
1717
this.injector = new Injector();
18+
this.measureStatementCoverage = (config.measureStatementCoverage === false) ? false : true;
19+
this.measureFunctionCoverage = (config.measureFunctionCoverage === false) ? false: true;
1820
}
1921

2022
_isRootNode(node){
@@ -58,6 +60,8 @@ class Instrumenter {
5860
contract.instrumented = contractSource;
5961

6062
this._initializeCoverageFields(contract);
63+
parse.configureStatementCoverage(this.measureStatementCoverage)
64+
parse.configureFunctionCoverage(this.measureFunctionCoverage)
6165

6266
// First, we run over the original contract to get the source mapping.
6367
let ast = SolidityParser.parse(contract.source, {loc: true, range: true});
@@ -102,7 +106,6 @@ class Instrumenter {
102106
retValue.runnableLines = contract.runnableLines;
103107
retValue.contract = contract.instrumented;
104108
retValue.contractName = contract.contractName;
105-
106109
return retValue;
107110
}
108111
}

lib/parse.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ const register = new Registrar();
88

99
const parse = {};
1010

11+
// Utilities
12+
parse.configureStatementCoverage = function(val){
13+
register.measureStatementCoverage = val;
14+
}
15+
16+
parse.configureFunctionCoverage = function(val){
17+
register.measureFunctionCoverage = val;
18+
}
19+
20+
// Nodes
1121
parse.AssignmentExpression = function(contract, expression) {
1222
register.statement(contract, expression);
1323
};

lib/registrar.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@
66
class Registrar {
77

88
constructor(){
9-
// Sometimes we don't want to inject statements
10-
// because they're an unnecessary expense. ex: `receive`
9+
// When, at *certain nodes* we don't want to inject statements
10+
// because they're an unnecessary expense. ex: `receive`, this
11+
// can be toggled on/off in the parser
1112
this.trackStatements = true;
13+
14+
// These are set by user option and enable/disable the measurement completely
15+
this.measureStatementCoverage = true;
16+
this.measureFunctionCoverage = true;
1217
}
1318

1419
/**
@@ -31,7 +36,7 @@ class Registrar {
3136
* @param {Object} expression AST node
3237
*/
3338
statement(contract, expression) {
34-
if (!this.trackStatements) return;
39+
if (!this.trackStatements || !this.measureStatementCoverage) return;
3540

3641
const startContract = contract.instrumented.slice(0, expression.range[0]);
3742
const startline = ( startContract.match(/\n/g) || [] ).length + 1;
@@ -102,6 +107,8 @@ class Registrar {
102107
* @param {Object} expression AST node
103108
*/
104109
functionDeclaration(contract, expression) {
110+
if (!this.measureFunctionCoverage) return;
111+
105112
let start = 0;
106113

107114
// It's possible functions will have modifiers that take string args

lib/validator.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ const configSchema = {
2020
silent: {type: "boolean"},
2121
autoLaunchServer: {type: "boolean"},
2222
istanbulFolder: {type: "string"},
23+
measureStatementCoverage: {type: "boolean"},
24+
measureFunctionCoverage: {type: "boolean"},
2325

2426
// Hooks:
2527
onServerReady: {type: "function", format: "isFunction"},

test/units/truffle/standard.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,25 @@ describe('Truffle Plugin: standard use cases', function() {
414414
);
415415
});
416416

417+
it('config: includeStatementCoverage, includeFunctionCoverage', async function(){
418+
solcoverConfig.istanbulReporter = ['json-summary', 'text']
419+
solcoverConfig.measureStatementCoverage = false;
420+
solcoverConfig.measureFunctionCoverage = false;
421+
422+
mock.install('Simple', 'simple.js', solcoverConfig);
423+
await plugin(truffleConfig);
424+
425+
const expected = [
426+
{
427+
file: mock.pathToContract(truffleConfig, 'Simple.sol'),
428+
total: 0
429+
}
430+
];
431+
432+
verify.statementCoverage(expected, 'total');
433+
verify.functionCoverage(expected, 'total');
434+
});
435+
417436
// Fails with Truffle 5.0.31, but newer Truffle causes OOM when running whole suite.
418437
// Running the same test with Buidler though...
419438
it.skip('solc 0.6.x', async function(){

0 commit comments

Comments
 (0)