From cc3e18a34a981607b412bb729ffbf7c8aa663db9 Mon Sep 17 00:00:00 2001 From: Kanstantsin Andryanau Date: Mon, 9 Sep 2019 01:04:01 -0700 Subject: [PATCH] Istanbul reporter (#388) * Make istanbul reporter configurable * Add coverage expectations to plugin integration tests * Add kandrianov to contributors list --- README.md | 6 +- lib/app.js | 7 +- .../projects/import-paths/.solcover.js | 3 +- .../projects/multiple-migrations/.solcover.js | 1 + .../projects/skipping/.solcover.js | 3 +- .../projects/test-files/.solcover.js | 1 + test/units/app.js | 94 +++++++++++++++++++ 7 files changed, 108 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 48a7e04..9b87527 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ a local dependency, please see [this section](https://github.com/sc-forks/solidi ### Network Configuration By default, this tool connects to a coverage-enabled fork of ganache-cli -called **testrpc-sc** on port 8555. +called **testrpc-sc** on port 8555. + it's a dependency - there's nothing extra to download. + the solidity-coverage command launches it automatically in the background. (See [this section of the FAQ](https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#running-testrpc-sc-on-its-own) if you need to launch it separately yourself) @@ -95,7 +95,8 @@ module.exports = { | skipFiles | *Array* | `['Migrations.sol']` | Array of contracts or folders (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. | | deepSkip | boolean | false | Use this if instrumentation hangs on large, skipped files (like Oraclize). It's faster. | | dir | *String* | `.` | Solidity-coverage copies all the assets in your root directory (except `node_modules`) to a special folder where it instruments the contracts and executes the tests. `dir` allows you to define a relative path from the root directory to those assets. Useful if your contracts & tests are within their own folder as part of a larger project.| -| buildDirPath | *String* | `/build/contracts` | Build directory path for compiled smart contracts +| buildDirPath | *String* | `/build/contracts` | Build directory path for compiled smart contracts | +| istanbulReporter | *Array* | ['html', 'lcov', 'text'] | Coverage reporters for Istanbul. Optional reporter replaces the default reporters. | ### FAQ @@ -146,3 +147,4 @@ $ yarn + [@pinkiebell](https://github.com/pinkiebell) + [@obernardovieira](https://github.com/obernardovieira) + [@angus-hamill](https://github.com/angus-hamill) ++ [@kandrianov](https://github.com/kandrianov) diff --git a/lib/app.js b/lib/app.js index bcfbd02..af29aff 100644 --- a/lib/app.js +++ b/lib/app.js @@ -42,6 +42,8 @@ class App { this.gasLimit = 0xfffffffffff; this.gasLimitString = "0xfffffffffff"; this.gasPrice = 0x01; + + this.istanbulReporter = config.istanbulReporter || ['html', 'lcov', 'text']; } // -------------------------------------- Methods ----------------------------------------------- @@ -122,9 +124,8 @@ class App { this.saveCoverage(relativeMapping); collector.add(relativeMapping); - reporter.add('html'); - reporter.add('lcov'); - reporter.add('text'); + + this.istanbulReporter.forEach(report => reporter.add(report)); reporter.write(collector, true, () => { this.log('Istanbul coverage reports generated'); diff --git a/test/integration/projects/import-paths/.solcover.js b/test/integration/projects/import-paths/.solcover.js index f328e37..4278185 100644 --- a/test/integration/projects/import-paths/.solcover.js +++ b/test/integration/projects/import-paths/.solcover.js @@ -1,3 +1,4 @@ module.exports = { silent: process.env.SILENT ? true : false, -}; + istanbulReporter: ['json-summary', 'text'] +} diff --git a/test/integration/projects/multiple-migrations/.solcover.js b/test/integration/projects/multiple-migrations/.solcover.js index f328e37..7a54eb3 100644 --- a/test/integration/projects/multiple-migrations/.solcover.js +++ b/test/integration/projects/multiple-migrations/.solcover.js @@ -1,3 +1,4 @@ module.exports = { silent: process.env.SILENT ? true : false, + istanbulReporter: ['json-summary', 'text'] }; diff --git a/test/integration/projects/skipping/.solcover.js b/test/integration/projects/skipping/.solcover.js index 681cd56..4be0bbd 100644 --- a/test/integration/projects/skipping/.solcover.js +++ b/test/integration/projects/skipping/.solcover.js @@ -1,4 +1,5 @@ module.exports = { silent: process.env.SILENT ? true : false, - skipFiles: ['skipped-folder'] + skipFiles: ['skipped-folder'], + istanbulReporter: ['json-summary', 'text'] } diff --git a/test/integration/projects/test-files/.solcover.js b/test/integration/projects/test-files/.solcover.js index 8eb203c..4278185 100644 --- a/test/integration/projects/test-files/.solcover.js +++ b/test/integration/projects/test-files/.solcover.js @@ -1,3 +1,4 @@ module.exports = { silent: process.env.SILENT ? true : false, + istanbulReporter: ['json-summary', 'text'] } diff --git a/test/units/app.js b/test/units/app.js index f707771..9970999 100644 --- a/test/units/app.js +++ b/test/units/app.js @@ -12,6 +12,20 @@ const opts = { compact: false, depth: 5, breakLength: 80 }; // ======= function pathExists(path) { return shell.test('-e', path); } +function pathToContract(config, file) { + return path.join('contracts', file); +} + +function assertLineCoverage(expected=[]){ + let summary = JSON.parse(fs.readFileSync('coverage/coverage-summary.json')); + expected.forEach(item => assert(summary[item.file].lines.pct === item.pct)) +} + +function assertCoverageMissing(expected=[]){ + let summary = JSON.parse(fs.readFileSync('coverage/coverage-summary.json')); + expected.forEach(item => assert(summary[item.file] === undefined)) +} + function assertCleanInitialState(){ assert(pathExists('./coverage') === false, 'should start without: coverage'); assert(pathExists('./coverage.json') === false, 'should start without: coverage.json'); @@ -88,12 +102,41 @@ describe('app', function() { assertCleanInitialState(); mock.installFullProject('multiple-migrations'); await plugin(truffleConfig); + + const expected = [ + { + file: pathToContract(truffleConfig, 'ContractA.sol'), + pct: 100 + }, + { + file: pathToContract(truffleConfig, 'ContractB.sol'), + pct: 100, + }, + { + file: pathToContract(truffleConfig, 'ContractC.sol'), + pct: 100, + }, + ]; + + assertLineCoverage(expected); }); it('project skips a folder', async function() { assertCleanInitialState(); mock.installFullProject('skipping'); await plugin(truffleConfig); + + const expected = [{ + file: pathToContract(truffleConfig, 'ContractA.sol'), + pct: 100 + }]; + + const missing = [{ + file: pathToContract(truffleConfig, 'ContractB.sol'), + }]; + + assertLineCoverage(expected); + assertCoverageMissing(missing); }); it('project with relative path solidity imports', async function() { @@ -109,6 +152,23 @@ describe('app', function() { truffleConfig.file = testPath; mock.installFullProject('test-files'); await plugin(truffleConfig); + + const expected = [ + { + file: pathToContract(truffleConfig, 'ContractA.sol'), + pct: 100 + }, + { + file: pathToContract(truffleConfig, 'ContractB.sol'), + pct: 0, + }, + { + file: pathToContract(truffleConfig, 'ContractC.sol'), + pct: 0, + }, + ]; + + assertLineCoverage(expected); }); it('truffle run coverage --file test/', async function() { @@ -118,6 +178,23 @@ describe('app', function() { truffleConfig.file = testPath; mock.installFullProject('test-files'); await plugin(truffleConfig); + + const expected = [ + { + file: pathToContract(truffleConfig, 'ContractA.sol'), + pct: 0, + }, + { + file: pathToContract(truffleConfig, 'ContractB.sol'), + pct: 100, + }, + { + file: pathToContract(truffleConfig, 'ContractC.sol'), + pct: 100, + }, + ]; + + assertLineCoverage(expected); }); it('truffle run coverage --file test/gl{o,b}*.js', async function() { @@ -127,6 +204,23 @@ describe('app', function() { truffleConfig.file = testPath; mock.installFullProject('test-files'); await plugin(truffleConfig); + + const expected = [ + { + file: pathToContract(truffleConfig, 'ContractA.sol'), + pct: 0, + }, + { + file: pathToContract(truffleConfig, 'ContractB.sol'), + pct: 100, + }, + { + file: pathToContract(truffleConfig, 'ContractC.sol'), + pct: 100, + }, + ]; + + assertLineCoverage(expected); }); it('contract only uses ".call"', async function(){