Code coverage for Solidity smart-contracts
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
solidity-coverage/lib/coverage.js

116 lines
3.6 KiB

/**
* Converts instrumentation data accumulated a the vm steps to an instanbul spec coverage object.
* @type {Coverage}
*/
const util = require('util');
class Coverage {
constructor() {
this.data = {};
this.assertData = {};
this.lineTopics = [];
this.functionTopics = [];
this.branchTopics = [];
this.statementTopics = [];
this.assertPreTopics = [];
this.assertPostTopics = [];
}
/**
* Initializes a coverage map object for contract
* + instrumented per `info`
* + located at `canonicalContractPath`
* @param {Object} info `info = getIntrumentedVersion(contract, fileName, true)`
* @param {String} canonicalContractPath path to contract file
* @return {Object} coverage map with all values set to zero
*/
addContract(info, canonicalContractPath) {
this.data[canonicalContractPath] = {
l: {},
path: canonicalContractPath,
s: {},
b: {},
f: {},
fnMap: {},
statementMap: {},
branchMap: {},
};
this.assertData[canonicalContractPath] = { };
info.runnableLines.forEach((item, idx) => {
this.data[canonicalContractPath].l[info.runnableLines[idx]] = 0;
});
this.data[canonicalContractPath].fnMap = info.fnMap;
for (let x = 1; x <= Object.keys(info.fnMap).length; x++) {
this.data[canonicalContractPath].f[x] = 0;
}
this.data[canonicalContractPath].branchMap = info.branchMap;
for (let x = 1; x <= Object.keys(info.branchMap).length; x++) {
this.data[canonicalContractPath].b[x] = [0, 0];
this.assertData[canonicalContractPath][x] = {
preEvents: 0,
postEvents: 0,
};
}
this.data[canonicalContractPath].statementMap = info.statementMap;
for (let x = 1; x <= Object.keys(info.statementMap).length; x++) {
this.data[canonicalContractPath].s[x] = 0;
}
}
/**
* Populates an empty coverage map with values derived from a hash map of
* data collected as the instrumented contracts are tested
* @param {Object} map of collected instrumentation data
* @return {Object} coverage map.
*/
generate(collectedData) {
const hashes = Object.keys(collectedData);
for (let hash of hashes){
const data = collectedData[hash];
const contractPath = collectedData[hash].contractPath;
const id = collectedData[hash].id;
const hits = collectedData[hash].hits;
switch(collectedData[hash].type){
case 'line': this.data[contractPath].l[id] = hits; break;
case 'function': this.data[contractPath].f[id] = hits; break;
case 'statement': this.data[contractPath].s[id] = hits; break;
case 'branch': this.data[contractPath].b[id][data.locationIdx] = hits; break;
case 'assertPre': this.assertData[contractPath][id].preEvents = hits; break;
case 'assertPost': this.assertData[contractPath][id].postEvents = hits; break;
}
}
// Finally, interpret the assert pre/post events
const contractPaths = Object.keys(this.assertData);
for (let contractPath of contractPaths){
const contract = this.data[contractPath];
for (let i = 1; i <= Object.keys(contract.b).length; i++) {
const branch = this.assertData[contractPath][i];
// Was it an assert branch?
if (branch && branch.preEvents > 0){
this.data[contractPath].b[i] = [
branch.postEvents,
branch.preEvents - branch.postEvents
]
}
}
}
return Object.assign({}, this.data);
}
};
module.exports = Coverage;