Skip to content

Commit a29d712

Browse files
committed
Add logicalOR branch highlighting for html report
1 parent 0763bd6 commit a29d712

File tree

10 files changed

+189
-7
lines changed

10 files changed

+189
-7
lines changed

lib/registrar.js

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -182,29 +182,50 @@ class Registrar {
182182
* @param {Object} expression AST node
183183
*/
184184
addNewLogicalORBranch(contract, expression) {
185-
const startContract = contract.instrumented.slice(0, expression.range[0]);
186-
const startline = ( startContract.match(/\n/g) || [] ).length + 1;
185+
let start;
186+
187+
// Instabul HTML highlighting location data...
188+
const leftZero = expression.left.range[0];
189+
const leftOne = expression.left.range[1];
190+
const rightZero = expression.right.range[0];
191+
const rightOne = expression.right.range[1];
192+
193+
start = contract.instrumented.slice(0, leftZero);
194+
const leftStartLine = ( start.match(/\n/g) || [] ).length + 1;
195+
const leftStartCol = leftZero - start.lastIndexOf('\n') - 1;
196+
197+
start = contract.instrumented.slice(0, leftOne);
198+
const leftEndLine = ( start.match(/\n/g) || [] ).length + 1;
199+
const leftEndCol = leftOne - start.lastIndexOf('\n') - 1;
200+
201+
start = contract.instrumented.slice(0, rightZero);
202+
const rightStartLine = ( start.match(/\n/g) || [] ).length + 1;
203+
const rightStartCol = rightZero - start.lastIndexOf('\n') - 1;
204+
205+
start = contract.instrumented.slice(0, rightOne);
206+
const rightEndLine = ( start.match(/\n/g) || [] ).length + 1;
207+
const rightEndCol = rightOne - start.lastIndexOf('\n') - 1;
187208

188209
contract.branchId += 1;
189210

190211
// NB locations for if branches in istanbul are zero
191212
// length and associated with the start of the if.
192213
contract.branchMap[contract.branchId] = {
193-
line: startline,
214+
line: leftStartLine,
194215
type: 'cond-expr',
195216
locations: [{
196217
start: {
197-
line: startline, column: expression.left.range[0],
218+
line: leftStartLine, column: leftStartCol,
198219
},
199220
end: {
200-
line: startline, column: expression.left.range[1],
221+
line: leftEndLine, column: leftEndCol,
201222
},
202223
}, {
203224
start: {
204-
line: startline, column: expression.right.range[0],
225+
line: rightStartLine, column: rightStartCol,
205226
},
206227
end: {
207-
line: startline, column: expression.right.range[1],
228+
line: rightEndLine, column: rightEndCol,
208229
},
209230
}],
210231
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Testing hooks
2+
const fn = (msg, config) => config.logger.log(msg);
3+
4+
module.exports = {
5+
skipFiles: ['Migrations.sol'],
6+
silent: process.env.SILENT ? true : false,
7+
istanbulReporter: ['json-summary', 'text'],
8+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const { loadPluginFile } = require("@nomiclabs/buidler/plugins-testing");
2+
loadPluginFile(__dirname + "/../plugins/buidler.plugin");
3+
usePlugin("@nomiclabs/buidler-truffle5");
4+
5+
module.exports={
6+
defaultNetwork: "buidlerevm"
7+
};
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
pragma solidity ^0.5.0;
2+
3+
4+
contract ContractA {
5+
6+
function _if(uint i) public pure {
7+
if (i == 0 || i > 5){
8+
/* ignore */
9+
}
10+
}
11+
12+
function _if_and(uint i) public pure {
13+
if (i != 0 && (i < 2 || i > 5)){
14+
/* ignore */
15+
}
16+
}
17+
18+
function _return(uint i) public pure returns (bool){
19+
return (i != 0 && i != 1 ) ||
20+
((i + 1) == 2);
21+
}
22+
23+
function _while(uint i) public pure returns (bool){
24+
uint counter;
25+
while( (i == 1 || i == 2) && counter < 2 ){
26+
counter++;
27+
}
28+
}
29+
30+
function _require(uint x) public {
31+
require(x == 1 || x == 2);
32+
}
33+
34+
function _require_multi_line(uint x) public {
35+
require(
36+
(x == 1 || x == 2) ||
37+
x == 3
38+
);
39+
}
40+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const ContractA = artifacts.require("ContractA");
2+
3+
contract("contracta", function(accounts) {
4+
let instance;
5+
6+
before(async () => instance = await ContractA.new())
7+
8+
it('_if', async function(){
9+
await instance._if(0);
10+
await instance._if(7);
11+
});
12+
13+
it('_if_and', async function(){
14+
await instance._if_and(1);
15+
});
16+
17+
it('_return', async function(){
18+
await instance._return(4);
19+
});
20+
21+
it('_while', async function(){
22+
await instance._while(1);
23+
});
24+
25+
it('_require', async function(){
26+
await instance._require(2);
27+
})
28+
29+
it('_require_multi_line', async function(){
30+
await instance._require_multi_line(1);
31+
await instance._require_multi_line(3);
32+
})
33+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module.exports = {
2+
networks: {},
3+
mocha: {},
4+
compilers: {
5+
solc: {}
6+
}
7+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
pragma solidity ^0.5.0;
2+
3+
contract Test {
4+
function a(uint x) public {
5+
require(
6+
x == 1 ||
7+
x == 2 ||
8+
x == 3
9+
);
10+
}
11+
}

test/units/or.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,33 @@ describe('logical OR branches', () => {
7979
});
8080
});
8181

82+
// require(
83+
// x == 1 ||
84+
// x == 2 ||
85+
// x == 3
86+
// )
87+
it('should cover a require statement with multiline OR condition (two branches)', async function() {
88+
const contract = await util.bootstrapCoverage('or/require-multiline-or', provider, collector);
89+
coverage.addContract(contract.instrumented, util.filePath);
90+
await contract.instance.a(1);
91+
await contract.instance.a(3);
92+
93+
const mapping = coverage.generate(contract.data, util.pathPrefix);
94+
95+
assert.deepEqual(mapping[util.filePath].l, {
96+
5: 2,
97+
});
98+
assert.deepEqual(mapping[util.filePath].b, {
99+
1: [2, 0], 2: [1, 0], 3: [0, 1]
100+
});
101+
assert.deepEqual(mapping[util.filePath].s, {
102+
1: 2,
103+
});
104+
assert.deepEqual(mapping[util.filePath].f, {
105+
1: 2,
106+
});
107+
});
108+
82109
// require(x == 1 || x == 2)
83110
it('should cover a require statement with a simple OR condition (both branches)', async function() {
84111
const contract = await util.bootstrapCoverage('or/require-or', provider, collector);

test/units/truffle/standard.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,4 +433,18 @@ describe('Truffle Plugin: standard use cases', function() {
433433

434434
verify.lineCoverage(expected);
435435
})
436+
437+
it('logicalOR', async function(){
438+
mock.installFullProject('logical-or');
439+
await plugin(truffleConfig);
440+
441+
const expected = [
442+
{
443+
file: mock.pathToContract(truffleConfig, 'ContractA.sol'),
444+
pct: 59.09
445+
}
446+
];
447+
448+
verify.branchCoverage(expected);
449+
})
436450
})

test/util/verifiers.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@ function lineCoverage(expected=[]){
1818
});
1919
}
2020

21+
function branchCoverage(expected=[]){
22+
let summary = JSON.parse(fs.readFileSync('coverage/coverage-summary.json'));
23+
24+
expected.forEach((item, idx) => {
25+
assert(
26+
summary[item.file].branches.pct === item.pct,
27+
28+
`For condition ${idx} - expected ${item.pct} %,` +
29+
`saw - ${summary[item.file].branches.pct} %`
30+
)
31+
});
32+
}
33+
2134
function coverageMissing(expected=[]){
2235
let summary = JSON.parse(fs.readFileSync('coverage/coverage-summary.json'));
2336
expected.forEach(item => assert(summary[item.file] === undefined))
@@ -47,6 +60,7 @@ function coverageNotGenerated(config){
4760
module.exports = {
4861
pathExists: pathExists,
4962
lineCoverage: lineCoverage,
63+
branchCoverage: branchCoverage,
5064
coverageMissing: coverageMissing,
5165
cleanInitialState: cleanInitialState,
5266
coverageGenerated: coverageGenerated,

0 commit comments

Comments
 (0)