From 5228fe7054553da7766d90c30420e9aeb3450816 Mon Sep 17 00:00:00 2001 From: Adria Massanet Date: Wed, 12 Apr 2017 12:45:52 +0200 Subject: [PATCH 1/6] Exit process at end (travis got stuck if not) --- runCoveredTests.js | 1 + 1 file changed, 1 insertion(+) diff --git a/runCoveredTests.js b/runCoveredTests.js index 71c9302..19d1ecb 100644 --- a/runCoveredTests.js +++ b/runCoveredTests.js @@ -59,3 +59,4 @@ shell.exec('./node_modules/istanbul/lib/cli.js report lcov'); testrpcProcess.kill(); shell.rm('-rf', './../contracts'); shell.mv('./../originalContracts', './../contracts'); +process.exit(0); From da24a90e0d44808661fb820733493b0450e5c5de Mon Sep 17 00:00:00 2001 From: Adria Massanet Date: Wed, 12 Apr 2017 17:05:03 +0200 Subject: [PATCH 2/6] Allows contract inheritance --- coverageMap.js | 37 ++++++++++++++++++++++++++++--------- injector.js | 18 +++++++++--------- instrumentSolidity.js | 6 ++++++ 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/coverageMap.js b/coverageMap.js index d17a0f3..f1c6b8b 100644 --- a/coverageMap.js +++ b/coverageMap.js @@ -5,11 +5,7 @@ */ const SolidityCoder = require('web3/lib/solidity/coder.js'); const path = require('path'); - -const lineTopic = 'b8995a65f405d9756b41a334f38d8ff0c93c4934e170d3c1429c3e7ca101014d'; -const functionTopic = 'd4ce765fd23c5cc3660249353d61ecd18ca60549dd62cb9ca350a4244de7b87f'; -const branchTopic = 'd4cf56ed5ba572684f02f889f12ac42d9583c8e3097802060e949bfbb3c1bff5'; -const statementTopic = 'b51abbff580b3a34bbc725f2dc6f736e9d4b45a41293fd0084ad865a31fde0c8'; +const keccak = require('keccakjs') /** * Converts solcover event data into an object that can be @@ -20,6 +16,10 @@ module.exports = class CoverageMap { constructor() { this.coverage = {}; + this.lineTopics = [] + this.functionTopics = [] + this.branchTopics = [] + this.statementTopics = [] } /** @@ -29,7 +29,9 @@ module.exports = class CoverageMap { * @param {String} canonicalContractPath target file location * @return {Object} coverage map with all values set to zero */ + addContract(info, canonicalContractPath) { + this.coverage[canonicalContractPath] = { l: {}, path: canonicalContractPath, @@ -56,6 +58,18 @@ module.exports = class CoverageMap { for (let x = 1; x <= Object.keys(info.statementMap).length; x++) { this.coverage[canonicalContractPath].s[x] = 0; } + + const keccakhex = (x => { + var hash = new keccak(256) + hash.update(x) + return hash.digest('hex') + }); + + this.lineTopics.push(keccakhex("__Coverage"+info.contractName+"(string,uint256)")); + this.functionTopics.push(keccakhex("__FunctionCoverage"+info.contractName+"(string,uint256)")); + this.branchTopics.push(keccakhex("__BranchCoverage"+info.contractName+"(string,uint256,uint256)")); + this.statementTopics.push(keccakhex("__StatementCoverage"+info.contractName+"(string,uint256)")); + } /** @@ -68,22 +82,27 @@ module.exports = class CoverageMap { generate(events, pathPrefix) { for (let idx = 0; idx < events.length; idx++) { const event = JSON.parse(events[idx]); - if (event.topics.indexOf(lineTopic) >= 0) { + + if (event.topics.filter( t => this.lineTopics.indexOf(t) >= 0 ).length > 0) { const data = SolidityCoder.decodeParams(['string', 'uint256'], event.data.replace('0x', '')); const canonicalContractPath = data[0]; this.coverage[canonicalContractPath].l[data[1].toNumber()] += 1; - } else if (event.topics.indexOf(functionTopic) >= 0) { + + } else if (event.topics.filter( t => this.functionTopics.indexOf(t) >= 0 ).length > 0) { const data = SolidityCoder.decodeParams(['string', 'uint256'], event.data.replace('0x', '')); const canonicalContractPath = data[0]; this.coverage[canonicalContractPath].f[data[1].toNumber()] += 1; - } else if (event.topics.indexOf(branchTopic) >= 0) { + + } else if (event.topics.filter( t => this.branchTopics.indexOf(t) >= 0 ).length > 0) { const data = SolidityCoder.decodeParams(['string', 'uint256', 'uint256'], event.data.replace('0x', '')); const canonicalContractPath = data[0]; this.coverage[canonicalContractPath].b[data[1].toNumber()][data[2].toNumber()] += 1; - } else if (event.topics.indexOf(statementTopic) >= 0) { + + } else if (event.topics.filter( t => this.statementTopics.indexOf(t) >= 0 ).length > 0) { const data = SolidityCoder.decodeParams(['string', 'uint256'], event.data.replace('0x', '')); const canonicalContractPath = data[0]; this.coverage[canonicalContractPath].s[data[1].toNumber()] += 1; + } } return Object.assign({}, this.coverage); diff --git a/injector.js b/injector.js index c3dd7a5..169e114 100644 --- a/injector.js +++ b/injector.js @@ -6,27 +6,27 @@ injector.callEvent = function injectCallEvent(contract, fileName, injectionPoint const linecount = (contract.instrumented.slice(0, injectionPoint).match(/\n/g) || []).length + 1; contract.runnableLines.push(linecount); contract.instrumented = contract.instrumented.slice(0, injectionPoint) + - 'Coverage(\'' + fileName + '\',' + linecount + ');\n' + + '__Coverage'+contract.contractName+'(\'' + fileName + '\',' + linecount + ');\n' + contract.instrumented.slice(injectionPoint); }; injector.callFunctionEvent = function injectCallFunctionEvent(contract, fileName, injectionPoint, injection) { contract.instrumented = contract.instrumented.slice(0, injectionPoint) + - 'FunctionCoverage(\'' + fileName + '\',' + injection.fnId + ');\n' + + '__FunctionCoverage'+contract.contractName+'(\'' + fileName + '\',' + injection.fnId + ');\n' + contract.instrumented.slice(injectionPoint); }; injector.callBranchEvent = function injectCallFunctionEvent(contract, fileName, injectionPoint, injection) { contract.instrumented = contract.instrumented.slice(0, injectionPoint) + (injection.openBracket ? '{' : '') + - 'BranchCoverage(\'' + fileName + '\',' + injection.branchId + ',' + injection.locationIdx + ')' + + '__BranchCoverage'+contract.contractName+'(\'' + fileName + '\',' + injection.branchId + ',' + injection.locationIdx + ')' + (injection.comma ? ',' : ';') + contract.instrumented.slice(injectionPoint); }; injector.callEmptyBranchEvent = function injectCallEmptyBranchEvent(contract, fileName, injectionPoint, injection) { contract.instrumented = contract.instrumented.slice(0, injectionPoint) + - 'else { BranchCoverage(\'' + fileName + '\',' + injection.branchId + ',' + injection.locationIdx + ');}\n' + + 'else { __BranchCoverage'+contract.contractName+'(\'' + fileName + '\',' + injection.branchId + ',' + injection.locationIdx + ');}\n' + contract.instrumented.slice(injectionPoint); }; @@ -44,16 +44,16 @@ injector.literal = function injectLiteral(contract, fileName, injectionPoint, in injector.statement = function injectStatement(contract, fileName, injectionPoint, injection) { contract.instrumented = contract.instrumented.slice(0, injectionPoint) + - ' StatementCoverage(\'' + fileName + '\',' + injection.statementId + ');\n' + + ' __StatementCoverage'+contract.contractName+'(\'' + fileName + '\',' + injection.statementId + ');\n' + contract.instrumented.slice(injectionPoint); }; injector.eventDefinition = function injectEventDefinition(contract, fileName, injectionPoint, injection) { contract.instrumented = contract.instrumented.slice(0, injectionPoint) + - 'event Coverage(string fileName, uint256 lineNumber);\n' + - 'event FunctionCoverage(string fileName, uint256 fnId);\n' + - 'event StatementCoverage(string fileName, uint256 statementId);\n' + - 'event BranchCoverage(string fileName, uint256 branchId, uint256 locationIdx);\n' + + 'event __Coverage'+contract.contractName+'(string fileName, uint256 lineNumber);\n' + + 'event __FunctionCoverage'+contract.contractName+'(string fileName, uint256 fnId);\n' + + 'event __StatementCoverage'+contract.contractName+'(string fileName, uint256 statementId);\n' + + 'event __BranchCoverage'+contract.contractName+'(string fileName, uint256 branchId, uint256 locationIdx);\n' + contract.instrumented.slice(injectionPoint); }; diff --git a/instrumentSolidity.js b/instrumentSolidity.js index 7837a1b..2d75844 100644 --- a/instrumentSolidity.js +++ b/instrumentSolidity.js @@ -38,8 +38,12 @@ module.exports = function instrumentSolidity(contractSource, fileName) { contract.preprocessed = preprocessor.run(contract.source); contract.instrumented = contract.preprocessed; + ast = SolidityParser.parse(contract.preprocessed); + const contractStatement = ast['body'].filter( node => { return node.type == 'ContractStatement' }); + contract.contractName = contractStatement[0].name + parse[ast.type](contract, ast); // var result = solparse.parse(contract); @@ -59,5 +63,7 @@ module.exports = function instrumentSolidity(contractSource, fileName) { }); retValue.runnableLines = contract.runnableLines; retValue.contract = contract.instrumented; + retValue.contractName = contractStatement[0].name + return retValue; }; From 128dd31c6ff34a129c36d8e6ef0eb61580dc0df6 Mon Sep 17 00:00:00 2001 From: Adria Massanet Date: Wed, 12 Apr 2017 17:13:10 +0200 Subject: [PATCH 3/6] Added keccakjs dependency --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 25c3156..1917c5c 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "dependencies": { "ethereumjs-testrpc": "https://github.com/ethereumjs/testrpc.git#5ba3c45b2ca306ab589f4a4c649d9afc39042680", "istanbul": "^0.4.5", + "keccakjs": "^0.2.1", "mkdirp": "^0.5.1", "shelljs": "^0.7.4", "sol-explore": "^1.6.2", From fbfd049f06dff4ba9955334bccb35d0205193a75 Mon Sep 17 00:00:00 2001 From: Adria Massanet Date: Wed, 12 Apr 2017 22:32:09 +0200 Subject: [PATCH 4/6] eslint-ed --- coverageMap.js | 38 ++++++++++++++++---------------------- injector.js | 18 +++++++++--------- instrumentSolidity.js | 6 +++--- 3 files changed, 28 insertions(+), 34 deletions(-) diff --git a/coverageMap.js b/coverageMap.js index f1c6b8b..d2880b2 100644 --- a/coverageMap.js +++ b/coverageMap.js @@ -5,7 +5,7 @@ */ const SolidityCoder = require('web3/lib/solidity/coder.js'); const path = require('path'); -const keccak = require('keccakjs') +const keccak = require('keccakjs'); /** * Converts solcover event data into an object that can be @@ -16,10 +16,10 @@ module.exports = class CoverageMap { constructor() { this.coverage = {}; - this.lineTopics = [] - this.functionTopics = [] - this.branchTopics = [] - this.statementTopics = [] + this.lineTopics = []; + this.functionTopics = []; + this.branchTopics = []; + this.statementTopics = []; } /** @@ -31,7 +31,6 @@ module.exports = class CoverageMap { */ addContract(info, canonicalContractPath) { - this.coverage[canonicalContractPath] = { l: {}, path: canonicalContractPath, @@ -60,16 +59,15 @@ module.exports = class CoverageMap { } const keccakhex = (x => { - var hash = new keccak(256) - hash.update(x) - return hash.digest('hex') + const hash = new keccak(256); // eslint-disable-line new-cap + hash.update(x); + return hash.digest('hex'); }); - this.lineTopics.push(keccakhex("__Coverage"+info.contractName+"(string,uint256)")); - this.functionTopics.push(keccakhex("__FunctionCoverage"+info.contractName+"(string,uint256)")); - this.branchTopics.push(keccakhex("__BranchCoverage"+info.contractName+"(string,uint256,uint256)")); - this.statementTopics.push(keccakhex("__StatementCoverage"+info.contractName+"(string,uint256)")); - + this.lineTopics.push(keccakhex('__Coverage' + info.contractName + '(string,uint256)')); + this.functionTopics.push(keccakhex('__FunctionCoverage' + info.contractName + '(string,uint256)')); + this.branchTopics.push(keccakhex('__BranchCoverage' + info.contractName + '(string,uint256,uint256)')); + this.statementTopics.push(keccakhex('__StatementCoverage' + info.contractName + '(string,uint256)')); } /** @@ -83,26 +81,22 @@ module.exports = class CoverageMap { for (let idx = 0; idx < events.length; idx++) { const event = JSON.parse(events[idx]); - if (event.topics.filter( t => this.lineTopics.indexOf(t) >= 0 ).length > 0) { + if (event.topics.filter(t => this.lineTopics.indexOf(t) >= 0).length > 0) { const data = SolidityCoder.decodeParams(['string', 'uint256'], event.data.replace('0x', '')); const canonicalContractPath = data[0]; this.coverage[canonicalContractPath].l[data[1].toNumber()] += 1; - - } else if (event.topics.filter( t => this.functionTopics.indexOf(t) >= 0 ).length > 0) { + } else if (event.topics.filter(t => this.functionTopics.indexOf(t) >= 0).length > 0) { const data = SolidityCoder.decodeParams(['string', 'uint256'], event.data.replace('0x', '')); const canonicalContractPath = data[0]; this.coverage[canonicalContractPath].f[data[1].toNumber()] += 1; - - } else if (event.topics.filter( t => this.branchTopics.indexOf(t) >= 0 ).length > 0) { + } else if (event.topics.filter(t => this.branchTopics.indexOf(t) >= 0).length > 0) { const data = SolidityCoder.decodeParams(['string', 'uint256', 'uint256'], event.data.replace('0x', '')); const canonicalContractPath = data[0]; this.coverage[canonicalContractPath].b[data[1].toNumber()][data[2].toNumber()] += 1; - - } else if (event.topics.filter( t => this.statementTopics.indexOf(t) >= 0 ).length > 0) { + } else if (event.topics.filter(t => this.statementTopics.indexOf(t) >= 0).length > 0) { const data = SolidityCoder.decodeParams(['string', 'uint256'], event.data.replace('0x', '')); const canonicalContractPath = data[0]; this.coverage[canonicalContractPath].s[data[1].toNumber()] += 1; - } } return Object.assign({}, this.coverage); diff --git a/injector.js b/injector.js index 169e114..d3f687d 100644 --- a/injector.js +++ b/injector.js @@ -6,27 +6,27 @@ injector.callEvent = function injectCallEvent(contract, fileName, injectionPoint const linecount = (contract.instrumented.slice(0, injectionPoint).match(/\n/g) || []).length + 1; contract.runnableLines.push(linecount); contract.instrumented = contract.instrumented.slice(0, injectionPoint) + - '__Coverage'+contract.contractName+'(\'' + fileName + '\',' + linecount + ');\n' + + '__Coverage' + contract.contractName + '(\'' + fileName + '\',' + linecount + ');\n' + contract.instrumented.slice(injectionPoint); }; injector.callFunctionEvent = function injectCallFunctionEvent(contract, fileName, injectionPoint, injection) { contract.instrumented = contract.instrumented.slice(0, injectionPoint) + - '__FunctionCoverage'+contract.contractName+'(\'' + fileName + '\',' + injection.fnId + ');\n' + + '__FunctionCoverage' + contract.contractName + '(\'' + fileName + '\',' + injection.fnId + ');\n' + contract.instrumented.slice(injectionPoint); }; injector.callBranchEvent = function injectCallFunctionEvent(contract, fileName, injectionPoint, injection) { contract.instrumented = contract.instrumented.slice(0, injectionPoint) + (injection.openBracket ? '{' : '') + - '__BranchCoverage'+contract.contractName+'(\'' + fileName + '\',' + injection.branchId + ',' + injection.locationIdx + ')' + + '__BranchCoverage' + contract.contractName + '(\'' + fileName + '\',' + injection.branchId + ',' + injection.locationIdx + ')' + (injection.comma ? ',' : ';') + contract.instrumented.slice(injectionPoint); }; injector.callEmptyBranchEvent = function injectCallEmptyBranchEvent(contract, fileName, injectionPoint, injection) { contract.instrumented = contract.instrumented.slice(0, injectionPoint) + - 'else { __BranchCoverage'+contract.contractName+'(\'' + fileName + '\',' + injection.branchId + ',' + injection.locationIdx + ');}\n' + + 'else { __BranchCoverage' + contract.contractName + '(\'' + fileName + '\',' + injection.branchId + ',' + injection.locationIdx + ');}\n' + contract.instrumented.slice(injectionPoint); }; @@ -44,16 +44,16 @@ injector.literal = function injectLiteral(contract, fileName, injectionPoint, in injector.statement = function injectStatement(contract, fileName, injectionPoint, injection) { contract.instrumented = contract.instrumented.slice(0, injectionPoint) + - ' __StatementCoverage'+contract.contractName+'(\'' + fileName + '\',' + injection.statementId + ');\n' + + ' __StatementCoverage' + contract.contractName + '(\'' + fileName + '\',' + injection.statementId + ');\n' + contract.instrumented.slice(injectionPoint); }; injector.eventDefinition = function injectEventDefinition(contract, fileName, injectionPoint, injection) { contract.instrumented = contract.instrumented.slice(0, injectionPoint) + - 'event __Coverage'+contract.contractName+'(string fileName, uint256 lineNumber);\n' + - 'event __FunctionCoverage'+contract.contractName+'(string fileName, uint256 fnId);\n' + - 'event __StatementCoverage'+contract.contractName+'(string fileName, uint256 statementId);\n' + - 'event __BranchCoverage'+contract.contractName+'(string fileName, uint256 branchId, uint256 locationIdx);\n' + + 'event __Coverage' + contract.contractName + '(string fileName, uint256 lineNumber);\n' + + 'event __FunctionCoverage' + contract.contractName + '(string fileName, uint256 fnId);\n' + + 'event __StatementCoverage' + contract.contractName + '(string fileName, uint256 statementId);\n' + + 'event __BranchCoverage' + contract.contractName + '(string fileName, uint256 branchId, uint256 locationIdx);\n' + contract.instrumented.slice(injectionPoint); }; diff --git a/instrumentSolidity.js b/instrumentSolidity.js index 2d75844..f8cdd6f 100644 --- a/instrumentSolidity.js +++ b/instrumentSolidity.js @@ -41,8 +41,8 @@ module.exports = function instrumentSolidity(contractSource, fileName) { ast = SolidityParser.parse(contract.preprocessed); - const contractStatement = ast['body'].filter( node => { return node.type == 'ContractStatement' }); - contract.contractName = contractStatement[0].name + const contractStatement = ast.body.filter(node => node.type === 'ContractStatement'); + contract.contractName = contractStatement[0].name; parse[ast.type](contract, ast); @@ -63,7 +63,7 @@ module.exports = function instrumentSolidity(contractSource, fileName) { }); retValue.runnableLines = contract.runnableLines; retValue.contract = contract.instrumented; - retValue.contractName = contractStatement[0].name + retValue.contractName = contractStatement[0].name; return retValue; }; From 2004589037f57e3440cabc250213f9ea29cbbd8a Mon Sep 17 00:00:00 2001 From: Adria Massanet Date: Wed, 12 Apr 2017 22:37:34 +0200 Subject: [PATCH 5/6] Updated solc to 0.4.10 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1917c5c..396a4d4 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,6 @@ "istanbul": "^0.4.5", "merkle-patricia-tree": "~2.1.2", "mocha": "^3.1.0", - "solc": "0.4.6" + "solc": "0.4.10" } } From 7012cda82ee19535ff87d6e6faeb65261b79487a Mon Sep 17 00:00:00 2001 From: Adria Massanet Date: Wed, 12 Apr 2017 22:49:07 +0200 Subject: [PATCH 6/6] reverted version to solc 0.4.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 396a4d4..310b335 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,6 @@ "istanbul": "^0.4.5", "merkle-patricia-tree": "~2.1.2", "mocha": "^3.1.0", - "solc": "0.4.10" + "solc": "0.4.8" } }