Code coverage for Solidity testing
—
The core library contains the fundamental classes that power solidity-coverage's instrumentation, data collection, and reporting capabilities. These classes work together to provide comprehensive coverage analysis for Solidity smart contracts.
The core instrumentation engine that modifies Solidity source code to inject coverage tracking calls.
/**
* Main instrumentation class that modifies Solidity source code
* Injects coverage tracking calls while preserving code functionality
*/
const Instrumenter = require('solidity-coverage/lib/instrumenter');
class Instrumenter {
/**
* Creates a new instrumenter instance
* @param config - Configuration options for instrumentation behavior
*/
constructor(config?: {
viaIR?: boolean; // Enable Solidity viaIR compilation mode
modifierWhitelist?: string[]; // Specific modifiers to measure
measureStatementCoverage?: boolean; // Enable statement coverage measurement
measureFunctionCoverage?: boolean; // Enable function coverage measurement
measureModifierCoverage?: boolean; // Enable modifier coverage measurement
measureBranchCoverage?: boolean; // Enable branch coverage measurement
measureLineCoverage?: boolean; // Enable line coverage measurement
});
/**
* Instruments Solidity source code for coverage tracking
* @param source - Solidity source code to instrument
* @param canonicalPath - Absolute path to the source file
* @returns Instrumentation result with modified source and mapping data
*/
instrument(source: string, canonicalPath: string): InstrumentationResult;
}
interface InstrumentationResult {
contract: string; // Instrumented Solidity source code
runnableLines: number[]; // Array of line numbers that can be executed
fnMap: object; // Function mapping data for coverage
branchMap: object; // Branch mapping data for coverage
statementMap: object; // Statement mapping data for coverage
}Usage Examples:
const Instrumenter = require('solidity-coverage/lib/instrumenter');
// Create instrumenter with custom configuration
const instrumenter = new Instrumenter({
measureStatementCoverage: true,
measureFunctionCoverage: true,
measureBranchCoverage: true,
viaIR: false
});
// Instrument contract source
const source = `
contract Token {
uint256 public totalSupply;
function transfer(address to, uint256 amount) public returns (bool) {
if (balances[msg.sender] >= amount) {
balances[msg.sender] -= amount;
balances[to] += amount;
return true;
}
return false;
}
}`;
const result = instrumenter.instrument(source, '/contracts/Token.sol');
console.log('Instrumented contract:', result.contract);
console.log('Runnable lines:', result.runnableLines);Manages coverage data collection and generates Istanbul-compatible coverage reports.
/**
* Coverage data management and report generation class
* Converts execution data into standardized coverage reports
*/
const Coverage = require('solidity-coverage/lib/coverage');
class Coverage {
/**
* Creates a new coverage instance
*/
constructor();
/**
* Adds contract instrumentation data to coverage tracking
* @param info - Instrumentation data from Instrumenter.instrument()
* @param contractPath - Canonical path to the contract file
*/
addContract(info: InstrumentationResult, contractPath: string): void;
/**
* Generates final coverage data from collected execution traces
* @param instrumentationData - Hit data collected during test execution
*/
generate(instrumentationData: object): void;
}Usage Examples:
const Coverage = require('solidity-coverage/lib/coverage');
// Create coverage instance
const coverage = new Coverage();
// Add instrumented contract data
coverage.addContract(instrumentationResult, '/contracts/Token.sol');
// Generate coverage report after tests complete
coverage.generate(executionData);Hooks into EVM execution to collect coverage hit data during test runs.
/**
* EVM execution hook for collecting coverage data
* Tracks which lines, functions, and branches are executed
*/
const DataCollector = require('solidity-coverage/lib/collector');
class DataCollector {
/**
* Creates a new data collector instance
* @param instrumentationData - Hit map from instrumentation (optional)
* @param viaIR - Whether using viaIR compilation mode (optional)
*/
constructor(instrumentationData?: object, viaIR?: boolean);
/**
* EVM step callback that processes each instruction
* @param info - EVM execution step information
*/
step(info: EVMStepInfo): void;
}
interface EVMStepInfo {
pc: number; // Program counter
opcode: { // Current opcode information
name: string; // Opcode name (e.g., 'SSTORE', 'CALL')
};
stack: any[]; // EVM stack state
}Usage Examples:
const DataCollector = require('solidity-coverage/lib/collector');
// Create data collector with instrumentation data
const collector = new DataCollector(instrumentationData, false);
// Hook into EVM execution (typically done by the API)
provider._node._vm.evm.events.on('step', collector.step.bind(collector));Validates configuration objects to ensure proper setup.
/**
* Configuration validation utility
* Ensures coverage configuration is valid and complete
*/
const ConfigValidator = require('solidity-coverage/lib/validator');
class ConfigValidator {
/**
* Creates a new validator instance
*/
constructor();
/**
* Validates a coverage configuration object
* @param config - Configuration object to validate
* @returns True if configuration is valid
* @throws Error with descriptive message if invalid
*/
validate(config: object): boolean;
}Valid Configuration Properties:
The validator accepts these configuration properties:
client, cwd, host, port, abiOutputPath, matrixOutputPathmatrixReporterPath, providerOptions, silent, autoLaunchServeristanbulFolder, measureStatementCoverage, measureFunctionCoveragemeasureModifierCoverage, measureLineCoverage, measureBranchCoverageonServerReady, onCompileComplete, onTestComplete, onIstanbulCompleteskipFiles, istanbulReporter, modifierWhitelistUsage Examples:
const ConfigValidator = require('solidity-coverage/lib/validator');
const validator = new ConfigValidator();
const config = {
skipFiles: ['contracts/mocks/'],
istanbulReporter: ['html', 'lcov'],
measureStatementCoverage: true,
measureFunctionCoverage: true
};
try {
const isValid = validator.validate(config);
console.log('Configuration is valid:', isValid);
} catch (error) {
console.error('Invalid configuration:', error.message);
}Utilities for working with contract ABI data and generating human-readable reports.
/**
* ABI analysis and comparison utilities
* Generates human-readable ABI information and diffs
*/
const AbiUtils = require('solidity-coverage/lib/abi');
class AbiUtils {
/**
* Compares two ABI objects and generates a unified diff
* @param original - Original ABI object (optional)
* @param current - Current ABI object (optional)
* @returns Diff result with statistics and unified diff lines
*/
diff(original?: object, current?: object): AbiDiffResult;
/**
* Converts contract ABI to human-readable function signatures
* @param contract - Contract object with ABI property
* @returns Array of human-readable function signature strings
*/
toHumanReadableFunctions(contract: {abi: any[]}): string[];
}
interface AbiDiffResult {
plus: number; // Number of additions
minus: number; // Number of deletions
unifiedDiff: string[]; // Array of diff lines
}Usage Examples:
const AbiUtils = require('solidity-coverage/lib/abi');
const abiUtils = new AbiUtils();
// Generate human-readable function signatures
const contract = {
abi: [
{
"name": "transfer",
"type": "function",
"inputs": [
{"name": "to", "type": "address"},
{"name": "amount", "type": "uint256"}
],
"outputs": [{"type": "bool"}]
}
]
};
const signatures = abiUtils.toHumanReadableFunctions(contract);
console.log('Function signatures:', signatures);
// Output: ['function transfer(address to, uint256 amount) returns (bool)']
// Compare ABI changes
const oldAbi = { /* previous ABI */ };
const newAbi = { /* current ABI */ };
const diff = abiUtils.diff(oldAbi, newAbi);
console.log(`ABI changes: +${diff.plus} -${diff.minus}`);User interface classes for different output formats and environments.
/**
* Base UI class for coverage output formatting
*/
const UI = require('solidity-coverage/lib/ui');
class UI {
/**
* Creates a new UI instance
* @param log - Optional logging function
*/
constructor(log?: (message: string) => void);
/**
* Reports a message using the specified kind and arguments
* @param kind - Message type/template identifier
* @param args - Arguments for message formatting (optional)
*/
report(kind: string, args?: string[]): void;
/**
* Generates a formatted message string
* @param kind - Message type/template identifier
* @param args - Arguments for message formatting (optional)
* @returns Formatted message string
*/
generate(kind: string, args?: string[]): string;
}
/**
* Application-specific UI class with enhanced formatting
*/
class AppUI extends UI {
constructor(log?: (message: string) => void);
report(kind: string, args?: string[]): void;
generate(kind: string, args?: string[]): string;
}
/**
* Plugin-specific UI class for Hardhat integration
*/
class PluginUI extends UI {
constructor(log?: (message: string) => void);
flags: {
testfiles: string; // Test files flag description
testMatrix: string; // Test matrix flag description
abi: string; // ABI flag description
solcoverjs: string; // Config file flag description
temp: string; // Temp directory flag description
};
}Usage Examples:
const { UI, AppUI, PluginUI } = require('solidity-coverage/lib/ui');
// Basic UI usage
const ui = new UI(console.log);
ui.report('coverage-starts');
// Plugin UI with command flags
const pluginUI = new PluginUI();
console.log('Available flags:', pluginUI.flags);const API = require('solidity-coverage/api');
const Instrumenter = require('solidity-coverage/lib/instrumenter');
const Coverage = require('solidity-coverage/lib/coverage');
const DataCollector = require('solidity-coverage/lib/collector');
async function runCoverageAnalysis() {
// Initialize components
const instrumenter = new Instrumenter({ measureStatementCoverage: true });
const coverage = new Coverage();
// Instrument contracts
const targets = [{ source: contractSource, canonicalPath: '/contracts/Token.sol' }];
const instrumented = targets.map(target => ({
...target,
...instrumenter.instrument(target.source, target.canonicalPath)
}));
// Add contracts to coverage tracking
instrumented.forEach(target => {
coverage.addContract(target, target.canonicalPath);
});
// Set up data collection
const collector = new DataCollector(api.getInstrumentationData());
provider._node._vm.evm.events.on('step', collector.step.bind(collector));
// Run tests (instrumented contracts will be executed)
// ... test execution ...
// Generate coverage reports
coverage.generate(collector.data);
}This comprehensive core library documentation covers all the fundamental classes that developers may need to access when building custom coverage solutions or integrating with solidity-coverage at a lower level than the high-level API.
Install with Tessl CLI
npx tessl i tessl/npm-solidity-coverage