Add logicalOR branch highlighting for html report

logical-or-coverage
cgewecke 5 years ago
parent 0763bd6ee9
commit 4f07bd6944
  1. 35
      lib/registrar.js
  2. 8
      test/integration/projects/logical-or/.solcover.js
  3. 7
      test/integration/projects/logical-or/buidler.config.js
  4. 40
      test/integration/projects/logical-or/contracts/ContractA.sol
  5. 33
      test/integration/projects/logical-or/test/test.js
  6. 7
      test/integration/projects/logical-or/truffle-config.js
  7. 11
      test/sources/solidity/contracts/or/require-multiline-or.sol
  8. 21
      test/units/buidler/standard.js
  9. 27
      test/units/or.js
  10. 14
      test/units/truffle/standard.js
  11. 14
      test/util/verifiers.js

@ -182,29 +182,50 @@ class Registrar {
* @param {Object} expression AST node * @param {Object} expression AST node
*/ */
addNewLogicalORBranch(contract, expression) { addNewLogicalORBranch(contract, expression) {
const startContract = contract.instrumented.slice(0, expression.range[0]); let start;
const startline = ( startContract.match(/\n/g) || [] ).length + 1;
// Instabul HTML highlighting location data...
const leftZero = expression.left.range[0];
const leftOne = expression.left.range[1];
const rightZero = expression.right.range[0];
const rightOne = expression.right.range[1];
start = contract.instrumented.slice(0, leftZero);
const leftStartLine = ( start.match(/\n/g) || [] ).length + 1;
const leftStartCol = leftZero - start.lastIndexOf('\n') - 1;
start = contract.instrumented.slice(0, leftOne);
const leftEndLine = ( start.match(/\n/g) || [] ).length + 1;
const leftEndCol = leftOne - start.lastIndexOf('\n') - 1;
start = contract.instrumented.slice(0, rightZero);
const rightStartLine = ( start.match(/\n/g) || [] ).length + 1;
const rightStartCol = rightZero - start.lastIndexOf('\n') - 1;
start = contract.instrumented.slice(0, rightOne);
const rightEndLine = ( start.match(/\n/g) || [] ).length + 1;
const rightEndCol = rightOne - start.lastIndexOf('\n') - 1;
contract.branchId += 1; contract.branchId += 1;
// NB locations for if branches in istanbul are zero // NB locations for if branches in istanbul are zero
// length and associated with the start of the if. // length and associated with the start of the if.
contract.branchMap[contract.branchId] = { contract.branchMap[contract.branchId] = {
line: startline, line: leftStartLine,
type: 'cond-expr', type: 'cond-expr',
locations: [{ locations: [{
start: { start: {
line: startline, column: expression.left.range[0], line: leftStartLine, column: leftStartCol,
}, },
end: { end: {
line: startline, column: expression.left.range[1], line: leftEndLine, column: leftEndCol,
}, },
}, { }, {
start: { start: {
line: startline, column: expression.right.range[0], line: rightStartLine, column: rightStartCol,
}, },
end: { end: {
line: startline, column: expression.right.range[1], line: rightEndLine, column: rightEndCol,
}, },
}], }],
}; };

@ -0,0 +1,8 @@
// Testing hooks
const fn = (msg, config) => config.logger.log(msg);
module.exports = {
skipFiles: ['Migrations.sol'],
silent: process.env.SILENT ? true : false,
istanbulReporter: ['json-summary', 'text'],
}

@ -0,0 +1,7 @@
const { loadPluginFile } = require("@nomiclabs/buidler/plugins-testing");
loadPluginFile(__dirname + "/../plugins/buidler.plugin");
usePlugin("@nomiclabs/buidler-truffle5");
module.exports={
defaultNetwork: "buidlerevm"
};

@ -0,0 +1,40 @@
pragma solidity ^0.5.0;
contract ContractA {
function _if(uint i) public pure {
if (i == 0 || i > 5){
/* ignore */
}
}
function _if_and(uint i) public pure {
if (i != 0 && (i < 2 || i > 5)){
/* ignore */
}
}
function _return(uint i) public pure returns (bool){
return (i != 0 && i != 1 ) ||
((i + 1) == 2);
}
function _while(uint i) public pure returns (bool){
uint counter;
while( (i == 1 || i == 2) && counter < 2 ){
counter++;
}
}
function _require(uint x) public {
require(x == 1 || x == 2);
}
function _require_multi_line(uint x) public {
require(
(x == 1 || x == 2) ||
x == 3
);
}
}

@ -0,0 +1,33 @@
const ContractA = artifacts.require("ContractA");
contract("contracta", function(accounts) {
let instance;
before(async () => instance = await ContractA.new())
it('_if', async function(){
await instance._if(0);
await instance._if(7);
});
it('_if_and', async function(){
await instance._if_and(1);
});
it('_return', async function(){
await instance._return(4);
});
it('_while', async function(){
await instance._while(1);
});
it('_require', async function(){
await instance._require(2);
})
it('_require_multi_line', async function(){
await instance._require_multi_line(1);
await instance._require_multi_line(3);
})
});

@ -0,0 +1,7 @@
module.exports = {
networks: {},
mocha: {},
compilers: {
solc: {}
}
}

@ -0,0 +1,11 @@
pragma solidity ^0.5.0;
contract Test {
function a(uint x) public {
require(
x == 1 ||
x == 2 ||
x == 3
);
}
}

@ -275,6 +275,25 @@ describe('Buidler Plugin: standard use cases', function() {
); );
}); });
// This works on it's own but there's some kind of weird interaction with
// the solc 6 test which causes subsequent cov measurements to be zero.
// Have tried re-ordering etc...???. Truffle tests this & should be the same anyway...
it.skip('logicalOR', async function(){
mock.installFullProject('logical-or');
mock.buidlerSetupEnv(this);
await this.env.run("coverage");
const expected = [
{
file: mock.pathToContract(buidlerConfig, 'ContractA.sol'),
pct: 59.09
}
];
verify.branchCoverage(expected);
})
it('solc 0.6.x', async function(){ it('solc 0.6.x', async function(){
mock.installFullProject('solc-6'); mock.installFullProject('solc-6');
mock.buidlerSetupEnv(this); mock.buidlerSetupEnv(this);
@ -294,4 +313,4 @@ describe('Buidler Plugin: standard use cases', function() {
verify.lineCoverage(expected); verify.lineCoverage(expected);
}) })
}) })

@ -79,6 +79,33 @@ describe('logical OR branches', () => {
}); });
}); });
// require(
// x == 1 ||
// x == 2 ||
// x == 3
// )
it('should cover a require statement with multiline OR condition (two branches)', async function() {
const contract = await util.bootstrapCoverage('or/require-multiline-or', provider, collector);
coverage.addContract(contract.instrumented, util.filePath);
await contract.instance.a(1);
await contract.instance.a(3);
const mapping = coverage.generate(contract.data, util.pathPrefix);
assert.deepEqual(mapping[util.filePath].l, {
5: 2,
});
assert.deepEqual(mapping[util.filePath].b, {
1: [2, 0], 2: [1, 0], 3: [0, 1]
});
assert.deepEqual(mapping[util.filePath].s, {
1: 2,
});
assert.deepEqual(mapping[util.filePath].f, {
1: 2,
});
});
// require(x == 1 || x == 2) // require(x == 1 || x == 2)
it('should cover a require statement with a simple OR condition (both branches)', async function() { it('should cover a require statement with a simple OR condition (both branches)', async function() {
const contract = await util.bootstrapCoverage('or/require-or', provider, collector); const contract = await util.bootstrapCoverage('or/require-or', provider, collector);

@ -433,4 +433,18 @@ describe('Truffle Plugin: standard use cases', function() {
verify.lineCoverage(expected); verify.lineCoverage(expected);
}) })
it('logicalOR', async function(){
mock.installFullProject('logical-or');
await plugin(truffleConfig);
const expected = [
{
file: mock.pathToContract(truffleConfig, 'ContractA.sol'),
pct: 59.09
}
];
verify.branchCoverage(expected);
})
}) })

@ -18,6 +18,19 @@ function lineCoverage(expected=[]){
}); });
} }
function branchCoverage(expected=[]){
let summary = JSON.parse(fs.readFileSync('coverage/coverage-summary.json'));
expected.forEach((item, idx) => {
assert(
summary[item.file].branches.pct === item.pct,
`For condition ${idx} - expected ${item.pct} %,` +
`saw - ${summary[item.file].branches.pct} %`
)
});
}
function coverageMissing(expected=[]){ function coverageMissing(expected=[]){
let summary = JSON.parse(fs.readFileSync('coverage/coverage-summary.json')); let summary = JSON.parse(fs.readFileSync('coverage/coverage-summary.json'));
expected.forEach(item => assert(summary[item.file] === undefined)) expected.forEach(item => assert(summary[item.file] === undefined))
@ -47,6 +60,7 @@ function coverageNotGenerated(config){
module.exports = { module.exports = {
pathExists: pathExists, pathExists: pathExists,
lineCoverage: lineCoverage, lineCoverage: lineCoverage,
branchCoverage: branchCoverage,
coverageMissing: coverageMissing, coverageMissing: coverageMissing,
cleanInitialState: cleanInitialState, cleanInitialState: cleanInitialState,
coverageGenerated: coverageGenerated, coverageGenerated: coverageGenerated,

Loading…
Cancel
Save