diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..88ae067 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +allFiredEvents +coverage.json +coverage/ +node_modules/ diff --git a/hookIntoEvents.patch b/hookIntoEvents.patch new file mode 100644 index 0000000..c2c31a3 --- /dev/null +++ b/hookIntoEvents.patch @@ -0,0 +1,23 @@ +--- opFns.js.orig 2016-09-27 10:00:02.000000000 +0100 ++++ opFns.js 2016-09-27 10:00:59.000000000 +0100 +@@ -6,6 +6,7 @@ + const logTable = require('./logTable.js') + const ERROR = constants.ERROR + const MAX_INT = 9007199254740991 ++const fs = require('fs'); + + // the opcode functions + module.exports = { +@@ -507,6 +508,12 @@ + + // add data + log.push(mem) ++ //Added to allow tracking of instrumentation even for .call() and throw ++ let toWrite = {}; ++ toWrite.address= log[0].toString('hex'); ++ toWrite.topics = log[1].map(function(x){return x.toString('hex')}) ++ toWrite.data = log[2].toString('hex'); ++ fs.appendFileSync('./allFiredEvents', JSON.stringify(toWrite) + '\n') + runState.logs.push(log) + }, + diff --git a/package.json b/package.json new file mode 100644 index 0000000..00e34ad --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "truffle-coverage", + "version": "1.0.0", + "description": "", + "main": "instrumentSolidity.js", + "directories": { + "test": "test" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "ethereumjs-testrpc": "^2.2.7" + } +} diff --git a/runCoveredTests.js b/runCoveredTests.js index 67b9bb1..2e84287 100644 --- a/runCoveredTests.js +++ b/runCoveredTests.js @@ -8,18 +8,27 @@ var fs = require('fs'); var path = require('path'); var getInstrumentedVersion = require('./instrumentSolidity.js'); +var childprocess = require('child_process'); -shell.mv('./contracts/', './originalContracts'); -shell.mkdir('./contracts/'); +//PAtch our local testrpc if necessary +if (!shell.test('-e','./node_modules/ethereumjs-vm/lib/opFns.js.orig')){ + console.log('patch local testrpc...') + shell.exec('patch -b ./node_modules/ethereumjs-vm/lib/opFns.js ./hookIntoEvents.patch') +} +//Run the modified testrpc with large block limit +var testrpcProcess = childprocess.exec('./node_modules/ethereumjs-testrpc/bin/testrpc --gasLimit 0xfffffffffff') + +shell.mv('./../contracts/', './../originalContracts'); +shell.mkdir('./../contracts/'); //For each contract in originalContracts, get the instrumented version -shell.ls('./originalContracts/*.sol').forEach(function(file) { +shell.ls('./../originalContracts/*.sol').forEach(function(file) { if (file !== 'originalContracts/Migrations.sol') { console.log("=================") console.log(file); console.log("=================") var instrumentedContractInfo = getInstrumentedVersion(file, true); - fs.writeFileSync('./contracts/' + path.basename(file), instrumentedContractInfo.contract); - var canonicalContractPath = path.resolve('./originalContracts/' + path.basename(file)); + fs.writeFileSync('./../contracts/' + path.basename(file), instrumentedContractInfo.contract); + var canonicalContractPath = path.resolve('./../originalContracts/' + path.basename(file)); coverage[canonicalContractPath] = { "l": {}, "path": canonicalContractPath, "s": {}, "b": {}, "f": {}, "fnMap": {}, "statementMap": {}, "branchMap": {} }; for (idx in instrumentedContractInfo.runnableLines) { coverage[canonicalContractPath]["l"][instrumentedContractInfo.runnableLines[idx]] = 0; @@ -38,52 +47,38 @@ shell.ls('./originalContracts/*.sol').forEach(function(file) { } } }); -shell.cp("./originalContracts/Migrations.sol", "./contracts/Migrations.sol"); -var filter = web3.eth.filter('latest'); -var res = web3.currentProvider.send({ - jsonrpc: '2.0', - method: 'eth_newFilter', - params: [{ "fromBlock": "0x0", "toBlock": "latest" }], - id: new Date().getTime() -}); -var filterid = res.result; -shell.exec("truffle test"); +shell.cp("./../originalContracts/Migrations.sol", "./../contracts/Migrations.sol"); +shell.rm('./allFiredEvents'); +shell.exec('truffle test'); -var res = web3.currentProvider.send({ - jsonrpc: '2.0', - method: 'eth_getFilterChanges', - params: [filterid], - id: new Date().getTime() -}); - -events = res.result; -for (idx in res.result) { - var event = res.result[idx]; - if (event.topics.indexOf("0xb8995a65f405d9756b41a334f38d8ff0c93c4934e170d3c1429c3e7ca101014d") >= 0) { +events = fs.readFileSync('./allFiredEvents').toString().split('\n') +for (idx==0; idx < events.length-1; idx++){ + //The limit here isn't a bug - there is an empty line at the end of this file, so we don't + //want to go to the very end of the array. + var event = JSON.parse(events[idx]); + if (event.topics.indexOf("b8995a65f405d9756b41a334f38d8ff0c93c4934e170d3c1429c3e7ca101014d") >= 0) { var data = SolidityCoder.decodeParams(["string", "uint256"], event.data.replace("0x", "")); - var canonicalContractPath = path.resolve('./originalContracts/' + path.basename(data[0])); + var canonicalContractPath = path.resolve('./../originalContracts/' + path.basename(data[0])); coverage[canonicalContractPath]["l"][data[1].toNumber()] += 1; - }else if(event.topics.indexOf("0xd4ce765fd23c5cc3660249353d61ecd18ca60549dd62cb9ca350a4244de7b87f")>=0){ + }else if(event.topics.indexOf("d4ce765fd23c5cc3660249353d61ecd18ca60549dd62cb9ca350a4244de7b87f")>=0){ var data = SolidityCoder.decodeParams(["string", "uint256"], event.data.replace("0x", "")); - var canonicalContractPath = path.resolve('./originalContracts/' + path.basename(data[0])); + var canonicalContractPath = path.resolve('./../originalContracts/' + path.basename(data[0])); coverage[canonicalContractPath]["f"][data[1].toNumber()] += 1; - }else if(event.topics.indexOf("0xd4cf56ed5ba572684f02f889f12ac42d9583c8e3097802060e949bfbb3c1bff5")>=0){ + }else if(event.topics.indexOf("d4cf56ed5ba572684f02f889f12ac42d9583c8e3097802060e949bfbb3c1bff5")>=0){ var data = SolidityCoder.decodeParams(["string", "uint256", "uint256"], event.data.replace("0x", "")); - var canonicalContractPath = path.resolve('./originalContracts/' + path.basename(data[0])); + var canonicalContractPath = path.resolve('./../originalContracts/' + path.basename(data[0])); coverage[canonicalContractPath]["b"][data[1].toNumber()][data[2].toNumber()] += 1; - }else if(event.topics.indexOf("0xb51abbff580b3a34bbc725f2dc6f736e9d4b45a41293fd0084ad865a31fde0c8")>=0){ + }else if(event.topics.indexOf("b51abbff580b3a34bbc725f2dc6f736e9d4b45a41293fd0084ad865a31fde0c8")>=0){ var data = SolidityCoder.decodeParams(["string","uint256"], event.data.replace("0x", "")); - var canonicalContractPath = path.resolve('./originalContracts/' + path.basename(data[0])); + var canonicalContractPath = path.resolve('./../originalContracts/' + path.basename(data[0])); coverage[canonicalContractPath]["s"][data[1].toNumber()]+= 1; } - } fs.writeFileSync('./coverage.json', JSON.stringify(coverage)); shell.exec("istanbul report html") - -// shell.rm('-rf', './contracts'); -// shell.rm('-rf', './canonicalContracts'); -// shell.mv('./originalContracts', './contracts'); +testrpcProcess.kill(); +shell.rm('-rf', './../contracts'); +shell.mv('./../originalContracts', './../contracts');