diff --git a/.gitignore b/.gitignore index 70336c6..50c9a4e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ coverage/ node_modules/ .changelog .DS_Store +package-lock.json diff --git a/lib/coverageMap.js b/lib/coverageMap.js index a3cec78..89d994c 100644 --- a/lib/coverageMap.js +++ b/lib/coverageMap.js @@ -87,7 +87,7 @@ module.exports = class CoverageMap { this.assertPreTopics.push(assertPreHash); this.assertPostTopics.push(assertPostHash); - const topics = `${lineHash}\n${fnHash}\n${branchHash}\n${statementHash}\n${assertPreHash}\n${assertPostHash}`; + const topics = `${lineHash}\n${fnHash}\n${branchHash}\n${statementHash}\n${assertPreHash}\n${assertPostHash}\n`; fs.appendFileSync('./scTopics', topics); } diff --git a/package.json b/package.json index bd744ad..92564fc 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "eslint-plugin-import": "^2.2.0", "eslint-plugin-mocha": "^4.8.0", "ethereumjs-account": "~2.0.4", + "ethereumjs-vm": "https://github.com/sc-forks/ethereumjs-vm-sc.git#3.0.3", "ethereumjs-tx": "^1.2.2", "ethereumjs-util": "^5.0.1", "istanbul": "^0.4.5", diff --git a/test/assert.js b/test/assert.js index e0c21bd..04e38a7 100644 --- a/test/assert.js +++ b/test/assert.js @@ -11,9 +11,7 @@ describe('asserts and requires', () => { const filePath = path.resolve('./test.sol'); const pathPrefix = './'; - before(() => process.env.NO_EVENTS_FILTER = true); - - it.only('should cover assert statements as if they are if statements when they pass', done => { + it('should cover assert statements as if they are if statements when they pass', done => { const contract = util.getCode('assert/Assert.sol'); const info = getInstrumentedVersion(contract, filePath); const coverage = new CoverageMap(); @@ -37,7 +35,7 @@ describe('asserts and requires', () => { }).catch(done); }); - it.only('should cover assert statements as if they are if statements when they fail', done => { + it('should cover assert statements as if they are if statements when they fail', done => { const contract = util.getCode('assert/Assert.sol'); const info = getInstrumentedVersion(contract, filePath); const coverage = new CoverageMap(); @@ -60,4 +58,53 @@ describe('asserts and requires', () => { done(); }).catch(done); }); + + it('should cover multi-line require statements as if they are if statements when they pass', done => { + const contract = util.getCode('assert/RequireMultiline.sol'); + const info = getInstrumentedVersion(contract, filePath); + const coverage = new CoverageMap(); + coverage.addContract(info, filePath); + + vm.execute(info.contract, 'a', [true, true, true]).then(events => { + const mapping = coverage.generate(events, pathPrefix); + assert.deepEqual(mapping[filePath].l, { + 5: 1, + }); + assert.deepEqual(mapping[filePath].b, { + 1: [1, 0], + }); + assert.deepEqual(mapping[filePath].s, { + 1: 1, + }); + assert.deepEqual(mapping[filePath].f, { + 1: 1, + }); + done(); + }).catch(done); + }); + + it('should cover multi-line require statements as if they are if statements when they fail', done => { + const contract = util.getCode('assert/RequireMultiline.sol'); + const info = getInstrumentedVersion(contract, filePath); + const coverage = new CoverageMap(); + coverage.addContract(info, filePath); + + vm.execute(info.contract, 'a', [true, true, false]).then(events => { + const mapping = coverage.generate(events, pathPrefix); + assert.deepEqual(mapping[filePath].l, { + 5: 1, + }); + assert.deepEqual(mapping[filePath].b, { + 1: [0, 1], + }); + assert.deepEqual(mapping[filePath].s, { + 1: 1, + }); + assert.deepEqual(mapping[filePath].f, { + 1: 1, + }); + done(); + }).catch(done); + }); + }); diff --git a/test/conditional.js b/test/conditional.js index 17e848e..582385f 100644 --- a/test/conditional.js +++ b/test/conditional.js @@ -11,8 +11,6 @@ describe('conditional statements', () => { const filePath = path.resolve('./test.sol'); const pathPrefix = './'; - before(() => process.env.NO_EVENTS_FILTER = true); - it('should cover a conditional that reaches the consequent (same-line)', done => { const contract = util.getCode('conditional/sameline-consequent.sol'); const info = getInstrumentedVersion(contract, filePath); diff --git a/test/expressions.js b/test/expressions.js index 0ce9290..fd3558b 100644 --- a/test/expressions.js +++ b/test/expressions.js @@ -13,8 +13,6 @@ const path = require('path'); describe('generic expressions', () => { const filePath = path.resolve('./test.sol'); - before(() => process.env.NO_EVENTS_FILTER = true); - it('should compile after instrumenting a single binary expression', () => { const contract = util.getCode('expressions/single-binary-expression.sol'); const info = getInstrumentedVersion(contract, filePath); diff --git a/test/function.js b/test/function.js index 70340f1..2deb735 100644 --- a/test/function.js +++ b/test/function.js @@ -17,8 +17,6 @@ describe('function declarations', () => { const filePath = path.resolve('./test.sol'); const pathPrefix = './'; - before(() => process.env.NO_EVENTS_FILTER = true); - it('should compile after instrumenting an ordinary function declaration', () => { const contract = util.getCode('function/function.sol'); const info = getInstrumentedVersion(contract, 'test.sol'); @@ -101,15 +99,15 @@ describe('function declarations', () => { vm.execute(info.contract, 'a', []).then(events => { const mapping = coverage.generate(events, pathPrefix); assert.deepEqual(mapping[filePath].l, { - 9: 0, + 9: 1, }); assert.deepEqual(mapping[filePath].b, {}); assert.deepEqual(mapping[filePath].s, { - 1: 0, + 1: 1, }); assert.deepEqual(mapping[filePath].f, { 1: 0, - 2: 0, + 2: 1, }); done(); }).catch(done); @@ -125,15 +123,15 @@ describe('function declarations', () => { vm.execute(info.contract, 'a', []).then(events => { const mapping = coverage.generate(events, pathPrefix); assert.deepEqual(mapping[filePath].l, { - 10: 0, + 10: 1, }); assert.deepEqual(mapping[filePath].b, {}); assert.deepEqual(mapping[filePath].s, { - 1: 0, + 1: 1, }); assert.deepEqual(mapping[filePath].f, { 1: 0, - 2: 0, + 2: 1, }); done(); }).catch(done); diff --git a/test/if.js b/test/if.js index 7bbf5d6..4c8fd82 100644 --- a/test/if.js +++ b/test/if.js @@ -11,8 +11,6 @@ describe('if, else, and else if statements', () => { const filePath = path.resolve('./test.sol'); const pathPrefix = './'; - before(() => process.env.NO_EVENTS_FILTER = true); - it('should cover an if statement with a bracketed consequent', done => { const contract = util.getCode('if/if-with-brackets.sol'); const info = getInstrumentedVersion(contract, filePath); diff --git a/test/loops.js b/test/loops.js index 64a1961..6cba5e5 100644 --- a/test/loops.js +++ b/test/loops.js @@ -11,8 +11,6 @@ describe('for and while statements', () => { const filePath = path.resolve('./test.sol'); const pathPrefix = './'; - before(() => process.env.NO_EVENTS_FILTER = true); - it('should cover a for statement with a bracketed body (multiline)', done => { const contract = util.getCode('loops/for-with-brackets.sol'); const info = getInstrumentedVersion(contract, filePath); diff --git a/test/return.js b/test/return.js index 299706a..04bfd48 100644 --- a/test/return.js +++ b/test/return.js @@ -5,8 +5,7 @@ const getInstrumentedVersion = require('./../lib/instrumentSolidity.js'); const util = require('./util/util.js'); describe('return statements', () => { - before(() => process.env.NO_EVENTS_FILTER = true); - + it('should compile after instrumenting function that returns true', () => { const contract = util.getCode('return/return.sol'); const info = getInstrumentedVersion(contract, 'test.sol'); diff --git a/test/sources/assert/RequireMultiline.sol b/test/sources/assert/RequireMultiline.sol new file mode 100644 index 0000000..b45bb2e --- /dev/null +++ b/test/sources/assert/RequireMultiline.sol @@ -0,0 +1,9 @@ +pragma solidity ^0.4.13; + +contract Test { + function a(bool a, bool b, bool c){ + require(a && + b && + c); + } +} \ No newline at end of file diff --git a/test/sources/cli/Events.sol b/test/sources/cli/Events.sol index f8cdbde..8a80ca4 100644 --- a/test/sources/cli/Events.sol +++ b/test/sources/cli/Events.sol @@ -2,13 +2,26 @@ pragma solidity ^0.4.3; contract Events { uint x = 0; + bool a; + bool b; event LogEventOne( uint x, address y); event LogEventTwo( uint x, address y); function test(uint val) { + // Assert / Require events + require(true); + + // Contract Events LogEventOne(100, msg.sender); x = x + val; LogEventTwo(200, msg.sender); + + // Branch events + if (true) { + a = false; + } else { + b = false; + } } function getX() returns (uint){ diff --git a/test/statements.js b/test/statements.js index 6e222d2..f2277f3 100644 --- a/test/statements.js +++ b/test/statements.js @@ -17,8 +17,6 @@ describe('generic statements', () => { const filePath = path.resolve('./test.sol'); const pathPrefix = './'; - before(() => process.env.NO_EVENTS_FILTER = true); - it('should compile after instrumenting a single statement (first line of function)', () => { const contract = util.getCode('statements/single.sol'); const info = getInstrumentedVersion(contract, filePath); diff --git a/test/util/util.js b/test/util/util.js index d7b7c9c..f17b162 100644 --- a/test/util/util.js +++ b/test/util/util.js @@ -11,7 +11,8 @@ module.exports.getCode = function getCode(_path) { }; module.exports.report = function report(errors) { - if (errors) { + + if (errors && errors.indexOf('Warning:') !== -1) { throw new Error(`Instrumented solidity invalid: ${errors}`); } }; diff --git a/test/util/vm.js b/test/util/vm.js index 0ebbe16..2bcabbd 100644 --- a/test/util/vm.js +++ b/test/util/vm.js @@ -1,4 +1,6 @@ const solc = require('solc'); +const shell = require('shelljs'); +const fs = require('fs'); const VM = require('ethereumjs-vm'); const Account = require('ethereumjs-account'); const Transaction = require('ethereumjs-tx'); @@ -116,15 +118,14 @@ function callMethod(vm, abi, address, functionName, args) { vm.runTx({ tx, }, (err, results) => { - const seenEvents = []; - results.vm.runState.logs.forEach(log => { - const toWrite = {}; - toWrite.address = log[0].toString('hex'); - toWrite.topics = log[1].map(x => x.toString('hex')); - toWrite.data = log[2].toString('hex'); - seenEvents.push(JSON.stringify(toWrite)); - }); - resolve(seenEvents); + try { + const events = fs.readFileSync('./allFiredEvents').toString().split('\n'); + events.pop(); + shell.rm('./allFiredEvents'); + resolve(events); + } catch (err) { + resolve([]); + } }); }); } diff --git a/test/zeppelin.js b/test/zeppelin.js index 834607c..957f49d 100644 --- a/test/zeppelin.js +++ b/test/zeppelin.js @@ -5,8 +5,7 @@ const getInstrumentedVersion = require('./../lib/instrumentSolidity.js'); const util = require('./util/util.js'); describe('Battery test of production contracts: OpenZeppelin', () => { - before(() => process.env.NO_EVENTS_FILTER = true); - + it('should compile after instrumenting zeppelin-solidity/Bounty.sol', () => { const bounty = getInstrumentedVersion(util.getCode('zeppelin/Bounty.sol'), 'Bounty.sol'); const ownable = getInstrumentedVersion(util.getCode('zeppelin/Ownable.sol'), 'Ownable.sol');