|
|
@ -15,20 +15,20 @@ const gasPriceHex = 0x01; // Low gas price |
|
|
|
* Coverage Runner |
|
|
|
* Coverage Runner |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
class App { |
|
|
|
class App { |
|
|
|
constructor(config){ |
|
|
|
constructor(config) { |
|
|
|
this.coverageDir = './coverageEnv'; // Env that instrumented .sols are tested in
|
|
|
|
this.coverageDir = './coverageEnv'; // Env that instrumented .sols are tested in
|
|
|
|
|
|
|
|
|
|
|
|
// Options
|
|
|
|
// Options
|
|
|
|
this.network = ''; // Default truffle network execution flag
|
|
|
|
this.network = ''; // Default truffle network execution flag
|
|
|
|
this.silence = ''; // Default log level: configurable by --silence
|
|
|
|
this.silence = ''; // Default log level passed to shell
|
|
|
|
this.log = console.log; |
|
|
|
this.log = console.log; |
|
|
|
|
|
|
|
|
|
|
|
// Other
|
|
|
|
// Other
|
|
|
|
this.testrpcProcess; // ref to testrpc server we need to close on exit
|
|
|
|
this.testrpcProcess = null; // ref to testrpc server we need to close on exit
|
|
|
|
this.events; // ref to string loaded from 'allFiredEvents'
|
|
|
|
this.events = null; // ref to string array loaded from 'allFiredEvents'
|
|
|
|
this.testsErrored = null; // flag set to non-null if truffle tests error
|
|
|
|
this.testsErrored = null; // flag set to non-null if truffle tests error
|
|
|
|
this.coverage = new CoverageMap(); // initialize a coverage map
|
|
|
|
this.coverage = new CoverageMap(); // initialize a coverage map
|
|
|
|
|
|
|
|
|
|
|
|
// Config
|
|
|
|
// Config
|
|
|
|
this.config = config || {}; |
|
|
|
this.config = config || {}; |
|
|
|
this.workingDir = config.dir || '.'; // Relative path to contracts folder
|
|
|
|
this.workingDir = config.dir || '.'; // Relative path to contracts folder
|
|
|
@ -36,7 +36,7 @@ class App { |
|
|
|
this.skipFiles = config.skipFiles || []; // Which files should be skipped during instrumentation
|
|
|
|
this.skipFiles = config.skipFiles || []; // Which files should be skipped during instrumentation
|
|
|
|
this.norpc = config.norpc || false; // Launch testrpc-sc internally?
|
|
|
|
this.norpc = config.norpc || false; // Launch testrpc-sc internally?
|
|
|
|
this.port = config.port || 8555; // Port testrpc should listen on
|
|
|
|
this.port = config.port || 8555; // Port testrpc should listen on
|
|
|
|
|
|
|
|
|
|
|
|
this.copyNodeModules = config.copyNodeModules || false; // Copy node modules into coverageEnv?
|
|
|
|
this.copyNodeModules = config.copyNodeModules || false; // Copy node modules into coverageEnv?
|
|
|
|
this.testrpcOptions = config.testrpcOptions || null; // Options for testrpc-sc
|
|
|
|
this.testrpcOptions = config.testrpcOptions || null; // Options for testrpc-sc
|
|
|
|
this.testCommand = config.testCommand || null; // Optional test command
|
|
|
|
this.testCommand = config.testCommand || null; // Optional test command
|
|
|
@ -44,23 +44,22 @@ class App { |
|
|
|
this.setLoggingLevel(config.silent); |
|
|
|
this.setLoggingLevel(config.silent); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------- Methods ------------------------------------------------
|
|
|
|
// -------------------------------------- Methods ------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Generates a copy of the target project configured for solidity-coverage and saves to
|
|
|
|
* Generates a copy of the target project configured for solidity-coverage and saves to |
|
|
|
* the coverage environment folder. Process exits(1) if try fails |
|
|
|
* the coverage environment folder. Process exits(1) if try fails |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
generateCoverageEnvironment(){ |
|
|
|
generateCoverageEnvironment() { |
|
|
|
|
|
|
|
|
|
|
|
this.log('Generating coverage environment'); |
|
|
|
this.log('Generating coverage environment'); |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
let files = shell.ls(this.workingDir); |
|
|
|
let files = shell.ls(this.workingDir); |
|
|
|
const nmIndex = files.indexOf('node_modules'); |
|
|
|
const nmIndex = files.indexOf('node_modules'); |
|
|
|
|
|
|
|
|
|
|
|
// Removes node_modules from array (unless requested).
|
|
|
|
// Removes node_modules from array (unless requested).
|
|
|
|
if (!this.copyNodeModules && nmIndex > -1) { |
|
|
|
if (!this.copyNodeModules && nmIndex > -1) { |
|
|
|
files.splice(nmIndex, 1);
|
|
|
|
files.splice(nmIndex, 1); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
files = files.map(file => `${this.workingDir}/${file}`); |
|
|
|
files = files.map(file => `${this.workingDir}/${file}`); |
|
|
@ -73,10 +72,10 @@ class App { |
|
|
|
if (truffleConfig && truffleConfig.networks && truffleConfig.networks.coverage) { |
|
|
|
if (truffleConfig && truffleConfig.networks && truffleConfig.networks.coverage) { |
|
|
|
this.port = truffleConfig.networks.coverage.port || this.port; |
|
|
|
this.port = truffleConfig.networks.coverage.port || this.port; |
|
|
|
this.network = '--network coverage'; |
|
|
|
this.network = '--network coverage'; |
|
|
|
|
|
|
|
|
|
|
|
// No coverage network defaults to the dev network on port 8555, high gas / low price.
|
|
|
|
// No coverage network defaults to the dev network on port 8555, high gas / low price.
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
const trufflejs = defaultTruffleConfig(this.port, gasLimitHex, gasPriceHex) |
|
|
|
const trufflejs = defaultTruffleConfig(this.port, gasLimitHex, gasPriceHex); |
|
|
|
fs.writeFileSync(`${this.coverageDir}/truffle.js`, trufflejs); |
|
|
|
fs.writeFileSync(`${this.coverageDir}/truffle.js`, trufflejs); |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (err) { |
|
|
|
} catch (err) { |
|
|
@ -93,8 +92,7 @@ class App { |
|
|
|
* + Save instrumented contract in the coverage environment folder where covered tests will run |
|
|
|
* + Save instrumented contract in the coverage environment folder where covered tests will run |
|
|
|
* + Add instrumentation info to the coverage map |
|
|
|
* + Add instrumentation info to the coverage map |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
instrumentTarget(){ |
|
|
|
instrumentTarget() { |
|
|
|
|
|
|
|
|
|
|
|
this.skipFiles = this.skipFiles.map(contract => `${this.coverageDir}/contracts/${contract}`); |
|
|
|
this.skipFiles = this.skipFiles.map(contract => `${this.coverageDir}/contracts/${contract}`); |
|
|
|
this.skipFiles.push(`${this.coverageDir}/contracts/Migrations.sol`); |
|
|
|
this.skipFiles.push(`${this.coverageDir}/contracts/Migrations.sol`); |
|
|
|
|
|
|
|
|
|
|
@ -126,9 +124,8 @@ class App { |
|
|
|
* Changes here should be also be added to the before() block of test/run.js). |
|
|
|
* Changes here should be also be added to the before() block of test/run.js). |
|
|
|
* @return {Promise} Resolves when testrpc prints 'Listening' to std out / norpc is true. |
|
|
|
* @return {Promise} Resolves when testrpc prints 'Listening' to std out / norpc is true. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
launchTestrpc(){ |
|
|
|
launchTestrpc() { |
|
|
|
return new Promise((resolve, reject) => { |
|
|
|
return new Promise((resolve, reject) => { |
|
|
|
|
|
|
|
|
|
|
|
if (!this.norpc) { |
|
|
|
if (!this.norpc) { |
|
|
|
const defaultRpcOptions = `--gasLimit ${gasLimitHex} --accounts ${this.accounts} --port ${this.port}`; |
|
|
|
const defaultRpcOptions = `--gasLimit ${gasLimitHex} --accounts ${this.accounts} --port ${this.port}`; |
|
|
|
const options = this.testrpcOptions || defaultRpcOptions; |
|
|
|
const options = this.testrpcOptions || defaultRpcOptions; |
|
|
@ -137,13 +134,13 @@ class App { |
|
|
|
// Launch
|
|
|
|
// Launch
|
|
|
|
this.testrpcProcess = childprocess.exec(command + options, null, (err, stdout, stderr) => { |
|
|
|
this.testrpcProcess = childprocess.exec(command + options, null, (err, stdout, stderr) => { |
|
|
|
if (err) { |
|
|
|
if (err) { |
|
|
|
if(stdout) this.log(`testRpc stdout:\n${stdout}`); |
|
|
|
if (stdout) this.log(`testRpc stdout:\n${stdout}`); |
|
|
|
if(stderr) this.log(`testRpc stderr:\n${stderr}`); |
|
|
|
if (stderr) this.log(`testRpc stderr:\n${stderr}`); |
|
|
|
this.cleanUp('testRpc errored after launching as a childprocess.'); |
|
|
|
this.cleanUp('testRpc errored after launching as a childprocess.'); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// Resolve when testrpc logs that it's listening.
|
|
|
|
// Resolve when testrpc logs that it's listening.
|
|
|
|
this.testrpcProcess.stdout.on('data', data => { |
|
|
|
this.testrpcProcess.stdout.on('data', data => { |
|
|
|
if (data.includes('Listening')) { |
|
|
|
if (data.includes('Listening')) { |
|
|
|
this.log(`Launched testrpc on port ${this.port}`); |
|
|
|
this.log(`Launched testrpc on port ${this.port}`); |
|
|
@ -153,7 +150,7 @@ class App { |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
return resolve(); |
|
|
|
return resolve(); |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
@ -162,10 +159,8 @@ class App { |
|
|
|
* as its own statement for command line options to work, apparently. |
|
|
|
* as its own statement for command line options to work, apparently. |
|
|
|
* Also reads the 'allFiredEvents' log. |
|
|
|
* Also reads the 'allFiredEvents' log. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
runTestCommand(){ |
|
|
|
runTestCommand() { |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
|
|
|
|
|
|
|
|
const defaultCommand = `truffle test ${this.network} ${this.silence}`; |
|
|
|
const defaultCommand = `truffle test ${this.network} ${this.silence}`; |
|
|
|
const command = this.testCommand || defaultCommand; |
|
|
|
const command = this.testCommand || defaultCommand; |
|
|
|
this.log(`Running: ${command}\n(this can take a few seconds)...`); |
|
|
|
this.log(`Running: ${command}\n(this can take a few seconds)...`); |
|
|
@ -196,8 +191,7 @@ class App { |
|
|
|
/** |
|
|
|
/** |
|
|
|
* Generate coverage / write coverage report / run istanbul |
|
|
|
* Generate coverage / write coverage report / run istanbul |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
generateReport(){ |
|
|
|
generateReport() { |
|
|
|
|
|
|
|
|
|
|
|
const collector = new istanbul.Collector(); |
|
|
|
const collector = new istanbul.Collector(); |
|
|
|
const reporter = new istanbul.Reporter(); |
|
|
|
const reporter = new istanbul.Reporter(); |
|
|
|
|
|
|
|
|
|
|
@ -218,20 +212,19 @@ class App { |
|
|
|
resolve(); |
|
|
|
resolve(); |
|
|
|
}); |
|
|
|
}); |
|
|
|
} catch (err) { |
|
|
|
} catch (err) { |
|
|
|
const msg = 'There was a problem generating the coverage map / running Istanbul.\n'; |
|
|
|
const msg = 'There was a problem generating the coverage map / running Istanbul.\n'; |
|
|
|
this.cleanUp(msg + err); |
|
|
|
this.cleanUp(msg + err); |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------ Utils ----------------------------------------------
|
|
|
|
// ------------------------------------------ Utils ----------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Allows config to turn logging off (for CI) |
|
|
|
* Allows config to turn logging off (for CI) |
|
|
|
* @param {Boolean} isSilent
|
|
|
|
* @param {Boolean} isSilent |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
setLoggingLevel(isSilent){ |
|
|
|
setLoggingLevel(isSilent) { |
|
|
|
if (isSilent) { |
|
|
|
if (isSilent) { |
|
|
|
this.silence = '> /dev/null 2>&1'; |
|
|
|
this.silence = '> /dev/null 2>&1'; |
|
|
|
this.log = () => {}; |
|
|
|
this.log = () => {}; |
|
|
|