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/plugins/resources/nomiclabs.utils.js

275 lines
7.3 KiB

const shell = require('shelljs');
const globby = require('globby');
const pluginUtils = require("./plugin.utils");
const path = require('path');
const DataCollector = require("./../../lib/collector")
const semver = require("semver")
const util = require('util')
// =============================
// Nomiclabs Plugin Utils
// =============================
/**
* Returns a list of test files to pass to mocha.
* @param {String} files file or glob
* @return {String[]} list of files to pass to mocha
*/
function getTestFilePaths(files){
const target = globby.sync([files])
// Hardhat supports js & ts
const testregex = /.*\.(js|ts)$/;
return target.filter(f => f.match(testregex) != null);
}
/**
* Normalizes Hardhat paths / logging for use by the plugin utilities and
* attaches them to the config
* @param {HardhatConfig} config
* @return {HardhatConfig} updated config
*/
function normalizeConfig(config, args={}){
let sources;
(args.sources)
? sources = path.join(config.paths.sources, args.sources)
: sources = config.paths.sources;
if (!path.isAbsolute(sources)) {
sources = path.join(config.paths.root, sources);
}
if (config.solidity && config.solidity.compilers.length) {
config.viaIR = isUsingViaIR(config.solidity);
config.usingSolcV4 = isUsingSolcV4(config.solidity);
}
config.workingDir = config.paths.root;
config.contractsDir = sources;
config.testDir = config.paths.tests;
config.artifactsDir = config.paths.artifacts;
config.logger = config.logger ? config.logger : {log: null};
config.solcoverjs = args.solcoverjs
config.gasReporter = { enabled: false }
config.matrix = args.matrix;
try {
const hardhatPackage = require('hardhat/package.json');
if (semver.gt(hardhatPackage.version, '2.0.3')){
config.useHardhatDefaultPaths = true;
}
} catch(e){ /* ignore */ }
return config;
}
function isUsingSolcV4(solidity) {
for (compiler of solidity.compilers) {
if (compiler.version && semver.lt(compiler.version, '0.5.0')) {
return true;
}
}
if (solidity.overrides) {
for (key of Object.keys(solidity.overrides)){
if (solidity.overrides[key].version && semver.lt(solidity.overrides[key].version, '0.5.0')) {
return true;
}
}
}
return false;
}
function isUsingViaIR(solidity) {
for (compiler of solidity.compilers) {
if (compiler.settings && compiler.settings.viaIR) {
return true;
}
}
if (solidity.overrides) {
for (key of Object.keys(solidity.overrides)){
if (solidity.overrides[key].settings && solidity.overrides[key].settings.viaIR) {
return true;
}
}
}
return false;
}
async function setupHardhatNetwork(env, api, ui){
const hardhatPackage = require('hardhat/package.json');
const { createProvider } = require("hardhat/internal/core/providers/construction");
const { HARDHAT_NETWORK_NAME } = require("hardhat/plugins")
// after 2.15.0, the internal createProvider function has a different signature
const newCreateProviderSignature = semver.satisfies(hardhatPackage.version, "^2.15.0");
let provider, networkConfig;
// HardhatEVM
networkConfig = env.network.config;
configureHardhatEVMGas(networkConfig, api);
if (newCreateProviderSignature) {
provider = await createProvider(
env.config,
HARDHAT_NETWORK_NAME,
env.artifacts,
)
} else {
provider = createProvider(
HARDHAT_NETWORK_NAME,
networkConfig,
env.config.paths,
env.artifacts,
)
}
return configureNetworkEnv(
env,
HARDHAT_NETWORK_NAME,
networkConfig,
provider
)
}
function requiresEVMConfiguration(networkConfig, api) {
return (
networkConfig.allowUnlimitedContractSize !== true ||
networkConfig.blockGasLimit !== api.gasLimitNumber ||
networkConfig.gas !== api.gasLimit ||
networkConfig.gasPrice !== api.gasPrice ||
networkConfig.initialBaseFeePerGas !== 0
)
}
function configureHardhatEVMGas(networkConfig, api){
networkConfig.allowUnlimitedContractSize = true;
networkConfig.blockGasLimit = api.gasLimitNumber;
networkConfig.gas = api.gasLimit;
networkConfig.gasPrice = api.gasPrice;
networkConfig.initialBaseFeePerGas = 0;
}
function configureNetworkEnv(env, networkName, networkConfig, provider){
env.config.networks[networkName] = networkConfig;
env.config.defaultNetwork = networkName;
env.network = Object.assign(env.network, {
name: networkName,
config: networkConfig,
provider: provider,
isHardhatEVM: true
});
env.ethereum = provider;
// Return a reference so we can set the from account
return env.network;
}
/**
* Configures mocha to generate a json object which maps which tests
* hit which lines of code.
*/
function collectTestMatrixData(args, env, api){
if (args.matrix){
mochaConfig = env.config.mocha || {};
mochaConfig.reporter = api.matrixReporterPath;
mochaConfig.reporterOptions = {
collectTestMatrixData: api.collectTestMatrixData.bind(api),
saveMochaJsonOutput: api.saveMochaJsonOutput.bind(api),
cwd: api.cwd
}
env.config.mocha = mochaConfig;
}
}
/**
* Returns all Hardhat artifacts.
* @param {HRE} env
* @return {Artifact[]}
*/
async function getAllArtifacts(env){
const all = [];
const qualifiedNames = await env.artifacts.getArtifactPaths();
for (const name of qualifiedNames){
all.push(require(name));
}
return all;
}
/**
* Compiles project
* Collects all artifacts from Hardhat project,
* Converts them to a format that can be consumed by api.abiUtils.diff
* Saves them to `api.abiOutputPath`
* @param {HRE} env
* @param {SolidityCoverageAPI} api
*/
async function generateHumanReadableAbiList(env, api, TASK_COMPILE){
await env.run(TASK_COMPILE);
const _artifacts = await getAllArtifacts(env);
const list = api.abiUtils.generateHumanReadableAbiList(_artifacts)
api.saveHumanReadableAbis(list);
}
/**
* Sets the default `from` account field in the network that will be used.
* This needs to be done after accounts are fetched from the launched client.
* @param {env} config
* @param {Array} accounts
*/
function setNetworkFrom(networkConfig, accounts){
if (!networkConfig.from){
networkConfig.from = accounts[0];
}
}
// TODO: Hardhat cacheing??
/**
* Generates a path to a temporary compilation cache directory
* @param {HardhatConfig} config
* @return {String} .../.coverage_cache
*/
function tempCacheDir(config){
return path.join(config.paths.root, '.coverage_cache');
}
/**
* Silently removes temporary folders and calls api.finish to shut server down
* @param {HardhatConfig} config
* @param {SolidityCoverage} api
* @return {Promise}
*/
async function finish(config, api, shouldKill){
const {
tempContractsDir,
tempArtifactsDir
} = pluginUtils.getTempLocations(config);
shell.config.silent = true;
shell.rm('-Rf', tempContractsDir);
shell.rm('-Rf', tempArtifactsDir);
shell.rm('-Rf', path.join(config.paths.root, '.coverage_cache'));
shell.config.silent = false;
if (api) await api.finish();
if (shouldKill) process.exit(1)
}
module.exports = {
configureHardhatEVMGas,
requiresEVMConfiguration,
normalizeConfig,
finish,
tempCacheDir,
setupHardhatNetwork,
getTestFilePaths,
setNetworkFrom,
collectTestMatrixData,
getAllArtifacts,
generateHumanReadableAbiList
}