diff options
Diffstat (limited to 'packages/sol-cov/src')
-rw-r--r-- | packages/sol-cov/src/collect_contract_data.ts | 27 | ||||
-rw-r--r-- | packages/sol-cov/src/coverage_manager.ts | 63 | ||||
-rw-r--r-- | packages/sol-cov/src/coverage_subprovider.ts | 10 | ||||
-rw-r--r-- | packages/sol-cov/src/utils.ts | 4 |
4 files changed, 54 insertions, 50 deletions
diff --git a/packages/sol-cov/src/collect_contract_data.ts b/packages/sol-cov/src/collect_contract_data.ts index 1d8bc7178..2c2a12835 100644 --- a/packages/sol-cov/src/collect_contract_data.ts +++ b/packages/sol-cov/src/collect_contract_data.ts @@ -5,28 +5,27 @@ import * as path from 'path'; import { ContractData } from './types'; -export const collectContractsData = (artifactsPath: string, sourcesPath: string, networkId: number) => { +export const collectContractsData = (artifactsPath: string, sourcesPath: string) => { const artifactsGlob = `${artifactsPath}/**/*.json`; const artifactFileNames = glob.sync(artifactsGlob, { absolute: true }); - const contractsDataIfExists: Array<ContractData | {}> = _.map(artifactFileNames, artifactFileName => { + const contractsData: ContractData[] = []; + _.forEach(artifactFileNames, artifactFileName => { const artifact = JSON.parse(fs.readFileSync(artifactFileName).toString()); - const sources = artifact.networks[networkId].sources; - const contractName = artifact.contract_name; + const sources = _.keys(artifact.sources); + const contractName = artifact.contractName; // We don't compute coverage for dependencies - const sourceCodes = _.map(sources, (source: string) => fs.readFileSync(source).toString()); - if (_.isUndefined(artifact.networks[networkId])) { - throw new Error(`No ${contractName} artifacts found for networkId ${networkId}`); - } + const sourceCodes = _.map(sources, (source: string) => + fs.readFileSync(path.join(sourcesPath, source)).toString(), + ); const contractData = { sourceCodes, sources, - sourceMap: artifact.networks[networkId].source_map, - sourceMapRuntime: artifact.networks[networkId].source_map_runtime, - runtimeBytecode: artifact.networks[networkId].runtime_bytecode, - bytecode: artifact.networks[networkId].bytecode, + bytecode: artifact.compilerOutput.evm.bytecode.object, + sourceMap: artifact.compilerOutput.evm.bytecode.sourceMap, + runtimeBytecode: artifact.compilerOutput.evm.deployedBytecode.object, + sourceMapRuntime: artifact.compilerOutput.evm.deployedBytecode.sourceMap, }; - return contractData; + contractsData.push(contractData); }); - const contractsData = _.filter(contractsDataIfExists, contractData => !_.isEmpty(contractData)) as ContractData[]; return contractsData; }; diff --git a/packages/sol-cov/src/coverage_manager.ts b/packages/sol-cov/src/coverage_manager.ts index 230ccc3c9..800ca96dd 100644 --- a/packages/sol-cov/src/coverage_manager.ts +++ b/packages/sol-cov/src/coverage_manager.ts @@ -1,6 +1,9 @@ +import { promisify } from '@0xproject/utils'; +import { addHexPrefix } from 'ethereumjs-util'; import * as fs from 'fs'; import { Collector } from 'istanbul'; import * as _ from 'lodash'; +import * as mkdirp from 'mkdirp'; import * as path from 'path'; import { collectContractsData } from './collect_contract_data'; @@ -28,11 +31,32 @@ import { } from './types'; import { utils } from './utils'; +const mkdirpAsync = promisify<undefined>(mkdirp); + export class CoverageManager { + private _sourcesPath: string; private _traceInfos: TraceInfo[] = []; private _contractsData: ContractData[] = []; private _getContractCodeAsync: (address: string) => Promise<string>; - private static _getSingleFileCoverageForTrace( + constructor( + artifactsPath: string, + sourcesPath: string, + getContractCodeAsync: (address: string) => Promise<string>, + ) { + this._getContractCodeAsync = getContractCodeAsync; + this._sourcesPath = sourcesPath; + this._contractsData = collectContractsData(artifactsPath, this._sourcesPath); + } + public appendTraceInfo(traceInfo: TraceInfo): void { + this._traceInfos.push(traceInfo); + } + public async writeCoverageAsync(): Promise<void> { + const finalCoverage = await this._computeCoverageAsync(); + const stringifiedCoverage = JSON.stringify(finalCoverage, null, '\t'); + await mkdirpAsync('coverage'); + fs.writeFileSync('coverage/coverage.json', stringifiedCoverage); + } + private _getSingleFileCoverageForTrace( contractData: ContractData, coveredPcs: number[], pcToSourceRange: { [programCounter: number]: SourceRange }, @@ -94,11 +118,12 @@ export class CoverageManager { ); statementCoverage[modifierStatementId] = isModifierCovered; } + const absoluteFileName = path.join(this._sourcesPath, fileName); const partialCoverage = { - [contractData.sources[fileIndex]]: { + [absoluteFileName]: { ...coverageEntriesDescription, l: {}, // It's able to derive it from statement coverage - path: fileName, + path: absoluteFileName, f: functionCoverage, s: statementCoverage, b: branchCoverage, @@ -106,31 +131,13 @@ export class CoverageManager { }; return partialCoverage; } - constructor( - artifactsPath: string, - sourcesPath: string, - networkId: number, - getContractCodeAsync: (address: string) => Promise<string>, - ) { - this._getContractCodeAsync = getContractCodeAsync; - this._contractsData = collectContractsData(artifactsPath, sourcesPath, networkId); - } - public appendTraceInfo(traceInfo: TraceInfo): void { - this._traceInfos.push(traceInfo); - } - public async writeCoverageAsync(): Promise<void> { - const finalCoverage = await this._computeCoverageAsync(); - const jsonReplacer: null = null; - const numberOfJsonSpaces = 4; - const stringifiedCoverage = JSON.stringify(finalCoverage, jsonReplacer, numberOfJsonSpaces); - fs.writeFileSync('coverage/coverage.json', stringifiedCoverage); - } private async _computeCoverageAsync(): Promise<Coverage> { const collector = new Collector(); for (const traceInfo of this._traceInfos) { if (traceInfo.address !== constants.NEW_CONTRACT) { // Runtime transaction - const runtimeBytecode = (traceInfo as TraceInfoExistingContract).runtimeBytecode; + let runtimeBytecode = (traceInfo as TraceInfoExistingContract).runtimeBytecode; + runtimeBytecode = addHexPrefix(runtimeBytecode); const contractData = _.find(this._contractsData, { runtimeBytecode }) as ContractData; if (_.isUndefined(contractData)) { throw new Error(`Transaction to an unknown address: ${traceInfo.address}`); @@ -144,7 +151,7 @@ export class CoverageManager { contractData.sources, ); for (let fileIndex = 0; fileIndex < contractData.sources.length; fileIndex++) { - const singleFileCoverageForTrace = CoverageManager._getSingleFileCoverageForTrace( + const singleFileCoverageForTrace = this._getSingleFileCoverageForTrace( contractData, traceInfo.coveredPcs, pcToSourceRange, @@ -154,7 +161,8 @@ export class CoverageManager { } } else { // Contract creation transaction - const bytecode = (traceInfo as TraceInfoNewContract).bytecode; + let bytecode = (traceInfo as TraceInfoNewContract).bytecode; + bytecode = addHexPrefix(bytecode); const contractData = _.find(this._contractsData, contractDataCandidate => bytecode.startsWith(contractDataCandidate.bytecode), ) as ContractData; @@ -170,7 +178,7 @@ export class CoverageManager { contractData.sources, ); for (let fileIndex = 0; fileIndex < contractData.sources.length; fileIndex++) { - const singleFileCoverageForTrace = CoverageManager._getSingleFileCoverageForTrace( + const singleFileCoverageForTrace = this._getSingleFileCoverageForTrace( contractData, traceInfo.coveredPcs, pcToSourceRange, @@ -180,7 +188,6 @@ export class CoverageManager { } } } - // TODO: Remove any cast as soon as https://github.com/DefinitelyTyped/DefinitelyTyped/pull/24233 gets merged - return (collector as any).getFinalCoverage(); + return collector.getFinalCoverage(); } } diff --git a/packages/sol-cov/src/coverage_subprovider.ts b/packages/sol-cov/src/coverage_subprovider.ts index 6504d5a46..b08291afb 100644 --- a/packages/sol-cov/src/coverage_subprovider.ts +++ b/packages/sol-cov/src/coverage_subprovider.ts @@ -28,19 +28,13 @@ export class CoverageSubprovider extends Subprovider { * Instantiates a CoverageSubprovider instance * @param artifactsPath Path to the smart contract artifacts * @param sourcesPath Path to the smart contract source files - * @param networkId network id * @param defaultFromAddress default from address to use when sending transactions */ - constructor(artifactsPath: string, sourcesPath: string, networkId: number, defaultFromAddress: string) { + constructor(artifactsPath: string, sourcesPath: string, defaultFromAddress: string) { super(); this._lock = new Lock(); this._defaultFromAddress = defaultFromAddress; - this._coverageManager = new CoverageManager( - artifactsPath, - sourcesPath, - networkId, - this._getContractCodeAsync.bind(this), - ); + this._coverageManager = new CoverageManager(artifactsPath, sourcesPath, this._getContractCodeAsync.bind(this)); } /** * Write the test coverage results to a file in Istanbul format. diff --git a/packages/sol-cov/src/utils.ts b/packages/sol-cov/src/utils.ts index f155043a1..d970c42ee 100644 --- a/packages/sol-cov/src/utils.ts +++ b/packages/sol-cov/src/utils.ts @@ -4,6 +4,10 @@ export const utils = { compareLineColumn(lhs: LineColumn, rhs: LineColumn): number { return lhs.line !== rhs.line ? lhs.line - rhs.line : lhs.column - rhs.column; }, + removeHexPrefix(hex: string): string { + const hexPrefix = '0x'; + return hex.startsWith(hexPrefix) ? hex.slice(hexPrefix.length) : hex; + }, isRangeInside(childRange: SingleFileSourceRange, parentRange: SingleFileSourceRange): boolean { return ( utils.compareLineColumn(parentRange.start, childRange.start) <= 0 && |