Do not require tests to use .call()

fix/callNotRequired
Alex Rea 7 years ago
parent 7ae7f54ba4
commit 986eeadc1b
  1. 0
      bin/exec.js
  2. 42
      lib/app.js
  3. 1
      lib/coverageMap.js
  4. 1
      lib/instrumentSolidity.js
  5. 7
      lib/parse.js
  6. 16
      test/cli/totallyPure.js

@ -45,6 +45,7 @@ class App {
this.copyPackages = config.copyPackages || []; // Only copy specific node_modules packages into coverageEnv this.copyPackages = config.copyPackages || []; // Only copy specific node_modules packages 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
this.testCommand = config.compileCommand || null; // Optional compile command
this.setLoggingLevel(config.silent); this.setLoggingLevel(config.silent);
} }
@ -207,6 +208,29 @@ class App {
} }
} }
/**
* Run truffle compile (or config.compileCommand) over instrumented contracts in the
* coverage environment folder. Shell cd command needs to be invoked
* as its own statement for command line options to work, apparently.
*/
runCompileCommand() {
try {
const defaultCommand = `truffle compile ${this.network} ${this.silence}`;
const command = this.compileCommand || defaultCommand;
this.log(`Running: ${command}\n(this can take a few seconds)...`);
shell.cd(this.coverageDir);
shell.exec(command);
this.testsErrored = shell.error();
shell.cd('./..');
} catch (err) {
const msg =
`
There was an error compiling the contracts.
`;
this.cleanUp(msg + err);
}
}
/** /**
* Generate coverage / write coverage report / run istanbul * Generate coverage / write coverage report / run istanbul
*/ */
@ -305,6 +329,24 @@ class App {
fs.writeFileSync(contractPath, contractProcessed); fs.writeFileSync(contractPath, contractProcessed);
} }
}); });
// First, compile the instrumented contracts
this.runCompileCommand();
// Now, run through the generated ABIs and reset all pure/view/constant functions
// so that truffle etc uses 'call' on them.
for (let i = 0; i < Object.keys(this.coverage.coverage).length; i += 1) {
const canonicalPath = Object.keys(this.coverage.coverage)[i];
const contractName = path.basename(canonicalPath, '.sol');
const abiPath = this.platformNeutralPath(this.coverageDir + '/build/contracts/' + contractName + '.json');
const abi = fs.readFileSync(abiPath);
const abiJson = JSON.parse(abi);
for (let j = 0; j < abiJson.abi.length; j += 1) {
const func = abiJson.abi[j];
if (this.coverage.coverage[canonicalPath].pureFunctionNames.indexOf(func.name) > -1) {
func.constant = true;
}
}
fs.writeFileSync(abiPath, JSON.stringify(abiJson));
}
} }
/** /**

@ -66,6 +66,7 @@ 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;
} }
this.coverage[canonicalContractPath].pureFunctionNames = info.pureFunctionNames;
const keccakhex = (x => { const keccakhex = (x => {
const hash = new keccak(256); // eslint-disable-line new-cap const hash = new keccak(256); // eslint-disable-line new-cap

@ -18,6 +18,7 @@ module.exports = function instrumentSolidity(contractSource, fileName) {
contract.statementMap = {}; contract.statementMap = {};
contract.statementId = 0; contract.statementId = 0;
contract.injectionPoints = {}; contract.injectionPoints = {};
contract.pureFunctionNames = [];
// First, we run over the original contract to get the source mapping. // First, we run over the original contract to get the source mapping.
let ast = SolidityParser.parse(contract.source); let ast = SolidityParser.parse(contract.source);

@ -81,6 +81,13 @@ parse.ForStatement = function parseForStatement(contract, expression) {
parse.FunctionDeclaration = function parseFunctionDeclaration(contract, expression) { parse.FunctionDeclaration = function parseFunctionDeclaration(contract, expression) {
parse.Modifiers(contract, expression.modifiers); parse.Modifiers(contract, expression.modifiers);
if (expression.modifiers) {
for (let i = 0; i < expression.modifiers.length; i++) {
if (['pure', 'constant', 'view'].indexOf(expression.modifiers[i].name) > -1) {
contract.pureFunctionNames.push(expression.name);
}
}
}
if (expression.body) { if (expression.body) {
instrumenter.instrumentFunctionDeclaration(contract, expression); instrumenter.instrumentFunctionDeclaration(contract, expression);

@ -11,35 +11,41 @@ contract('TotallyPure', accounts => {
it('calls an imported, inherited pure function', async () => { it('calls an imported, inherited pure function', async () => {
const instance = await TotallyPure.deployed(); const instance = await TotallyPure.deployed();
const value = await instance.isPure.call(4, 5); const value = await instance.isPure(4, 5);
assert.equal(value.toNumber(), 20); assert.equal(value.toNumber(), 20);
}); });
it('calls an imported, inherited view function', async () => { it('calls an imported, inherited view function', async () => {
const instance = await TotallyPure.deployed(); const instance = await TotallyPure.deployed();
const value = await instance.isView.call(); const value = await instance.isView();
assert.equal(value.toNumber(), 5); assert.equal(value.toNumber(), 5);
}); });
it('calls an imported, inherited constant function', async () => { it('calls an imported, inherited constant function', async () => {
const instance = await TotallyPure.deployed(); const instance = await TotallyPure.deployed();
const value = await instance.isConstant.call(); const value = await instance.isConstant();
assert.equal(value.toNumber(), 99); assert.equal(value.toNumber(), 99);
}); });
it('overrides an imported, inherited abstract pure function', async () => { it('overrides an imported, inherited abstract pure function', async () => {
const instance = await TotallyPure.deployed(); const instance = await TotallyPure.deployed();
const value = await instance.bePure.call(4, 5); const value = await instance.bePure(4, 5);
assert.equal(value.toNumber(), 9); assert.equal(value.toNumber(), 9);
}); });
it('overrides an imported, inherited abstract view function', async () => { it('overrides an imported, inherited abstract view function', async () => {
const instance = await TotallyPure.deployed(); const instance = await TotallyPure.deployed();
const value = await instance.beView.call(); const value = await instance.beView();
assert.equal(value.toNumber(), 99); assert.equal(value.toNumber(), 99);
}); });
it('overrides an imported, inherited abstract constant function', async () => { it('overrides an imported, inherited abstract constant function', async () => {
const instance = await TotallyPure.deployed();
const value = await instance.beConstant();
assert.equal(value.toNumber(), 99);
});
it('overrides an imported, inherited abstract constant function, and uses .call()', async () => {
const instance = await TotallyPure.deployed(); const instance = await TotallyPure.deployed();
const value = await instance.beConstant.call(); const value = await instance.beConstant.call();
assert.equal(value.toNumber(), 99); assert.equal(value.toNumber(), 99);

Loading…
Cancel
Save