Refactor instrument method (#406)
* Move all filesystem & filtering logic to plugins * Move plugin helpers to own filepull/407/head
parent
37b54ccf12
commit
7e9be6c3c2
@ -0,0 +1,460 @@ |
||||
/** |
||||
* A collection of utilities for common tasks plugins will need in the course |
||||
* of composing a workflow using the solidity-coverage API |
||||
* |
||||
* TODO: Sweep back through here and make all `config.truffle_variable` plugin |
||||
* platform neutral... |
||||
*/ |
||||
|
||||
const PluginUI = require('./truffle.ui'); |
||||
|
||||
const path = require('path'); |
||||
const fs = require('fs-extra'); |
||||
const dir = require('node-dir'); |
||||
const globby = require('globby'); |
||||
const shell = require('shelljs'); |
||||
const globalModules = require('global-modules'); |
||||
const TruffleProvider = require('@truffle/provider'); |
||||
|
||||
// ===
|
||||
// UI
|
||||
// ===
|
||||
|
||||
/** |
||||
* Displays a list of skipped contracts |
||||
* @param {TruffleConfig} config |
||||
* @return {Object[]} skipped array of objects generated by `assembleTargets` method |
||||
*/ |
||||
function reportSkipped(config, skipped=[]){ |
||||
let started = false; |
||||
const ui = new PluginUI(config.logger.log); |
||||
|
||||
for (let item of skipped){ |
||||
if (!started) { |
||||
ui.report('instr-skip', []); |
||||
started = true; |
||||
} |
||||
ui.report('instr-skipped', [item.relativePath]); |
||||
} |
||||
} |
||||
|
||||
// ========
|
||||
// File I/O
|
||||
// ========
|
||||
|
||||
/** |
||||
* Loads source |
||||
* @param {String} _path absolute path |
||||
* @return {String} source file |
||||
*/ |
||||
function loadSource(_path){ |
||||
return fs.readFileSync(_path).toString(); |
||||
} |
||||
|
||||
/** |
||||
* Save a set of instrumented files to a temporary directory. |
||||
* @param {Object[]} targets array of targets generated by `assembleTargets` |
||||
* @param {[type]} originalDir absolute path to parent directory of original source |
||||
* @param {[type]} tempDir absolute path to temp parent directory |
||||
*/ |
||||
function save(targets, originalDir, tempDir){ |
||||
let _path; |
||||
for (target of targets) { |
||||
_path = target.canonicalPath.replace(originalDir, tempDir); |
||||
fs.outputFileSync(_path, target.source); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Relativizes an absolute file path, given an absolute parent path |
||||
* @param {String} pathToFile |
||||
* @param {String} pathToParent |
||||
* @return {String} relative path |
||||
*/ |
||||
function toRelativePath(pathToFile, pathToParent){ |
||||
return pathToFile.replace(`${pathToParent}${path.sep}`, ''); |
||||
} |
||||
|
||||
/** |
||||
* Returns a pair of canonically named temporary directory paths for contracts |
||||
* and artifacts. Instrumented assets can be written & compiled to these. |
||||
* Then the unit tests can be run, consuming them as sources. |
||||
* @param {TruffleConfig} config |
||||
* @return {Object} temp paths |
||||
*/ |
||||
function getTempLocations(config){ |
||||
const cwd = config.working_directory; |
||||
const contractsDirName = '.coverage_contracts'; |
||||
const artifactsDirName = '.coverage_artifacts'; |
||||
|
||||
return { |
||||
tempContractsDir: path.join(cwd, contractsDirName), |
||||
tempArtifactsDir: path.join(cwd, artifactsDirName) |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Checks for existence of contract sources, and sweeps away debris |
||||
* left over from an uncontrolled crash. |
||||
*/ |
||||
function checkContext(config, tempContractsDir, tempArtifactsDir){ |
||||
const ui = new PluginUI(config.logger.log); |
||||
|
||||
if (!shell.test('-e', config.contracts_directory)){ |
||||
|
||||
const msg = ui.generate('sources-fail', [config.contracts_directory]) |
||||
throw new Error(msg); |
||||
} |
||||
|
||||
if (shell.test('-e', tempContractsDir)){ |
||||
shell.rm('-Rf', tempContractsDir); |
||||
} |
||||
|
||||
if (shell.test('-e', tempArtifactsDir)){ |
||||
shell.rm('-Rf', tempArtifactsDir); |
||||
} |
||||
} |
||||
|
||||
|
||||
// =============================
|
||||
// Instrumentation Set Assembly
|
||||
// =============================
|
||||
|
||||
function assembleFiles(config, skipFiles=[]){ |
||||
let targets; |
||||
let skipFolders; |
||||
let skipped = []; |
||||
|
||||
const { |
||||
tempContractsDir, |
||||
tempArtifactsDir |
||||
} = getTempLocations(config); |
||||
|
||||
checkContext(config, tempContractsDir, tempArtifactsDir); |
||||
|
||||
shell.mkdir(tempContractsDir); |
||||
shell.mkdir(tempArtifactsDir); |
||||
|
||||
targets = shell.ls(`${config.contracts_directory}/**/*.sol`); |
||||
|
||||
skipFiles = assembleSkipped(config, targets, skipFiles); |
||||
|
||||
return assembleTargets(config, targets, skipFiles) |
||||
} |
||||
|
||||
function assembleTargets(config, targets=[], skipFiles=[]){ |
||||
const skipped = []; |
||||
const filtered = []; |
||||
const cd = config.contracts_directory; |
||||
|
||||
for (let target of targets){ |
||||
if (skipFiles.includes(target)){ |
||||
|
||||
skipped.push({ |
||||
canonicalPath: target, |
||||
relativePath: toRelativePath(target, cd), |
||||
source: loadSource(target) |
||||
}) |
||||
|
||||
} else { |
||||
|
||||
filtered.push({ |
||||
canonicalPath: target, |
||||
relativePath: toRelativePath(target, cd), |
||||
source: loadSource(target) |
||||
}) |
||||
} |
||||
} |
||||
|
||||
return { |
||||
skipped: skipped, |
||||
targets: filtered |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Parses the skipFiles option (which also accepts folders) |
||||
*/ |
||||
function assembleSkipped(config, targets, skipFiles=[]){ |
||||
const cd = config.contracts_directory; |
||||
|
||||
// Make paths absolute
|
||||
skipFiles = skipFiles.map(contract => `${cd}/${contract}`); |
||||
skipFiles.push(`${cd}/Migrations.sol`); |
||||
|
||||
// Enumerate files in skipped folders
|
||||
const skipFolders = skipFiles.filter(item => path.extname(item) !== '.sol') |
||||
|
||||
for (let folder of skipFolders){ |
||||
for (let target of targets ) { |
||||
if (target.indexOf(folder) === 0) |
||||
skipFiles.push(target); |
||||
} |
||||
}; |
||||
|
||||
return skipFiles; |
||||
} |
||||
|
||||
// ========
|
||||
// Truffle
|
||||
// ========
|
||||
|
||||
/** |
||||
* Returns a list of test files to pass to mocha. |
||||
* @param {Object} config truffleConfig |
||||
* @return {String[]} list of files to pass to mocha |
||||
*/ |
||||
function getTestFilePaths(config){ |
||||
let target; |
||||
let ui = new PluginUI(config.logger.log); |
||||
|
||||
|
||||
// Handle --file <path|glob> cli option (subset of tests)
|
||||
(typeof config.file === 'string') |
||||
? target = globby.sync([config.file]) |
||||
: target = dir.files(config.test_directory, { sync: true }) || []; |
||||
|
||||
// Filter native solidity tests and warn that they're skipped
|
||||
const solregex = /.*\.(sol)$/; |
||||
const hasSols = target.filter(f => f.match(solregex) != null); |
||||
|
||||
if (hasSols.length > 0) ui.report('sol-tests', [hasSols.length]); |
||||
|
||||
// Return list of test files
|
||||
const testregex = /.*\.(js|ts|es|es6|jsx)$/; |
||||
return target.filter(f => f.match(testregex) != null); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Configures the network. Runs before the server is launched. |
||||
* User can request a network from truffle-config with "--network <name>". |
||||
* There are overlapiing options in solcoverjs (like port and providerOptions.network_id). |
||||
* Where there are mismatches user is warned & the truffle network settings are preferred. |
||||
* |
||||
* Also generates a default config & sets the default gas high / gas price low. |
||||
* |
||||
* @param {TruffleConfig} config |
||||
* @param {SolidityCoverage} api |
||||
*/ |
||||
function setNetwork(config, api){ |
||||
const ui = new PluginUI(config.logger.log); |
||||
|
||||
// --network <network-name>
|
||||
if (config.network){ |
||||
const network = config.networks[config.network]; |
||||
|
||||
// Check network:
|
||||
if (!network){ |
||||
throw new Error(ui.generate('no-network', [config.network])); |
||||
} |
||||
|
||||
// Check network id
|
||||
if (!isNaN(parseInt(network.network_id))){ |
||||
|
||||
// Warn: non-matching provider options id and network id
|
||||
if (api.providerOptions.network_id && |
||||
api.providerOptions.network_id !== parseInt(network.network_id)){ |
||||
|
||||
ui.report('id-clash', [ parseInt(network.network_id) ]); |
||||
} |
||||
|
||||
// Prefer network defined id.
|
||||
api.providerOptions.network_id = parseInt(network.network_id); |
||||
|
||||
} else { |
||||
network.network_id = "*"; |
||||
} |
||||
|
||||
// Check port: use solcoverjs || default if undefined
|
||||
if (!network.port) { |
||||
ui.report('no-port', [api.port]); |
||||
network.port = api.port; |
||||
} |
||||
|
||||
// Warn: port conflicts
|
||||
if (api.port !== api.defaultPort && api.port !== network.port){ |
||||
ui.report('port-clash', [ network.port ]) |
||||
} |
||||
|
||||
// Prefer network port if defined;
|
||||
api.port = network.port; |
||||
|
||||
network.gas = api.gasLimit; |
||||
network.gasPrice = api.gasPrice; |
||||
|
||||
setOuterConfigKeys(config, api, network.network_id); |
||||
return; |
||||
} |
||||
|
||||
// Default Network Configuration
|
||||
config.network = 'soliditycoverage'; |
||||
setOuterConfigKeys(config, api, "*"); |
||||
|
||||
config.networks[config.network] = { |
||||
network_id: "*", |
||||
port: api.port, |
||||
host: api.host, |
||||
gas: api.gasLimit, |
||||
gasPrice: api.gasPrice |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Sets the default `from` account field in the truffle network that will be used. |
||||
* This needs to be done after accounts are fetched from the launched client. |
||||
* @param {TruffleConfig} config |
||||
* @param {Array} accounts |
||||
*/ |
||||
function setNetworkFrom(config, accounts){ |
||||
if (!config.networks[config.network].from){ |
||||
config.networks[config.network].from = accounts[0]; |
||||
} |
||||
} |
||||
|
||||
// Truffle complains that these outer keys *are not* set when running plugin fn directly.
|
||||
// But throws saying they *cannot* be manually set when running as truffle command.
|
||||
function setOuterConfigKeys(config, api, id){ |
||||
try { |
||||
config.network_id = id; |
||||
config.port = api.port; |
||||
config.host = api.host; |
||||
config.provider = TruffleProvider.create(config); |
||||
} catch (err){} |
||||
} |
||||
|
||||
/** |
||||
* Tries to load truffle module library and reports source. User can force use of |
||||
* a non-local version using cli flags (see option). It's necessary to maintain |
||||
* a fail-safe lib because feature was only introduced in 5.0.30. Load order is: |
||||
* |
||||
* 1. local node_modules |
||||
* 2. global node_modules |
||||
* 3. fail-safe (truffle lib v 5.0.31 at ./plugin-assets/truffle.library) |
||||
* |
||||
* @param {Object} truffleConfig config |
||||
* @return {Module} |
||||
*/ |
||||
function loadTruffleLibrary(config){ |
||||
const ui = new PluginUI(config.logger.log); |
||||
|
||||
// Local
|
||||
try { |
||||
if (config.useGlobalTruffle || config.usePluginTruffle) throw null; |
||||
|
||||
const lib = require("truffle"); |
||||
ui.report('lib-local'); |
||||
return lib; |
||||
|
||||
} catch(err) {}; |
||||
|
||||
// Global
|
||||
try { |
||||
if (config.usePluginTruffle) throw null; |
||||
|
||||
const globalTruffle = path.join(globalModules, 'truffle'); |
||||
const lib = require(globalTruffle); |
||||
ui.report('lib-global'); |
||||
return lib; |
||||
|
||||
} catch(err) {}; |
||||
|
||||
// Plugin Copy @ v 5.0.31
|
||||
try { |
||||
if (config.forceLibFailure) throw null; // For err unit testing
|
||||
|
||||
ui.report('lib-warn'); |
||||
return require("./truffle.library") |
||||
|
||||
} catch(err) { |
||||
throw new Error(ui.generate('lib-fail', [err])); |
||||
}; |
||||
|
||||
} |
||||
|
||||
function loadSolcoverJS(config){ |
||||
let solcoverjs; |
||||
let coverageConfig; |
||||
let ui = new PluginUI(config.logger.log); |
||||
|
||||
|
||||
// Handle --solcoverjs flag
|
||||
(config.solcoverjs) |
||||
? solcoverjs = path.join(config.working_directory, config.solcoverjs) |
||||
: solcoverjs = path.join(config.working_directory, '.solcover.js'); |
||||
|
||||
// Catch solcoverjs syntax errors
|
||||
if (shell.test('-e', solcoverjs)){ |
||||
|
||||
try { |
||||
coverageConfig = require(solcoverjs); |
||||
} catch(error){ |
||||
error.message = ui.generate('solcoverjs-fail') + error.message; |
||||
throw new Error(error) |
||||
} |
||||
|
||||
// Config is optional
|
||||
} else { |
||||
coverageConfig = {}; |
||||
} |
||||
|
||||
// Truffle writes to coverage config
|
||||
coverageConfig.log = config.logger.log; |
||||
coverageConfig.cwd = config.working_directory; |
||||
coverageConfig.originalContractsDir = config.contracts_directory; |
||||
|
||||
// Solidity-Coverage writes to Truffle config
|
||||
config.mocha = config.mocha || {}; |
||||
|
||||
if (coverageConfig.mocha && typeof coverageConfig.mocha === 'object'){ |
||||
config.mocha = Object.assign( |
||||
config.mocha, |
||||
coverageConfig.mocha |
||||
); |
||||
} |
||||
|
||||
return coverageConfig; |
||||
} |
||||
|
||||
// ==========================
|
||||
// Finishing / Cleanup
|
||||
// ==========================
|
||||
|
||||
/** |
||||
* Silently removes temporary folders and calls api.finish to shut server down |
||||
* @param {TruffleConfig} config |
||||
* @param {SolidityCoverage} api |
||||
* @return {Promise} |
||||
*/ |
||||
async function finish(config, api){ |
||||
const { |
||||
tempContractsDir, |
||||
tempArtifactsDir |
||||
} = getTempLocations(config); |
||||
|
||||
shell.config.silent = true; |
||||
shell.rm('-Rf', tempContractsDir); |
||||
shell.rm('-Rf', tempArtifactsDir); |
||||
|
||||
if (api) await api.finish(); |
||||
} |
||||
|
||||
module.exports = { |
||||
assembleFiles: assembleFiles, |
||||
assembleSkipped: assembleSkipped, |
||||
assembleTargets: assembleTargets, |
||||
checkContext: checkContext, |
||||
finish: finish, |
||||
getTempLocations: getTempLocations, |
||||
getTestFilePaths: getTestFilePaths, |
||||
loadSource: loadSource, |
||||
loadSolcoverJS: loadSolcoverJS, |
||||
loadTruffleLibrary: loadTruffleLibrary, |
||||
reportSkipped: reportSkipped, |
||||
save: save, |
||||
setNetwork: setNetwork, |
||||
setNetworkFrom: setNetworkFrom, |
||||
setOuterConfigKeys: setOuterConfigKeys, |
||||
checkContext: checkContext, |
||||
toRelativePath: toRelativePath |
||||
} |
@ -1,326 +1,114 @@ |
||||
const App = require('./../lib/app'); |
||||
const API = require('./../lib/api'); |
||||
const utils = require('./plugin-assets/plugin.utils'); |
||||
const PluginUI = require('./plugin-assets/truffle.ui'); |
||||
|
||||
const pkg = require('./../package.json'); |
||||
const req = require('req-cwd'); |
||||
const death = require('death'); |
||||
const path = require('path'); |
||||
const dir = require('node-dir'); |
||||
const Web3 = require('web3'); |
||||
const util = require('util'); |
||||
const globby = require('globby'); |
||||
const shell = require('shelljs'); |
||||
const globalModules = require('global-modules'); |
||||
const TruffleProvider = require('@truffle/provider'); |
||||
|
||||
|
||||
/** |
||||
* Truffle Plugin: `truffle run coverage [options]` |
||||
* @param {Object} truffleConfig @truffle/config config |
||||
* @param {Object} config @truffle/config config |
||||
* @return {Promise} |
||||
*/ |
||||
async function plugin(truffleConfig){ |
||||
async function plugin(config){ |
||||
let ui; |
||||
let app; |
||||
let api; |
||||
let error; |
||||
let truffle; |
||||
let solcoverjs; |
||||
let testsErrored = false; |
||||
|
||||
// Separate try block because this logic
|
||||
// runs before app.cleanUp is defined.
|
||||
try { |
||||
ui = new PluginUI(truffleConfig.logger.log); |
||||
|
||||
if(truffleConfig.help) return ui.report('help'); // Exit if --help
|
||||
death(utils.finish.bind(null, config, api)); // Catch interrupt signals
|
||||
|
||||
truffle = loadTruffleLibrary(ui, truffleConfig); |
||||
app = new App(loadSolcoverJS(ui, truffleConfig)); |
||||
ui = new PluginUI(config.logger.log); |
||||
|
||||
} catch (err) { throw err } |
||||
if(config.help) return ui.report('help'); // Exit if --help
|
||||
|
||||
try { |
||||
// Catch interrupt signals
|
||||
death(app.cleanUp); |
||||
truffle = utils.loadTruffleLibrary(config); |
||||
api = new API(utils.loadSolcoverJS(config)); |
||||
|
||||
setNetwork(ui, app, truffleConfig); |
||||
utils.setNetwork(config, api); |
||||
|
||||
// Provider / Server launch
|
||||
const address = await app.ganache(truffle.ganache); |
||||
// Server launch
|
||||
const address = await api.ganache(truffle.ganache); |
||||
|
||||
const web3 = new Web3(address); |
||||
const accounts = await web3.eth.getAccounts(); |
||||
const nodeInfo = await web3.eth.getNodeInfo(); |
||||
const ganacheVersion = nodeInfo.split('/')[1]; |
||||
|
||||
setNetworkFrom(truffleConfig, accounts); |
||||
utils.setNetworkFrom(config, accounts); |
||||
|
||||
// Version Info
|
||||
ui.report('truffle-version', [truffle.version]); |
||||
ui.report('ganache-version', [ganacheVersion]); |
||||
ui.report('coverage-version',[pkg.version]); |
||||
ui.report('versions', [ |
||||
truffle.version, |
||||
ganacheVersion, |
||||
pkg.version |
||||
]); |
||||
|
||||
if (truffleConfig.version) return app.cleanUp(); // Exit if --version
|
||||
// Exit if --version
|
||||
if (config.version) return await utils.finish(config, api); |
||||
|
||||
ui.report('network', [ |
||||
truffleConfig.network, |
||||
truffleConfig.networks[truffleConfig.network].network_id, |
||||
truffleConfig.networks[truffleConfig.network].port |
||||
config.network, |
||||
config.networks[config.network].network_id, |
||||
config.networks[config.network].port |
||||
]); |
||||
|
||||
// Instrument
|
||||
app.sanityCheckContext(); |
||||
app.generateStandardEnvironment(); |
||||
app.instrument(); |
||||
let { |
||||
targets, |
||||
skipped |
||||
} = utils.assembleFiles(config, api.skipFiles); |
||||
|
||||
targets = api.instrument(targets); |
||||
utils.reportSkipped(config, skipped); |
||||
|
||||
// Filesystem & Compiler Re-configuration
|
||||
truffleConfig.contracts_directory = app.contractsDir; |
||||
truffleConfig.build_directory = app.artifactsDir; |
||||
const { |
||||
tempArtifactsDir, |
||||
tempContractsDir |
||||
} = utils.getTempLocations(config); |
||||
|
||||
utils.save(targets, config.contracts_directory, tempContractsDir); |
||||
utils.save(skipped, config.contracts_directory, tempContractsDir); |
||||
|
||||
truffleConfig.contracts_build_directory = path.join( |
||||
app.artifactsDir, |
||||
path.basename(truffleConfig.contracts_build_directory) |
||||
config.contracts_directory = tempContractsDir; |
||||
config.build_directory = tempArtifactsDir; |
||||
|
||||
config.contracts_build_directory = path.join( |
||||
tempArtifactsDir, |
||||
path.basename(config.contracts_build_directory) |
||||
); |
||||
|
||||
truffleConfig.all = true; |
||||
truffleConfig.test_files = getTestFilePaths(ui, truffleConfig); |
||||
truffleConfig.compilers.solc.settings.optimizer.enabled = false; |
||||
config.all = true; |
||||
config.test_files = utils.getTestFilePaths(config); |
||||
config.compilers.solc.settings.optimizer.enabled = false; |
||||
|
||||
// Compile Instrumented Contracts
|
||||
await truffle.contracts.compile(truffleConfig); |
||||
await truffle.contracts.compile(config); |
||||
|
||||
// Run tests
|
||||
try { |
||||
failures = await truffle.test.run(truffleConfig) |
||||
failures = await truffle.test.run(config) |
||||
} catch (e) { |
||||
error = e.stack; |
||||
} |
||||
// Run Istanbul
|
||||
await app.report(); |
||||
await api.report(); |
||||
|
||||
} catch(e){ |
||||
error = e; |
||||
} |
||||
|
||||
|
||||
// Finish
|
||||
await app.cleanUp(); |
||||
await utils.finish(config, api); |
||||
|
||||
if (error !== undefined) throw error; |
||||
if (failures > 0) throw new Error(`${failures} test(s) failed under coverage.`) |
||||
} |
||||
|
||||
// -------------------------------------- Helpers --------------------------------------------------
|
||||
|
||||
/** |
||||
* Returns a list of test files to pass to mocha. |
||||
* @param {Object} ui reporter utility |
||||
* @param {Object} truffle truffleConfig |
||||
* @return {String[]} list of files to pass to mocha |
||||
*/ |
||||
function getTestFilePaths(ui, truffle){ |
||||
let target; |
||||
|
||||
// Handle --file <path|glob> cli option (subset of tests)
|
||||
(typeof truffle.file === 'string') |
||||
? target = globby.sync([truffle.file]) |
||||
: target = dir.files(truffle.test_directory, { sync: true }) || []; |
||||
|
||||
// Filter native solidity tests and warn that they're skipped
|
||||
const solregex = /.*\.(sol)$/; |
||||
const hasSols = target.filter(f => f.match(solregex) != null); |
||||
|
||||
if (hasSols.length > 0) ui.report('sol-tests', [hasSols.length]); |
||||
|
||||
// Return list of test files
|
||||
const testregex = /.*\.(js|ts|es|es6|jsx)$/; |
||||
return target.filter(f => f.match(testregex) != null); |
||||
} |
||||
|
||||
/** |
||||
* Configures the network. Runs before the server is launched. |
||||
* User can request a network from truffle-config with "--network <name>". |
||||
* There are overlapping options in solcoverjs (like port and providerOptions.network_id). |
||||
* Where there are mismatches user is warned & the truffle network settings are preferred. |
||||
* |
||||
* Also generates a default config & sets the default gas high / gas price low. |
||||
* |
||||
* @param {SolidityCoverage} app |
||||
* @param {TruffleConfig} config |
||||
*/ |
||||
function setNetwork(ui, app, config){ |
||||
|
||||
// --network <network-name>
|
||||
if (config.network){ |
||||
const network = config.networks[config.network]; |
||||
|
||||
// Check network:
|
||||
if (!network){ |
||||
throw new Error(ui.generate('no-network', [config.network])); |
||||
} |
||||
|
||||
// Check network id
|
||||
if (!isNaN(parseInt(network.network_id))){ |
||||
|
||||
// Warn: non-matching provider options id and network id
|
||||
if (app.providerOptions.network_id && |
||||
app.providerOptions.network_id !== parseInt(network.network_id)){ |
||||
|
||||
ui.report('id-clash', [ parseInt(network.network_id) ]); |
||||
} |
||||
|
||||
// Prefer network defined id.
|
||||
app.providerOptions.network_id = parseInt(network.network_id); |
||||
|
||||
} else { |
||||
network.network_id = "*"; |
||||
} |
||||
|
||||
// Check port: use solcoverjs || default if undefined
|
||||
if (!network.port) { |
||||
ui.report('no-port', [app.port]); |
||||
network.port = app.port; |
||||
} |
||||
|
||||
// Warn: port conflicts
|
||||
if (app.port !== app.defaultPort && app.port !== network.port){ |
||||
ui.report('port-clash', [ network.port ]) |
||||
} |
||||
|
||||
// Prefer network port if defined;
|
||||
app.port = network.port; |
||||
|
||||
network.gas = app.gasLimit; |
||||
network.gasPrice = app.gasPrice; |
||||
|
||||
setOuterConfigKeys(config, app, network.network_id); |
||||
return; |
||||
} |
||||
|
||||
// Default Network Configuration
|
||||
config.network = 'soliditycoverage'; |
||||
setOuterConfigKeys(config, app, "*"); |
||||
|
||||
config.networks[config.network] = { |
||||
network_id: "*", |
||||
port: app.port, |
||||
host: app.host, |
||||
gas: app.gasLimit, |
||||
gasPrice: app.gasPrice |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Sets the default `from` account field in the truffle network that will be used. |
||||
* This needs to be done after accounts are fetched from the launched client. |
||||
* @param {TruffleConfig} config |
||||
* @param {Array} accounts |
||||
*/ |
||||
function setNetworkFrom(config, accounts){ |
||||
if (!config.networks[config.network].from){ |
||||
config.networks[config.network].from = accounts[0]; |
||||
} |
||||
} |
||||
|
||||
// Truffle complains that these outer keys *are not* set when running plugin fn directly.
|
||||
// But throws saying they *cannot* be manually set when running as truffle command.
|
||||
function setOuterConfigKeys(config, app, id){ |
||||
try { |
||||
config.network_id = id; |
||||
config.port = app.port; |
||||
config.host = app.host; |
||||
config.provider = TruffleProvider.create(config); |
||||
} catch (err){} |
||||
} |
||||
|
||||
/** |
||||
* Tries to load truffle module library and reports source. User can force use of |
||||
* a non-local version using cli flags (see option). Load order is: |
||||
* |
||||
* 1. local node_modules |
||||
* 2. global node_modules |
||||
* 3. fail-safe (truffle lib v 5.0.31 at ./plugin-assets/truffle.library) |
||||
* |
||||
* @param {Object} ui reporter utility |
||||
* @param {Object} truffleConfig config |
||||
* @return {Module} |
||||
*/ |
||||
function loadTruffleLibrary(ui, truffleConfig){ |
||||
|
||||
// Local
|
||||
try { |
||||
if (truffleConfig.useGlobalTruffle || truffleConfig.usePluginTruffle) throw null; |
||||
|
||||
const lib = require("truffle"); |
||||
ui.report('lib-local'); |
||||
return lib; |
||||
|
||||
} catch(err) {}; |
||||
|
||||
// Global
|
||||
try { |
||||
if (truffleConfig.usePluginTruffle) throw null; |
||||
|
||||
const globalTruffle = path.join(globalModules, 'truffle'); |
||||
const lib = require(globalTruffle); |
||||
ui.report('lib-global'); |
||||
return lib; |
||||
|
||||
} catch(err) {}; |
||||
|
||||
// Plugin Copy @ v 5.0.31
|
||||
try { |
||||
if (truffleConfig.forceLibFailure) throw null; // For err unit testing
|
||||
|
||||
ui.report('lib-warn'); |
||||
return require("./plugin-assets/truffle.library") |
||||
|
||||
} catch(err) { |
||||
throw new Error(ui.generate('lib-fail', [err])); |
||||
}; |
||||
|
||||
} |
||||
|
||||
function loadSolcoverJS(ui, truffleConfig){ |
||||
let coverageConfig; |
||||
let solcoverjs; |
||||
|
||||
// Handle --solcoverjs flag
|
||||
(truffleConfig.solcoverjs) |
||||
? solcoverjs = path.join(truffleConfig.working_directory, truffleConfig.solcoverjs) |
||||
: solcoverjs = path.join(truffleConfig.working_directory, '.solcover.js'); |
||||
|
||||
// Catch solcoverjs syntax errors
|
||||
if (shell.test('-e', solcoverjs)){ |
||||
|
||||
try { |
||||
coverageConfig = require(solcoverjs); |
||||
} catch(error){ |
||||
error.message = ui.generate('solcoverjs-fail') + error.message; |
||||
throw new Error(error) |
||||
} |
||||
|
||||
// Config is optional
|
||||
} else { |
||||
coverageConfig = {}; |
||||
} |
||||
|
||||
// Truffle writes to coverage config
|
||||
coverageConfig.log = truffleConfig.logger.log; |
||||
coverageConfig.cwd = truffleConfig.working_directory; |
||||
coverageConfig.originalContractsDir = truffleConfig.contracts_directory; |
||||
|
||||
// Solidity-Coverage writes to Truffle config
|
||||
truffleConfig.mocha = truffleConfig.mocha || {}; |
||||
|
||||
if (coverageConfig.mocha && typeof coverageConfig.mocha === 'object'){ |
||||
truffleConfig.mocha = Object.assign( |
||||
truffleConfig.mocha, |
||||
coverageConfig.mocha |
||||
); |
||||
} |
||||
|
||||
return coverageConfig; |
||||
} |
||||
|
||||
|
||||
module.exports = plugin; |
||||
|
Loading…
Reference in new issue