diff --git a/README.md b/README.md index 2d6b397..e6203fc 100644 --- a/README.md +++ b/README.md @@ -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: "./"` would tell solidity-coverage to look for `.//contracts/` and `.//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. diff --git a/bin/exec.js b/bin/exec.js index 328b521..0392722 100644 --- a/bin/exec.js +++ b/bin/exec.js @@ -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) { diff --git a/test/cli.js b/test/cli.js index 186a02f..c8c394e 100644 --- a/test/cli.js +++ b/test/cli.js @@ -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');