Add modifier-invocation-as-branch coverage (#588)
parent
7403f3f0e1
commit
bf77884218
@ -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,45 @@ |
|||||||
|
pragma solidity ^0.6.0; |
||||||
|
|
||||||
|
import "./ModifiersB.sol"; |
||||||
|
|
||||||
|
/** |
||||||
|
* New syntaxes in solc 0.6.x |
||||||
|
*/ |
||||||
|
contract ModifiersA is ModifiersB { |
||||||
|
uint counter; |
||||||
|
bool flag = true; |
||||||
|
|
||||||
|
modifier flippable { |
||||||
|
require(flag); |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
modifier overridden() override { |
||||||
|
require(true); |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
function flip() public { |
||||||
|
flag = !flag; |
||||||
|
} |
||||||
|
|
||||||
|
function simpleSet(uint i) |
||||||
|
public |
||||||
|
override(ModifiersB) |
||||||
|
{ |
||||||
|
counter = counter + i; |
||||||
|
} |
||||||
|
|
||||||
|
function simpleView(uint i) |
||||||
|
view |
||||||
|
overridden |
||||||
|
external |
||||||
|
returns (uint, bool) |
||||||
|
{ |
||||||
|
return (counter + i, true); |
||||||
|
} |
||||||
|
|
||||||
|
function simpleSetFlip(uint i) flippable public { |
||||||
|
counter = counter + i; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
pragma solidity ^0.6.0; |
||||||
|
|
||||||
|
|
||||||
|
contract ModifiersB { |
||||||
|
uint value; |
||||||
|
uint b; |
||||||
|
|
||||||
|
constructor() public { |
||||||
|
} |
||||||
|
|
||||||
|
modifier overridden() virtual { |
||||||
|
require(true); |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
function simpleSet(uint i) public virtual { |
||||||
|
value = 5; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
pragma solidity ^0.6.0; |
||||||
|
|
||||||
|
import "./ModifiersB.sol"; |
||||||
|
|
||||||
|
/** |
||||||
|
* New syntaxes in solc 0.6.x |
||||||
|
*/ |
||||||
|
contract ModifiersC { |
||||||
|
uint counter; |
||||||
|
address owner; |
||||||
|
bool flag = true; |
||||||
|
|
||||||
|
constructor() public { |
||||||
|
owner = msg.sender; |
||||||
|
} |
||||||
|
|
||||||
|
modifier flippable { |
||||||
|
require(flag); |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
function flip() public { |
||||||
|
flag = !flag; |
||||||
|
} |
||||||
|
|
||||||
|
function simpleSetFlip(uint i) flippable public { |
||||||
|
counter = counter + i; |
||||||
|
} |
||||||
|
|
||||||
|
modifier onlyOwner { |
||||||
|
require(msg.sender == owner); |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
function set(uint i) |
||||||
|
onlyOwner |
||||||
|
public |
||||||
|
payable |
||||||
|
virtual |
||||||
|
{ |
||||||
|
counter = counter + i; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
require("@nomiclabs/hardhat-truffle5"); |
||||||
|
require(__dirname + "/../plugins/nomiclabs.plugin"); |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
solidity: { |
||||||
|
version: "0.6.7" |
||||||
|
}, |
||||||
|
logger: process.env.SILENT ? { log: () => {} } : console, |
||||||
|
}; |
@ -0,0 +1,40 @@ |
|||||||
|
const ModifiersA = artifacts.require("ModifiersA"); |
||||||
|
const ModifiersC = artifacts.require("ModifiersC"); |
||||||
|
|
||||||
|
contract("Modifiers", function(accounts) { |
||||||
|
let instance; |
||||||
|
|
||||||
|
before(async () => { |
||||||
|
A = await ModifiersA.new(); |
||||||
|
C = await ModifiersC.new(); |
||||||
|
}) |
||||||
|
|
||||||
|
it('simpleSet (overridden method)', async function(){ |
||||||
|
await A.simpleSet(5); |
||||||
|
}); |
||||||
|
|
||||||
|
it('simpleView (overridden modifier)', async function(){ |
||||||
|
await A.simpleView(5); |
||||||
|
}); |
||||||
|
|
||||||
|
it('simpleSetFlip (both branches)', async function(){ |
||||||
|
await A.simpleSetFlip(5); |
||||||
|
await A.flip(); |
||||||
|
|
||||||
|
try { |
||||||
|
await A.simpleSetFlip(5); |
||||||
|
} catch (e) { |
||||||
|
/* ignore */ |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
it('simpleSetFlip (false branch + other file)', async function(){ |
||||||
|
await C.flip(); |
||||||
|
|
||||||
|
try { |
||||||
|
await C.simpleSetFlip(5); |
||||||
|
} catch (e) { |
||||||
|
/* ignore */ |
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,9 @@ |
|||||||
|
module.exports = { |
||||||
|
networks: {}, |
||||||
|
mocha: {}, |
||||||
|
compilers: { |
||||||
|
solc: { |
||||||
|
version: "0.6.2" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
const Modified = artifacts.require('Modified'); |
||||||
|
|
||||||
|
contract('Modified', () => { |
||||||
|
it('should set counter', async function(){ |
||||||
|
const m = await Modified.new() |
||||||
|
await m.set(5); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,20 @@ |
|||||||
|
pragma solidity ^0.7.0; |
||||||
|
|
||||||
|
contract Modified { |
||||||
|
uint counter; |
||||||
|
|
||||||
|
modifier m { |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
// When modifier coverage is on, branch cov should be 50% |
||||||
|
// When off: 100% |
||||||
|
function set(uint i) |
||||||
|
m |
||||||
|
public |
||||||
|
payable |
||||||
|
virtual |
||||||
|
{ |
||||||
|
counter = counter + i; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
pragma solidity ^0.7.0; |
||||||
|
|
||||||
|
contract Test { |
||||||
|
bool flag = true; |
||||||
|
|
||||||
|
modifier m { |
||||||
|
require(flag); |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
function flip() public { |
||||||
|
flag = !flag; |
||||||
|
} |
||||||
|
|
||||||
|
function a() m public { |
||||||
|
uint x = 5; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
pragma solidity ^0.7.0; |
||||||
|
|
||||||
|
contract Test { |
||||||
|
modifier mmm { |
||||||
|
require(true); |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
modifier nnn { |
||||||
|
require(true); |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
function a() |
||||||
|
mmm |
||||||
|
nnn |
||||||
|
public |
||||||
|
{ |
||||||
|
uint x = 5; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
pragma solidity ^0.7.0; |
||||||
|
|
||||||
|
contract Test { |
||||||
|
modifier mmm { |
||||||
|
require(true); |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
function a() mmm public { |
||||||
|
uint x = 5; |
||||||
|
} |
||||||
|
|
||||||
|
function b() mmm public { |
||||||
|
uint x = 5; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
pragma solidity ^0.7.0; |
||||||
|
|
||||||
|
contract Test { |
||||||
|
modifier mmm { |
||||||
|
require(true); |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
modifier nnn { |
||||||
|
require(true); |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
function a() mmm nnn public { |
||||||
|
uint x = 5; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
pragma solidity ^0.7.0; |
||||||
|
|
||||||
|
abstract contract IM { |
||||||
|
function a() payable virtual public; |
||||||
|
} |
||||||
|
|
||||||
|
contract Test is IM { |
||||||
|
modifier m { |
||||||
|
require(true); |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
function a() payable m public override { |
||||||
|
uint x = 5; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
pragma solidity ^0.7.0; |
||||||
|
|
||||||
|
contract Test { |
||||||
|
modifier m { |
||||||
|
require(false); |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
function a() m public { |
||||||
|
uint x = 5; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
pragma solidity ^0.7.0; |
||||||
|
|
||||||
|
contract Test { |
||||||
|
modifier m { |
||||||
|
require(true); |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
function a() m public { |
||||||
|
uint x = 5; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,163 @@ |
|||||||
|
const assert = require('assert'); |
||||||
|
const util = require('./../util/util.js'); |
||||||
|
|
||||||
|
const client = require('ganache-cli'); |
||||||
|
const Coverage = require('./../../lib/coverage'); |
||||||
|
const Api = require('./../../lib/api') |
||||||
|
|
||||||
|
describe('modifiers', () => { |
||||||
|
let coverage; |
||||||
|
let api; |
||||||
|
|
||||||
|
before(async () => { |
||||||
|
api = new Api({silent: true}); |
||||||
|
await api.ganache(client); |
||||||
|
}) |
||||||
|
beforeEach(() => coverage = new Coverage()); |
||||||
|
after(async() => await api.finish()); |
||||||
|
|
||||||
|
async function setupAndRun(solidityFile){ |
||||||
|
const contract = await util.bootstrapCoverage(solidityFile, api); |
||||||
|
coverage.addContract(contract.instrumented, util.filePath); |
||||||
|
|
||||||
|
/* some modifiers intentionally fail */ |
||||||
|
try { |
||||||
|
await contract.instance.a(); |
||||||
|
} catch(e){} |
||||||
|
|
||||||
|
return coverage.generate(contract.data, util.pathPrefix); |
||||||
|
} |
||||||
|
|
||||||
|
it('should cover a modifier branch which always succeeds', async function() { |
||||||
|
const mapping = await setupAndRun('modifiers/same-contract-pass'); |
||||||
|
|
||||||
|
assert.deepEqual(mapping[util.filePath].l, { |
||||||
|
5: 1, 6: 1, 10: 1, |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].b, { |
||||||
|
1: [1, 0], 2: [1, 0] |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].s, { |
||||||
|
1: 1, 2: 1, |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].f, { |
||||||
|
1: 1, 2: 1 |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
// NB: Failures are replayed by truffle-contract
|
||||||
|
it('should cover a modifier branch which never succeeds', async function() { |
||||||
|
const mapping = await setupAndRun('modifiers/same-contract-fail'); |
||||||
|
|
||||||
|
assert.deepEqual(mapping[util.filePath].l, { |
||||||
|
5: 2, 6: 0, 10: 0, |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].b, { |
||||||
|
1: [0, 2], 2: [0, 2] |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].s, { |
||||||
|
1: 2, 2: 0, |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].f, { |
||||||
|
1: 2, 2: 0 |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should cover a modifier on an overridden function', async function() { |
||||||
|
const mapping = await setupAndRun('modifiers/override-function'); |
||||||
|
|
||||||
|
assert.deepEqual(mapping[util.filePath].l, { |
||||||
|
9: 1, 10: 1, 14: 1, |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].b, { |
||||||
|
1: [1, 0], 2: [1, 0] |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].s, { |
||||||
|
1: 1, 2: 1 |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].f, { |
||||||
|
1: 1, 2: 1 |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should cover multiple modifiers on the same function', async function() { |
||||||
|
const mapping = await setupAndRun('modifiers/multiple-mods-same-fn'); |
||||||
|
|
||||||
|
assert.deepEqual(mapping[util.filePath].l, { |
||||||
|
5: 1, 6: 1, 10: 1, 11: 1, 15: 1 |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].b, { |
||||||
|
1: [1, 0], 2: [1, 0], 3: [1, 0], 4: [1, 0] |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].s, { |
||||||
|
1: 1, 2: 1, 3: 1 |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].f, { |
||||||
|
1: 1, 2: 1, 3: 1 |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should cover multiple functions which use the same modifier', async function() { |
||||||
|
const contract = await util.bootstrapCoverage('modifiers/multiple-fns-same-mod', api); |
||||||
|
coverage.addContract(contract.instrumented, util.filePath); |
||||||
|
await contract.instance.a(); |
||||||
|
await contract.instance.b(); |
||||||
|
const mapping = coverage.generate(contract.data, util.pathPrefix); |
||||||
|
|
||||||
|
assert.deepEqual(mapping[util.filePath].l, { |
||||||
|
5: 2, 6: 2, 10: 1, 14: 1 |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].b, { |
||||||
|
1: [2, 0], 2: [1, 0], 3: [1, 0] |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].s, { |
||||||
|
1: 2, 2: 1, 3: 1, |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].f, { |
||||||
|
1: 2, 2: 1, 3: 1 |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should cover when both modifier branches are hit', async function() { |
||||||
|
const contract = await util.bootstrapCoverage('modifiers/both-branches', api); |
||||||
|
coverage.addContract(contract.instrumented, util.filePath); |
||||||
|
await contract.instance.a(); |
||||||
|
await contract.instance.flip(); |
||||||
|
|
||||||
|
try { |
||||||
|
await contract.instance.a(); |
||||||
|
} catch(e) { /*ignore*/ } |
||||||
|
|
||||||
|
const mapping = coverage.generate(contract.data, util.pathPrefix); |
||||||
|
|
||||||
|
assert.deepEqual(mapping[util.filePath].l, { |
||||||
|
7: 3, 8: 1, 12: 1, 16: 1 |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].b, { |
||||||
|
1: [1, 2], 2: [1, 2], |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].s, { |
||||||
|
1: 3, 2: 1, 3: 1, |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].f, { |
||||||
|
1: 3, 2: 1, 3: 1 |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should cover when modifiers are listed with newlines', async function() { |
||||||
|
const mapping = await setupAndRun('modifiers/listed-modifiers'); |
||||||
|
|
||||||
|
assert.deepEqual(mapping[util.filePath].l, { |
||||||
|
5: 1, 6: 1, 10: 1, 11: 1, 19: 1 |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].b, { |
||||||
|
1: [1, 0], 2: [1, 0], 3: [1, 0], 4: [1, 0] |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].s, { |
||||||
|
1: 1, 2: 1, 3: 1, |
||||||
|
}); |
||||||
|
assert.deepEqual(mapping[util.filePath].f, { |
||||||
|
1: 1, 2: 1, 3: 1 |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
Loading…
Reference in new issue