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.
allow-exclusions
Alex Rea 7 years ago
parent c0d5f2ecf3
commit c500b9df7a
  1. 3
      README.md
  2. 14
      bin/exec.js
  3. 22
      test/cli.js

@ -76,6 +76,7 @@ can be useful if you are using a different vm like the [sc-forks version of pyet
directory. `dir` allows you to define a relative path from the root directory to those assets.
`dir: "./<dirname>"` would tell solidity-coverage to look for `./<dirname>/contracts/` and `./<dirname>/test/`
+ **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.
+ **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.
**Example .solcover.js config file**
```javascript
@ -97,7 +98,7 @@ the extra events. If this is the case, then the coverage may be incomplete. To a
**Using `require` in `migrations.js` files**: Truffle overloads Node's `require` function but
implements a simplified search algorithm for node_modules packages
([see Truffle issue #383](https://github.com/trufflesuite/truffle/issues/383)).
([see Truffle issue #383](https://github.com/trufflesuite/truffle/issues/383)).
Because solidity-coverage copies an instrumented version of your project into a temporary folder, `require`
statements handled by Truffle internally won't resolve correctly.

@ -62,6 +62,7 @@ const workingDir = config.dir || '.'; // Relative path to contracts folder
let port = config.port || 8555; // Port testrpc listens on
const accounts = config.accounts || 35; // Number of accounts to testrpc launches with
const copyNodeModules = config.copyNodeModules || false; // Whether we copy node_modules when making coverage environment
let skipFiles = config.skipFiles || []; // Which files should be skipped during instrumentation
// Silence shell and script logging (for solcover's unit tests / CI)
if (config.silent) {
@ -89,7 +90,7 @@ try {
// Coverage network opts specified: use port if declared
if (truffleConfig && truffleConfig.networks && truffleConfig.networks.coverage) {
port = truffleConfig.networks.coverage.port || port;
// Coverage network opts NOT specified: default to the development network w/ modified
// port, gasLimit, gasPrice. Export the config object only.
} else {
@ -114,18 +115,19 @@ try {
cleanUp(msg + err);
}
// For each contract except migrations.sol:
// For each contract except migrations.sol (or those in skipFiles):
// 1. Generate file path reference for coverage report
// 2. Load contract as string
// 3. Instrument contract
// 4. Save instrumented contract in the coverage environment folder where covered tests will run
// 5. Add instrumentation info to the coverage map
skipFiles = skipFiles.map(contract => `${coverageDir}/contracts/` + contract);
skipFiles.push(`${coverageDir}/contracts/Migrations.sol`);
let currentFile;
try {
shell.ls(`${coverageDir}/contracts/**/*.sol`).forEach(file => {
const migrations = `${coverageDir}/contracts/Migrations.sol`;
if (file !== migrations) {
if (!skipFiles.includes(file)) {
log('Instrumenting ', file);
currentFile = file;
@ -135,6 +137,8 @@ try {
const instrumentedContractInfo = getInstrumentedVersion(contract, canonicalPath);
fs.writeFileSync(contractPath, instrumentedContractInfo.contract);
coverage.addContract(instrumentedContractInfo, canonicalPath);
} else {
log('Skipping instrumentation of ', file);
}
});
} catch (err) {

@ -242,6 +242,28 @@ describe('cli', () => {
collectGarbage();
});
it('contracts are skipped: should generate coverage, cleanup & exit(0)', () => {
// Skip instrumentation of some contracts
assert(pathExists('./coverage') === false, 'should start without: coverage');
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
const testConfig = Object.assign({}, config);
testConfig.skipFiles = ['Owned.sol'];
mock.installInheritanceTest(testConfig);
shell.exec(script);
assert(shell.error() === null, 'script should not error');
assert(pathExists('./coverage') === true, 'script should gen coverage folder');
assert(pathExists('./coverage.json') === true, 'script should gen coverage.json');
const produced = JSON.parse(fs.readFileSync('./coverage.json', 'utf8'));
const firstKey = Object.keys(produced)[0];
assert(Object.keys(produced).length === 1, 'coverage.json should only contain instrumentation for one contract');
assert(firstKey.substr(firstKey.length - 9) === 'Proxy.sol', 'coverage.json should only contain instrumentation for Proxy.sol');
collectGarbage();
});
it('truffle tests failing: should generate coverage, cleanup & exit(1)', () => {
assert(pathExists('./coverage') === false, 'should start without: coverage');
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');

Loading…
Cancel
Save