/** * 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;