Merge pull request #58 from cgewecke/truffle3-adriamb

Resolve inheritance issues
pull/1/head
c-g-e-w-e-k-e- 8 years ago committed by GitHub
commit 3fd5e32700
  1. 1
      .eslintignore
  2. 1
      .eslintrc
  3. 1
      .gitignore
  4. 1
      circle.yml
  5. 31
      coverageMap.js
  6. 73
      exec.js
  7. 18
      injector.js
  8. 6
      instrumentSolidity.js
  9. 8
      package.json
  10. 6
      test/conditional.js
  11. 4
      test/expressions.js
  12. 1
      test/if.js
  13. 1
      test/loops.js
  14. 25
      test/run.js
  15. 5
      test/run/block-gas-limit.js
  16. 4
      test/run/empty.js
  17. 18
      test/run/inheritance.js
  18. 18
      test/run/only-call.js
  19. 6
      test/run/simple.js
  20. 7
      test/run/sol-parse-fail.js
  21. 5
      test/run/truffle-crash.js
  22. 7
      test/run/truffle-test-fail.js
  23. 25
      test/util/mockTruffle.js
  24. 4
      test/util/util.js
  25. 7
      test/util/vm.js

@ -0,0 +1 @@
test/run/truffle-crash.js

@ -40,7 +40,6 @@
"semi": [2, "always"], "semi": [2, "always"],
"no-unused-vars": 0, "no-unused-vars": 0,
"import/extensions": [0], "import/extensions": [0],
"import/no-dynamic-require": [0],
"arrow-parens":[2, "as-needed"], "arrow-parens":[2, "as-needed"],
"no-plusplus":[2, { "allowForLoopAfterthoughts": true }], "no-plusplus":[2, { "allowForLoopAfterthoughts": true }],
"no-bitwise": [2], "no-bitwise": [2],

1
.gitignore vendored

@ -3,3 +3,4 @@ coverage.json
coverage/ coverage/
node_modules/ node_modules/
.changelog .changelog
.DS_Store

@ -3,6 +3,7 @@ machine:
version: 6.9.1 version: 6.9.1
dependencies: dependencies:
pre: pre:
- npm install -g truffle
- rm -rf node_modules/ - rm -rf node_modules/
test: test:
override: override:

@ -5,11 +5,7 @@
*/ */
const SolidityCoder = require('web3/lib/solidity/coder.js'); const SolidityCoder = require('web3/lib/solidity/coder.js');
const path = require('path'); const path = require('path');
const keccak = require('keccakjs');
const lineTopic = 'b8995a65f405d9756b41a334f38d8ff0c93c4934e170d3c1429c3e7ca101014d';
const functionTopic = 'd4ce765fd23c5cc3660249353d61ecd18ca60549dd62cb9ca350a4244de7b87f';
const branchTopic = 'd4cf56ed5ba572684f02f889f12ac42d9583c8e3097802060e949bfbb3c1bff5';
const statementTopic = 'b51abbff580b3a34bbc725f2dc6f736e9d4b45a41293fd0084ad865a31fde0c8';
/** /**
* Converts solcover event data into an object that can be * Converts solcover event data into an object that can be
@ -20,6 +16,10 @@ module.exports = class CoverageMap {
constructor() { constructor() {
this.coverage = {}; this.coverage = {};
this.lineTopics = [];
this.functionTopics = [];
this.branchTopics = [];
this.statementTopics = [];
} }
/** /**
@ -29,6 +29,7 @@ module.exports = class CoverageMap {
* @param {String} canonicalContractPath target file location * @param {String} canonicalContractPath target file location
* @return {Object} coverage map with all values set to zero * @return {Object} coverage map with all values set to zero
*/ */
addContract(info, canonicalContractPath) { addContract(info, canonicalContractPath) {
this.coverage[canonicalContractPath] = { this.coverage[canonicalContractPath] = {
l: {}, l: {},
@ -56,6 +57,17 @@ module.exports = class CoverageMap {
for (let x = 1; x <= Object.keys(info.statementMap).length; x++) { for (let x = 1; x <= Object.keys(info.statementMap).length; x++) {
this.coverage[canonicalContractPath].s[x] = 0; this.coverage[canonicalContractPath].s[x] = 0;
} }
const keccakhex = (x => {
const hash = new keccak(256); // eslint-disable-line new-cap
hash.update(x);
return hash.digest('hex');
});
this.lineTopics.push(keccakhex('__Coverage' + info.contractName + '(string,uint256)'));
this.functionTopics.push(keccakhex('__FunctionCoverage' + info.contractName + '(string,uint256)'));
this.branchTopics.push(keccakhex('__BranchCoverage' + info.contractName + '(string,uint256,uint256)'));
this.statementTopics.push(keccakhex('__StatementCoverage' + info.contractName + '(string,uint256)'));
} }
/** /**
@ -68,19 +80,20 @@ module.exports = class CoverageMap {
generate(events, pathPrefix) { generate(events, pathPrefix) {
for (let idx = 0; idx < events.length; idx++) { for (let idx = 0; idx < events.length; idx++) {
const event = JSON.parse(events[idx]); const event = JSON.parse(events[idx]);
if (event.topics.indexOf(lineTopic) >= 0) {
if (event.topics.filter(t => this.lineTopics.indexOf(t) >= 0).length > 0) {
const data = SolidityCoder.decodeParams(['string', 'uint256'], event.data.replace('0x', '')); const data = SolidityCoder.decodeParams(['string', 'uint256'], event.data.replace('0x', ''));
const canonicalContractPath = data[0]; const canonicalContractPath = data[0];
this.coverage[canonicalContractPath].l[data[1].toNumber()] += 1; this.coverage[canonicalContractPath].l[data[1].toNumber()] += 1;
} else if (event.topics.indexOf(functionTopic) >= 0) { } else if (event.topics.filter(t => this.functionTopics.indexOf(t) >= 0).length > 0) {
const data = SolidityCoder.decodeParams(['string', 'uint256'], event.data.replace('0x', '')); const data = SolidityCoder.decodeParams(['string', 'uint256'], event.data.replace('0x', ''));
const canonicalContractPath = data[0]; const canonicalContractPath = data[0];
this.coverage[canonicalContractPath].f[data[1].toNumber()] += 1; this.coverage[canonicalContractPath].f[data[1].toNumber()] += 1;
} else if (event.topics.indexOf(branchTopic) >= 0) { } else if (event.topics.filter(t => this.branchTopics.indexOf(t) >= 0).length > 0) {
const data = SolidityCoder.decodeParams(['string', 'uint256', 'uint256'], event.data.replace('0x', '')); const data = SolidityCoder.decodeParams(['string', 'uint256', 'uint256'], event.data.replace('0x', ''));
const canonicalContractPath = data[0]; const canonicalContractPath = data[0];
this.coverage[canonicalContractPath].b[data[1].toNumber()][data[2].toNumber()] += 1; this.coverage[canonicalContractPath].b[data[1].toNumber()][data[2].toNumber()] += 1;
} else if (event.topics.indexOf(statementTopic) >= 0) { } else if (event.topics.filter(t => this.statementTopics.indexOf(t) >= 0).length > 0) {
const data = SolidityCoder.decodeParams(['string', 'uint256'], event.data.replace('0x', '')); const data = SolidityCoder.decodeParams(['string', 'uint256'], event.data.replace('0x', ''));
const canonicalContractPath = data[0]; const canonicalContractPath = data[0];
this.coverage[canonicalContractPath].s[data[1].toNumber()] += 1; this.coverage[canonicalContractPath].s[data[1].toNumber()] += 1;

@ -7,11 +7,11 @@ const path = require('path');
const childprocess = require('child_process'); const childprocess = require('child_process');
const getInstrumentedVersion = require('./instrumentSolidity.js'); const getInstrumentedVersion = require('./instrumentSolidity.js');
const CoverageMap = require('./coverageMap.js'); const CoverageMap = require('./coverageMap.js');
const istanbul = require('istanbul'); const istanbul = require('istanbul');
const istanbulCollector = new istanbul.Collector(); const istanbulCollector = new istanbul.Collector();
const istanbulReporter = new istanbul.Reporter(); const istanbulReporter = new istanbul.Reporter();
// Very high gas block limits / contract deployment limits // Very high gas block limits / contract deployment limits
const gasLimitString = '0xfffffffffff'; const gasLimitString = '0xfffffffffff';
const gasLimitHex = 0xfffffffffff; const gasLimitHex = 0xfffffffffff;
@ -30,8 +30,28 @@ let silence = ''; // Default log level: configurable b
let log = console.log; // Default log level: configurable by --silence let log = console.log; // Default log level: configurable by --silence
let testrpcProcess; // ref to testrpc server we need to close on exit let testrpcProcess; // ref to testrpc server we need to close on exit
let events; // ref to string loaded from 'allFiredEvents' let events; // ref to string loaded from 'allFiredEvents'
// --------------------------------------- Utilities -----------------------------------------------
/**
* Removes coverage build artifacts, kills testrpc.
* Exits (1) and prints msg on error, exits (0) otherwise.
* @param {String} err error message
*/
function cleanUp(err) {
log('Cleaning up...');
shell.config.silent = true;
shell.rm('-Rf', `${coverageDir}`);
shell.rm('./allFiredEvents');
if (testrpcProcess) { testrpcProcess.kill(); }
if (err) {
log(`${err}\nExiting without generating coverage...`);
process.exit(1);
} else {
process.exit(0);
}
}
// --------------------------------------- Script -------------------------------------------------- // --------------------------------------- Script --------------------------------------------------
const config = reqCwd.silent(`${workingDir}/.solcover.js`) || {}; const config = reqCwd.silent(`${workingDir}/.solcover.js`) || {};
@ -48,7 +68,7 @@ if (config.silent) {
if (!config.norpc) { if (!config.norpc) {
try { try {
log(`Launching testrpc on port ${port}`); log(`Launching testrpc on port ${port}`);
const command = `./node_modules/ethereumjs-testrpc-sc/bin/testrpc`; const command = './node_modules/ethereumjs-testrpc-sc/bin/testrpc';
const options = `--gasLimit ${gasLimitString} --port ${port}`; const options = `--gasLimit ${gasLimitString} --port ${port}`;
testrpcProcess = childprocess.exec(command + options); testrpcProcess = childprocess.exec(command + options);
} catch (err) { } catch (err) {
@ -69,19 +89,19 @@ try {
const truffleConfig = reqCwd(`${workingDir}/truffle.js`); const truffleConfig = reqCwd(`${workingDir}/truffle.js`);
// Coverage network opts specified: copy truffle.js whole to coverage environment // Coverage network opts specified: copy truffle.js whole to coverage environment
if (truffleConfig.networks.coverage){ if (truffleConfig.networks.coverage) {
shell.cp(`${workingDir}/truffle.js`, `${coverageDir}/truffle.js`); shell.cp(`${workingDir}/truffle.js`, `${coverageDir}/truffle.js`);
// Coverage network opts NOT specified: default to the development network w/ modified // Coverage network opts NOT specified: default to the development network w/ modified
// port, gasLimit, gasPrice. Export the config object only. // port, gasLimit, gasPrice. Export the config object only.
} else { } else {
truffleConfig.networks.development.port = port; truffleConfig.networks.development.port = port;
truffleConfig.networks.development.gas = gasLimitHex; truffleConfig.networks.development.gas = gasLimitHex;
truffleConfig.networks.development.gasPrice = gasPriceHex; truffleConfig.networks.development.gasPrice = gasPriceHex;
coverageOption = ''; coverageOption = '';
fs.writeFileSync(`${coverageDir}/truffle.js`, `module.exports = ${JSON.stringify(truffleConfig)}`); fs.writeFileSync(`${coverageDir}/truffle.js`, `module.exports = ${JSON.stringify(truffleConfig)}`);
} }
} catch (err){ } catch (err) {
const msg = ('There was a problem generating the coverage environment: '); const msg = ('There was a problem generating the coverage environment: ');
cleanUp(msg + err); cleanUp(msg + err);
} }
@ -99,7 +119,7 @@ try {
if (file !== migrations) { if (file !== migrations) {
log('Instrumenting ', file); log('Instrumenting ', file);
const contractPath = path.resolve(file); const contractPath = path.resolve(file);
const canonicalPath = contractPath.split(`/coverageEnv`).join(''); const canonicalPath = contractPath.split('/coverageEnv').join('');
const contract = fs.readFileSync(contractPath).toString(); const contract = fs.readFileSync(contractPath).toString();
const instrumentedContractInfo = getInstrumentedVersion(contract, canonicalPath); const instrumentedContractInfo = getInstrumentedVersion(contract, canonicalPath);
fs.writeFileSync(contractPath, instrumentedContractInfo.contract); fs.writeFileSync(contractPath, instrumentedContractInfo.contract);
@ -114,9 +134,7 @@ try {
// coverage environment folder // coverage environment folder
try { try {
log('Launching Truffle (this can take a few seconds)...'); log('Launching Truffle (this can take a few seconds)...');
const truffle = `./../node_modules/truffle/cli.js`; const command = `cd coverageEnv && truffle test ${coverageOption} ${silence}`;
const command = `cd coverageEnv && ${truffle} test ${coverageOption} ${silence}`;
//const command = `cd coverageEnv && truffle test ${coverageOption} ${silence}`;
shell.exec(command); shell.exec(command);
} catch (err) { } catch (err) {
cleanUp(err); cleanUp(err);
@ -139,9 +157,9 @@ try {
// Generate coverage / write coverage report / run istanbul // Generate coverage / write coverage report / run istanbul
try { try {
//coverage.generate(events, `${coverageDir}/contracts/`); // coverage.generate(events, `${coverageDir}/contracts/`);
coverage.generate(events, './contracts'); coverage.generate(events, './contracts');
const json = JSON.stringify(coverage.coverage); const json = JSON.stringify(coverage.coverage);
fs.writeFileSync('./coverage.json', json); fs.writeFileSync('./coverage.json', json);
@ -150,39 +168,16 @@ try {
istanbulReporter.add('lcov'); istanbulReporter.add('lcov');
istanbulReporter.add('text'); istanbulReporter.add('text');
istanbulReporter.write(istanbulCollector, true, () => { istanbulReporter.write(istanbulCollector, true, () => {
log('Istanbul coverage reports generated'); log('Istanbul coverage reports generated');
}); });
} catch (err) { } catch (err) {
if (config.testing){ if (config.testing) {
cleanUp() cleanUp();
} else { } else {
const msg = 'There was a problem generating producing the coverage map / running Istanbul.\n'; const msg = 'There was a problem generating producing the coverage map / running Istanbul.\n';
cleanUp(msg + err); cleanUp(msg + err);
} }
} }
// Finish // Finish
cleanUp(); cleanUp();
// --------------------------------------- Utilities -----------------------------------------------
/**
* Removes coverage build artifacts, kills testrpc.
* Exits (1) and prints msg on error, exits (0) otherwise.
* @param {String} err error message
*/
function cleanUp(err) {
log('Cleaning up...');
shell.config.silent = true;
shell.rm('-Rf', `${coverageDir}`);
shell.rm('./allFiredEvents');
if (testrpcProcess) { testrpcProcess.kill(); }
if (err) {
log(`${err}\nExiting without generating coverage...`);
process.exit(1);
} else {
process.exit(0);
}
}

@ -6,27 +6,27 @@ injector.callEvent = function injectCallEvent(contract, fileName, injectionPoint
const linecount = (contract.instrumented.slice(0, injectionPoint).match(/\n/g) || []).length + 1; const linecount = (contract.instrumented.slice(0, injectionPoint).match(/\n/g) || []).length + 1;
contract.runnableLines.push(linecount); contract.runnableLines.push(linecount);
contract.instrumented = contract.instrumented.slice(0, injectionPoint) + contract.instrumented = contract.instrumented.slice(0, injectionPoint) +
'Coverage(\'' + fileName + '\',' + linecount + ');\n' + '__Coverage' + contract.contractName + '(\'' + fileName + '\',' + linecount + ');\n' +
contract.instrumented.slice(injectionPoint); contract.instrumented.slice(injectionPoint);
}; };
injector.callFunctionEvent = function injectCallFunctionEvent(contract, fileName, injectionPoint, injection) { injector.callFunctionEvent = function injectCallFunctionEvent(contract, fileName, injectionPoint, injection) {
contract.instrumented = contract.instrumented.slice(0, injectionPoint) + contract.instrumented = contract.instrumented.slice(0, injectionPoint) +
'FunctionCoverage(\'' + fileName + '\',' + injection.fnId + ');\n' + '__FunctionCoverage' + contract.contractName + '(\'' + fileName + '\',' + injection.fnId + ');\n' +
contract.instrumented.slice(injectionPoint); contract.instrumented.slice(injectionPoint);
}; };
injector.callBranchEvent = function injectCallFunctionEvent(contract, fileName, injectionPoint, injection) { injector.callBranchEvent = function injectCallFunctionEvent(contract, fileName, injectionPoint, injection) {
contract.instrumented = contract.instrumented.slice(0, injectionPoint) + contract.instrumented = contract.instrumented.slice(0, injectionPoint) +
(injection.openBracket ? '{' : '') + (injection.openBracket ? '{' : '') +
'BranchCoverage(\'' + fileName + '\',' + injection.branchId + ',' + injection.locationIdx + ')' + '__BranchCoverage' + contract.contractName + '(\'' + fileName + '\',' + injection.branchId + ',' + injection.locationIdx + ')' +
(injection.comma ? ',' : ';') + (injection.comma ? ',' : ';') +
contract.instrumented.slice(injectionPoint); contract.instrumented.slice(injectionPoint);
}; };
injector.callEmptyBranchEvent = function injectCallEmptyBranchEvent(contract, fileName, injectionPoint, injection) { injector.callEmptyBranchEvent = function injectCallEmptyBranchEvent(contract, fileName, injectionPoint, injection) {
contract.instrumented = contract.instrumented.slice(0, injectionPoint) + contract.instrumented = contract.instrumented.slice(0, injectionPoint) +
'else { BranchCoverage(\'' + fileName + '\',' + injection.branchId + ',' + injection.locationIdx + ');}\n' + 'else { __BranchCoverage' + contract.contractName + '(\'' + fileName + '\',' + injection.branchId + ',' + injection.locationIdx + ');}\n' +
contract.instrumented.slice(injectionPoint); contract.instrumented.slice(injectionPoint);
}; };
@ -44,16 +44,16 @@ injector.literal = function injectLiteral(contract, fileName, injectionPoint, in
injector.statement = function injectStatement(contract, fileName, injectionPoint, injection) { injector.statement = function injectStatement(contract, fileName, injectionPoint, injection) {
contract.instrumented = contract.instrumented.slice(0, injectionPoint) + contract.instrumented = contract.instrumented.slice(0, injectionPoint) +
' StatementCoverage(\'' + fileName + '\',' + injection.statementId + ');\n' + ' __StatementCoverage' + contract.contractName + '(\'' + fileName + '\',' + injection.statementId + ');\n' +
contract.instrumented.slice(injectionPoint); contract.instrumented.slice(injectionPoint);
}; };
injector.eventDefinition = function injectEventDefinition(contract, fileName, injectionPoint, injection) { injector.eventDefinition = function injectEventDefinition(contract, fileName, injectionPoint, injection) {
contract.instrumented = contract.instrumented.slice(0, injectionPoint) + contract.instrumented = contract.instrumented.slice(0, injectionPoint) +
'event Coverage(string fileName, uint256 lineNumber);\n' + 'event __Coverage' + contract.contractName + '(string fileName, uint256 lineNumber);\n' +
'event FunctionCoverage(string fileName, uint256 fnId);\n' + 'event __FunctionCoverage' + contract.contractName + '(string fileName, uint256 fnId);\n' +
'event StatementCoverage(string fileName, uint256 statementId);\n' + 'event __StatementCoverage' + contract.contractName + '(string fileName, uint256 statementId);\n' +
'event BranchCoverage(string fileName, uint256 branchId, uint256 locationIdx);\n' + 'event __BranchCoverage' + contract.contractName + '(string fileName, uint256 branchId, uint256 locationIdx);\n' +
contract.instrumented.slice(injectionPoint); contract.instrumented.slice(injectionPoint);
}; };

@ -38,8 +38,12 @@ module.exports = function instrumentSolidity(contractSource, fileName) {
contract.preprocessed = preprocessor.run(contract.source); contract.preprocessed = preprocessor.run(contract.source);
contract.instrumented = contract.preprocessed; contract.instrumented = contract.preprocessed;
ast = SolidityParser.parse(contract.preprocessed); ast = SolidityParser.parse(contract.preprocessed);
const contractStatement = ast.body.filter(node => node.type === 'ContractStatement');
contract.contractName = contractStatement[0].name;
parse[ast.type](contract, ast); parse[ast.type](contract, ast);
// var result = solparse.parse(contract); // var result = solparse.parse(contract);
@ -59,5 +63,7 @@ module.exports = function instrumentSolidity(contractSource, fileName) {
}); });
retValue.runnableLines = contract.runnableLines; retValue.runnableLines = contract.runnableLines;
retValue.contract = contract.instrumented; retValue.contract = contract.instrumented;
retValue.contractName = contractStatement[0].name;
return retValue; return retValue;
}; };

@ -18,16 +18,16 @@
"commander": "^2.9.0", "commander": "^2.9.0",
"ethereumjs-testrpc": "https://github.com/sc-forks/testrpc-sc.git", "ethereumjs-testrpc": "https://github.com/sc-forks/testrpc-sc.git",
"istanbul": "^0.4.5", "istanbul": "^0.4.5",
"keccakjs": "^0.2.1",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"req-cwd": "^1.0.1", "req-cwd": "^1.0.1",
"shelljs": "^0.7.4", "shelljs": "^0.7.4",
"sol-explore": "^1.6.2", "sol-explore": "^1.6.2",
"solidity-parser": "0.3.0", "solidity-parser": "0.3.0"
"truffle": "git+https://github.com/cgewecke/truffle.git"
}, },
"devDependencies": { "devDependencies": {
"crypto-js": "^3.1.9-1", "crypto-js": "^3.1.9-1",
"eslint": "^3.13.1", "eslint": "^3.19.0",
"eslint-config-airbnb-base": "^11.0.1", "eslint-config-airbnb-base": "^11.0.1",
"eslint-plugin-import": "^2.2.0", "eslint-plugin-import": "^2.2.0",
"eslint-plugin-mocha": "^4.8.0", "eslint-plugin-mocha": "^4.8.0",
@ -37,6 +37,6 @@
"istanbul": "^0.4.5", "istanbul": "^0.4.5",
"merkle-patricia-tree": "~2.1.2", "merkle-patricia-tree": "~2.1.2",
"mocha": "^3.1.0", "mocha": "^3.1.0",
"solc": "0.4.6" "solc": "0.4.8"
} }
} }

@ -1,6 +1,5 @@
/* eslint-env node, mocha */ /* eslint-env node, mocha */
const solc = require('solc');
const path = require('path'); const path = require('path');
const getInstrumentedVersion = require('./../instrumentSolidity.js'); const getInstrumentedVersion = require('./../instrumentSolidity.js');
const util = require('./util/util.js'); const util = require('./util/util.js');
@ -159,9 +158,10 @@ describe('conditional statements', () => {
}); });
// Solcover has trouble with this case. The conditional coverage strategy relies on being able to // Solcover has trouble with this case. The conditional coverage strategy relies on being able to
// reference the left-hand variable before its value is assigned. Solidity doesn't allow this for 'var'. // reference the left-hand variable before its value is assigned. Solidity doesn't allow this
// for 'var'.
/* it('should cover a variable delcaration assignment by conditional that reaches the alternate', (done) => { /* it('should cover a var decl assignment by conditional that reaches the alternate', (done) => {
const contract = util.getCode('conditional/variable-decl-assignment-alternate.sol'); const contract = util.getCode('conditional/variable-decl-assignment-alternate.sol');
const info = getInstrumentedVersion(contract, filePath); const info = getInstrumentedVersion(contract, filePath);
const coverage = new CoverageMap(); const coverage = new CoverageMap();

@ -3,10 +3,7 @@
const solc = require('solc'); const solc = require('solc');
const getInstrumentedVersion = require('./../instrumentSolidity.js'); const getInstrumentedVersion = require('./../instrumentSolidity.js');
const util = require('./util/util.js'); const util = require('./util/util.js');
const CoverageMap = require('./../coverageMap');
const path = require('path'); const path = require('path');
const vm = require('./util/vm');
const assert = require('assert');
/** /**
* NB: passing '1' to solc as an option activates the optimiser * NB: passing '1' to solc as an option activates the optimiser
@ -15,7 +12,6 @@ const assert = require('assert');
*/ */
describe('generic expressions', () => { describe('generic expressions', () => {
const filePath = path.resolve('./test.sol'); const filePath = path.resolve('./test.sol');
const pathPrefix = './';
it('should compile after instrumenting a single binary expression', () => { it('should compile after instrumenting a single binary expression', () => {
const contract = util.getCode('expressions/single-binary-expression.sol'); const contract = util.getCode('expressions/single-binary-expression.sol');

@ -1,6 +1,5 @@
/* eslint-env node, mocha */ /* eslint-env node, mocha */
const solc = require('solc');
const path = require('path'); const path = require('path');
const getInstrumentedVersion = require('./../instrumentSolidity.js'); const getInstrumentedVersion = require('./../instrumentSolidity.js');
const util = require('./util/util.js'); const util = require('./util/util.js');

@ -1,6 +1,5 @@
/* eslint-env node, mocha */ /* eslint-env node, mocha */
const solc = require('solc');
const path = require('path'); const path = require('path');
const getInstrumentedVersion = require('./../instrumentSolidity.js'); const getInstrumentedVersion = require('./../instrumentSolidity.js');
const util = require('./util/util.js'); const util = require('./util/util.js');

@ -1,3 +1,5 @@
/* eslint-env node, mocha */
const assert = require('assert'); const assert = require('assert');
const shell = require('shelljs'); const shell = require('shelljs');
const fs = require('fs'); const fs = require('fs');
@ -14,13 +16,12 @@ function collectGarbage() {
describe('run', () => { describe('run', () => {
let testrpcProcess = null; let testrpcProcess = null;
let script = 'node ./exec.js' const script = 'node ./exec.js';
let launchTestRpc = false; const port = 8555;
let port = 8555;
config = { const config = {
dir: "./mock", dir: './mock',
port: port, port,
testing: true, testing: true,
silent: true, // <-- Set to false to debug tests silent: true, // <-- Set to false to debug tests
norpc: true, norpc: true,
@ -38,20 +39,20 @@ describe('run', () => {
after(() => { after(() => {
mock.restoreCoverage(); mock.restoreCoverage();
//testrpcProcess.kill(); testrpcProcess.kill();
}); });
// This pre-test flushes the suite. There's some kind of sequencing issue here in development, // This pre-test flushes the suite. There's some kind of sequencing issue here in development,
// possibly tied to the use of ethereumjs-vm in the coverage tests? // possibly tied to the use of ethereumjs-vm in the coverage tests?
// - tests pass w/out this if we only run these test - e.g. it only fails when running the suite. // - tests pass w/out this if we only run these test - e.g. it only fails when running the suite.
// - the first test always fails unless there is a fresh testrpc install. // - the first test always fails unless there is a fresh testrpc install.
it('flush test suite', () => { it('flush test suite', () => {
mock.install('Simple.sol', 'simple.js', config); mock.install('Simple.sol', 'simple.js', config);
shell.exec(script); // <---- This fails mysteriously, but we don't test here. shell.exec(script); // <---- This fails mysteriously, but we don't test here.
collectGarbage(); collectGarbage();
}); });
// This test should be positioned first (or second if flushing) in the suite because of // This test should be positioned first (or second if flushing) in the suite because of
// the way we're launching testrpc // the way we're launching testrpc
it('simple contract: should generate coverage, cleanup & exit(0)', () => { it('simple contract: should generate coverage, cleanup & exit(0)', () => {
// Directory should be clean // Directory should be clean
@ -95,7 +96,7 @@ describe('run', () => {
}); });
it('contract uses inheritance: should generate coverage, cleanup & exit(0)', () => { it('contract uses inheritance: should generate coverage, cleanup & exit(0)', () => {
// Run against a contract that 'is' another contract // Run against a contract that 'is' another contract
assert(pathExists('./coverage') === false, 'should start without: coverage'); assert(pathExists('./coverage') === false, 'should start without: coverage');
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json'); assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
mock.installInheritanceTest(config); mock.installInheritanceTest(config);
@ -109,7 +110,7 @@ describe('run', () => {
const produced = JSON.parse(fs.readFileSync('./coverage.json', 'utf8')); const produced = JSON.parse(fs.readFileSync('./coverage.json', 'utf8'));
const ownedPath = Object.keys(produced)[0]; const ownedPath = Object.keys(produced)[0];
const proxyPath = Object.keys(produced)[1]; const proxyPath = Object.keys(produced)[1];
assert(produced[ownedPath].fnMap['1'].name === 'Owned', 'coverage.json should map "Owned"'); assert(produced[ownedPath].fnMap['1'].name === 'Owned', 'coverage.json should map "Owned"');
assert(produced[proxyPath].fnMap['1'].name === 'isOwner', 'coverage.json should map "isOwner"'); assert(produced[proxyPath].fnMap['1'].name === 'isOwner', 'coverage.json should map "isOwner"');
collectGarbage(); collectGarbage();

@ -1,6 +1,7 @@
/* eslint-env node, mocha */
/* global artifacts, contract */
const Expensive = artifacts.require('./Expensive.sol'); const Expensive = artifacts.require('./Expensive.sol');
contract('Expensive', accounts => { contract('Expensive', () => {
it('should deploy', () => Expensive.deployed()); it('should deploy', () => Expensive.deployed());
}); });

@ -1,6 +1,8 @@
/* eslint-env node, mocha */
/* global artifacts, contract */
const Empty = artifacts.require('./Empty.sol'); const Empty = artifacts.require('./Empty.sol');
contract('Empty', accounts => { contract('Empty', () => {
it('should deploy', () => Empty.deployed()); it('should deploy', () => Empty.deployed());
}); });

@ -1,12 +1,14 @@
/* eslint-env node, mocha */
/* global artifacts, contract, assert */
const Owned = artifacts.require('./Owned.sol'); const Owned = artifacts.require('./Owned.sol');
const Proxy = artifacts.require('./Proxy.sol'); const Proxy = artifacts.require('./Proxy.sol');
contract('Proxy', accounts => { contract('Proxy', accounts => {
it('Should compile and run when one contract inherits from another', () => { it('Should compile and run when one contract inherits from another', () => Owned.deployed()
let proxy; .then(() => Proxy.deployed())
return Owned.deployed() .then(instance => instance.isOwner.call({
.then(instance => Proxy.deployed()) from: accounts[0],
.then(instance => instance.isOwner.call({from: accounts[0]})) }))
.then(val => assert.equal(val, true)); .then(val => assert.equal(val, true)));
}); });
});

@ -1,13 +1,17 @@
/* eslint-env node, mocha */
/* global artifacts, contract, assert */
const OnlyCall = artifacts.require('./OnlyCall.sol'); const OnlyCall = artifacts.require('./OnlyCall.sol');
contract('OnlyCall', accounts => { contract('OnlyCall', accounts => {
it('should return val + 2', function(done){ it('should return val + 2', done => {
OnlyCall.deployed().then(function(instance){ OnlyCall.deployed().then(instance => {
instance.addTwo.call(5, {from: accounts[0]}).then(function(val){ instance.addTwo.call(5, {
assert.equal(val, 7); from: accounts[0],
done(); }).then(val => {
}) assert.equal(val, 7);
}) done();
});
});
}); });
}); });

@ -1,14 +1,16 @@
/* eslint-env node, mocha */
/* global artifacts, contract, assert */
const Simple = artifacts.require('./Simple.sol'); const Simple = artifacts.require('./Simple.sol');
contract('Simple', accounts => { contract('Simple', () => {
it('should set x to 5', () => { it('should set x to 5', () => {
let simple; let simple;
return Simple.deployed().then(instance => { return Simple.deployed().then(instance => {
simple = instance; simple = instance;
return simple.test(5); return simple.test(5);
}) })
.then(val => simple.getX.call()) .then(() => simple.getX.call())
.then(val => assert.equal(val.toNumber(), 5)); .then(val => assert.equal(val.toNumber(), 5));
}); });
}); });

@ -1,16 +1,17 @@
/* eslint-env node, mocha */
/* global artifacts, contract, assert */
const Simple = artifacts.require('./Simple.sol'); const Simple = artifacts.require('./Simple.sol');
// This test is constructed correctly but the SimpleError.sol has a syntax error // This test is constructed correctly but the SimpleError.sol has a syntax error
contract('SimpleError', accounts => { contract('SimpleError', () => {
it('should set x to 5', () => { it('should set x to 5', () => {
let simple; let simple;
return Simple.deployed().then(instance => { return Simple.deployed().then(instance => {
simple = instance; simple = instance;
return simple.test(5); return simple.test(5);
}) })
.then(val => simple.getX.call()) .then(() => simple.getX.call())
.then(val => assert.equal(val, 5)); .then(val => assert.equal(val, 5));
}); });
}); });

@ -1,9 +1,10 @@
'use strict' /* eslint-env node, mocha */
/* global artifacts, contract */
var Simple = artifacts.require('./Simple.sol'); var Simple = artifacts.require('./Simple.sol');
// This test should break truffle because it has a syntax error. // This test should break truffle because it has a syntax error.
contract('Simple', function(accounts){ contract('Simple', () => {
it('should crash', function(){ it('should crash', function(){
return Simple.deployed().then.why. return Simple.deployed().then.why.
}) })

@ -1,15 +1,16 @@
/* eslint-env node, mocha */
/* global artifacts, contract, assert */
const Simple = artifacts.require('./Simple.sol'); const Simple = artifacts.require('./Simple.sol');
contract('Simple', accounts => { contract('Simple', () => {
it('should set x to 5', () => { it('should set x to 5', () => {
let simple; let simple;
return Simple.deployed().then(instance => { return Simple.deployed().then(instance => {
simple = instance; simple = instance;
return simple.test(5); return simple.test(5);
}) })
.then(val => simple.getX.call()) .then(() => simple.getX.call())
.then(val => assert.equal(val.toNumber(), 4)); // <-- Wrong result: test fails .then(val => assert.equal(val.toNumber(), 4)); // <-- Wrong result: test fails
}); });
}); });

@ -3,7 +3,6 @@
This file contains utilities for generating a mock truffle project to test solcover's This file contains utilities for generating a mock truffle project to test solcover's
run script against. run script against.
*/ */
const assert = require('assert');
const fs = require('fs'); const fs = require('fs');
const shell = require('shelljs'); const shell = require('shelljs');
@ -11,7 +10,7 @@ const shell = require('shelljs');
* Moves existing coverage reports into a safe place while testing run script which * Moves existing coverage reports into a safe place while testing run script which
* would overwrite them. Silences shell complaints about non-existent files. * would overwrite them. Silences shell complaints about non-existent files.
*/ */
module.exports.protectCoverage = function () { module.exports.protectCoverage = function protectCoverage() {
shell.config.silent = true; shell.config.silent = true;
shell.rm('-Rf', './safe'); shell.rm('-Rf', './safe');
shell.mkdir('./safe'); shell.mkdir('./safe');
@ -24,7 +23,7 @@ module.exports.protectCoverage = function () {
* Restores pre-existing coverage reports after testing run script. * Restores pre-existing coverage reports after testing run script.
* Silences shell complaints about non-existent files. * Silences shell complaints about non-existent files.
*/ */
module.exports.restoreCoverage = function () { module.exports.restoreCoverage = function restoreCoverage() {
shell.config.silent = true; shell.config.silent = true;
shell.mv('./safe/coverage', './coverage'); shell.mv('./safe/coverage', './coverage');
shell.mv('./safe/coverage.json', './coverage.json'); shell.mv('./safe/coverage.json', './coverage.json');
@ -38,17 +37,17 @@ module.exports.restoreCoverage = function () {
* @param {String} contract <contractName.sol> located in /test/sources/run/ * @param {String} contract <contractName.sol> located in /test/sources/run/
* @param {[type]} test <testName.js> located in /test/run/ * @param {[type]} test <testName.js> located in /test/run/
*/ */
module.exports.install = function (contract, test, config) { module.exports.install = function install(contract, test, config) {
shell.mkdir('./mock'); shell.mkdir('./mock');
shell.mkdir('./mock/contracts'); shell.mkdir('./mock/contracts');
shell.mkdir('./mock/migrations'); shell.mkdir('./mock/migrations');
shell.mkdir('./mock/test'); shell.mkdir('./mock/test');
// Mock contracts // Mock contracts
if (Array.isArray(contract)){ if (Array.isArray(contract)) {
contract.forEach(item => { contract.forEach(item => {
shell.cp(`./test/sources/run/${item}`, `./mock/contracts/${item}`); shell.cp(`./test/sources/run/${item}`, `./mock/contracts/${item}`);
}) });
} else { } else {
shell.cp(`./test/sources/run/${contract}`, `./mock/contracts/${contract}`); shell.cp(`./test/sources/run/${contract}`, `./mock/contracts/${contract}`);
} }
@ -62,7 +61,7 @@ module.exports.install = function (contract, test, config) {
deployer.deploy(Migrations); deployer.deploy(Migrations);
};`; };`;
const contractLocation = './' + contract; const contractLocation = `./${contract}`;
const deployContracts = ` const deployContracts = `
var contract = artifacts.require('${contractLocation}'); var contract = artifacts.require('${contractLocation}');
module.exports = function(deployer) { module.exports = function(deployer) {
@ -97,15 +96,15 @@ module.exports.install = function (contract, test, config) {
* @param {String} contract <contractName.sol> located in /test/sources/run/ * @param {String} contract <contractName.sol> located in /test/sources/run/
* @param {[type]} test <testName.js> located in /test/run/ * @param {[type]} test <testName.js> located in /test/run/
*/ */
module.exports.installInheritanceTest = function (config) { module.exports.installInheritanceTest = function installInheritanceTest(config) {
shell.mkdir('./mock'); shell.mkdir('./mock');
shell.mkdir('./mock/contracts'); shell.mkdir('./mock/contracts');
shell.mkdir('./mock/migrations'); shell.mkdir('./mock/migrations');
shell.mkdir('./mock/test'); shell.mkdir('./mock/test');
// Mock contracts // Mock contracts
shell.cp(`./test/sources/run/Proxy.sol`, `./mock/contracts/Proxy.sol`); shell.cp('./test/sources/run/Proxy.sol', './mock/contracts/Proxy.sol');
shell.cp(`./test/sources/run/Owned.sol`, `./mock/contracts/Owned.sol`); shell.cp('./test/sources/run/Owned.sol', './mock/contracts/Owned.sol');
shell.cp('./test/sources/run/Migrations.sol', './mock/contracts/Migrations.sol'); shell.cp('./test/sources/run/Migrations.sol', './mock/contracts/Migrations.sol');
// Mock migrations // Mock migrations
@ -128,7 +127,7 @@ module.exports.installInheritanceTest = function (config) {
fs.writeFileSync('./mock/migrations/2_deploy_contracts.js', deployContracts); fs.writeFileSync('./mock/migrations/2_deploy_contracts.js', deployContracts);
// Mock test // Mock test
shell.cp(`./test/run/inheritance.js`, `./mock/test/inheritance.js`); shell.cp('./test/run/inheritance.js', './mock/test/inheritance.js');
// Mock truffle.js // Mock truffle.js
const trufflejs = `module.exports = { const trufflejs = `module.exports = {
@ -149,11 +148,11 @@ module.exports.installInheritanceTest = function (config) {
/** /**
* Removes mock truffle project and coverage reports generated by runCovered tests * Removes mock truffle project and coverage reports generated by runCovered tests
*/ */
module.exports.remove = function () { module.exports.remove = function remove() {
shell.config.silent = true; shell.config.silent = true;
shell.rm('./.solcover.js'); shell.rm('./.solcover.js');
shell.rm('-Rf', 'mock'); shell.rm('-Rf', 'mock');
shell.rm('-Rf', 'coverage'); shell.rm('-Rf', 'coverage');
shell.rm('coverage.json'); shell.rm('coverage.json');
shell.config.silent = false; shell.config.silent = false;
}; };

@ -7,11 +7,11 @@ const path = require('path');
* @return {String} contents of a .sol file * @return {String} contents of a .sol file
*/ */
module.exports.getCode = function getCode(_path) { module.exports.getCode = function getCode(_path) {
return fs.readFileSync(path.join(__dirname, './../sources/' + _path), 'utf8'); return fs.readFileSync(path.join(__dirname, `./../sources/${_path}`), 'utf8');
}; };
module.exports.report = function report(errors) { module.exports.report = function report(errors) {
if (errors) { if (errors) {
throw new Error('Instrumented solidity invalid: ' + errors); throw new Error(`Instrumented solidity invalid: ${errors}`);
} }
}; };

@ -1,5 +1,4 @@
const solc = require('solc'); const solc = require('solc');
const path = require('path');
const VM = require('ethereumjs-vm'); const VM = require('ethereumjs-vm');
const Account = require('ethereumjs-account'); const Account = require('ethereumjs-account');
const Transaction = require('ethereumjs-tx'); const Transaction = require('ethereumjs-tx');
@ -17,12 +16,12 @@ const accountAddress = new Buffer('7caf6f9bc8b3ba5c7824f934c826bd6dc38c8467', 'h
* Source: consensys/eth-lightwallet/lib/txutils.js (line 18) * Source: consensys/eth-lightwallet/lib/txutils.js (line 18)
*/ */
function encodeFunctionTxData(functionName, types, args) { function encodeFunctionTxData(functionName, types, args) {
const fullName = functionName + '(' + types.join() + ')'; const fullName = `${functionName}(${types.join()})`;
const signature = CryptoJS.SHA3(fullName, { const signature = CryptoJS.SHA3(fullName, {
outputLength: 256, outputLength: 256,
}).toString(CryptoJS.enc.Hex).slice(0, 8); }).toString(CryptoJS.enc.Hex).slice(0, 8);
const dataHex = signature + coder.encodeParams(types, args); const dataHex = signature + coder.encodeParams(types, args);
return '0x' + dataHex; return `0x${dataHex}`;
} }
/** /**
@ -112,7 +111,7 @@ function callMethod(vm, abi, address, functionName, args) {
const tx = new Transaction(options); const tx = new Transaction(options);
tx.sign(new Buffer(secretKey, 'hex')); tx.sign(new Buffer(secretKey, 'hex'));
return new Promise((resolve, reject) => { return new Promise(resolve => {
vm.runTx({ vm.runTx({
tx, tx,
}, (err, results) => { }, (err, results) => {

Loading…
Cancel
Save