Skip to content

Commit c500b9d

Browse files
committed
Allow files to be skipped during coverage
While ordinarily we shouldn't want to do these, it is possible to construct valid contracts using assembly that break when the coverage events are injected.
1 parent c0d5f2e commit c500b9d

File tree

3 files changed

+33
-6
lines changed

3 files changed

+33
-6
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ can be useful if you are using a different vm like the [sc-forks version of pyet
7676
directory. `dir` allows you to define a relative path from the root directory to those assets.
7777
`dir: "./<dirname>"` would tell solidity-coverage to look for `./<dirname>/contracts/` and `./<dirname>/test/`
7878
+ **copyNodeModules**: *{ Boolean }* : When true, will copy `node_modules` into the coverage environment. False by default, and may significantly increase the time for coverage to complete if enabled. Only enable if required.
79+
+ **skipFiles**: *{ Array }* : An array of contracts (with paths expressed relative to the `contracts` directory) that should be skipped when doing instrumentation. `Migrations.sol` is skipped by default, and does not need to be added to this configuration option if it is used.
7980

8081
**Example .solcover.js config file**
8182
```javascript
@@ -97,7 +98,7 @@ the extra events. If this is the case, then the coverage may be incomplete. To a
9798

9899
**Using `require` in `migrations.js` files**: Truffle overloads Node's `require` function but
99100
implements a simplified search algorithm for node_modules packages
100-
([see Truffle issue #383](https://github.com/trufflesuite/truffle/issues/383)).
101+
([see Truffle issue #383](https://github.com/trufflesuite/truffle/issues/383)).
101102
Because solidity-coverage copies an instrumented version of your project into a temporary folder, `require`
102103
statements handled by Truffle internally won't resolve correctly.
103104

bin/exec.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ const workingDir = config.dir || '.'; // Relative path to contracts folder
6262
let port = config.port || 8555; // Port testrpc listens on
6363
const accounts = config.accounts || 35; // Number of accounts to testrpc launches with
6464
const copyNodeModules = config.copyNodeModules || false; // Whether we copy node_modules when making coverage environment
65+
let skipFiles = config.skipFiles || []; // Which files should be skipped during instrumentation
6566

6667
// Silence shell and script logging (for solcover's unit tests / CI)
6768
if (config.silent) {
@@ -89,7 +90,7 @@ try {
8990
// Coverage network opts specified: use port if declared
9091
if (truffleConfig && truffleConfig.networks && truffleConfig.networks.coverage) {
9192
port = truffleConfig.networks.coverage.port || port;
92-
93+
9394
// Coverage network opts NOT specified: default to the development network w/ modified
9495
// port, gasLimit, gasPrice. Export the config object only.
9596
} else {
@@ -114,18 +115,19 @@ try {
114115
cleanUp(msg + err);
115116
}
116117

117-
// For each contract except migrations.sol:
118+
// For each contract except migrations.sol (or those in skipFiles):
118119
// 1. Generate file path reference for coverage report
119120
// 2. Load contract as string
120121
// 3. Instrument contract
121122
// 4. Save instrumented contract in the coverage environment folder where covered tests will run
122123
// 5. Add instrumentation info to the coverage map
124+
skipFiles = skipFiles.map(contract => `${coverageDir}/contracts/` + contract);
125+
skipFiles.push(`${coverageDir}/contracts/Migrations.sol`);
126+
123127
let currentFile;
124128
try {
125129
shell.ls(`${coverageDir}/contracts/**/*.sol`).forEach(file => {
126-
const migrations = `${coverageDir}/contracts/Migrations.sol`;
127-
128-
if (file !== migrations) {
130+
if (!skipFiles.includes(file)) {
129131
log('Instrumenting ', file);
130132
currentFile = file;
131133

@@ -135,6 +137,8 @@ try {
135137
const instrumentedContractInfo = getInstrumentedVersion(contract, canonicalPath);
136138
fs.writeFileSync(contractPath, instrumentedContractInfo.contract);
137139
coverage.addContract(instrumentedContractInfo, canonicalPath);
140+
} else {
141+
log('Skipping instrumentation of ', file);
138142
}
139143
});
140144
} catch (err) {

test/cli.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,28 @@ describe('cli', () => {
242242
collectGarbage();
243243
});
244244

245+
it('contracts are skipped: should generate coverage, cleanup & exit(0)', () => {
246+
// Skip instrumentation of some contracts
247+
assert(pathExists('./coverage') === false, 'should start without: coverage');
248+
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
249+
const testConfig = Object.assign({}, config);
250+
251+
testConfig.skipFiles = ['Owned.sol'];
252+
mock.installInheritanceTest(testConfig);
253+
254+
shell.exec(script);
255+
assert(shell.error() === null, 'script should not error');
256+
257+
assert(pathExists('./coverage') === true, 'script should gen coverage folder');
258+
assert(pathExists('./coverage.json') === true, 'script should gen coverage.json');
259+
260+
const produced = JSON.parse(fs.readFileSync('./coverage.json', 'utf8'));
261+
const firstKey = Object.keys(produced)[0];
262+
assert(Object.keys(produced).length === 1, 'coverage.json should only contain instrumentation for one contract');
263+
assert(firstKey.substr(firstKey.length - 9) === 'Proxy.sol', 'coverage.json should only contain instrumentation for Proxy.sol');
264+
collectGarbage();
265+
});
266+
245267
it('truffle tests failing: should generate coverage, cleanup & exit(1)', () => {
246268
assert(pathExists('./coverage') === false, 'should start without: coverage');
247269
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');

0 commit comments

Comments
 (0)