Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not require tests to use .call() #159

Merged
merged 1 commit into from
Nov 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified bin/exec.js
100644 → 100755
Empty file.
42 changes: 42 additions & 0 deletions lib/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class App {
this.copyPackages = config.copyPackages || []; // Only copy specific node_modules packages into coverageEnv
this.testrpcOptions = config.testrpcOptions || null; // Options for testrpc-sc
this.testCommand = config.testCommand || null; // Optional test command
this.testCommand = config.compileCommand || null; // Optional compile command

this.setLoggingLevel(config.silent);
}
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -305,6 +329,24 @@ class App {
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));
}
}

/**
Expand Down
1 change: 1 addition & 0 deletions lib/coverageMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ module.exports = class CoverageMap {
for (let x = 1; x <= Object.keys(info.statementMap).length; x++) {
this.coverage[canonicalContractPath].s[x] = 0;
}
this.coverage[canonicalContractPath].pureFunctionNames = info.pureFunctionNames;

const keccakhex = (x => {
const hash = new keccak(256); // eslint-disable-line new-cap
Expand Down
1 change: 1 addition & 0 deletions lib/instrumentSolidity.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module.exports = function instrumentSolidity(contractSource, fileName) {
contract.statementMap = {};
contract.statementId = 0;
contract.injectionPoints = {};
contract.pureFunctionNames = [];

// First, we run over the original contract to get the source mapping.
let ast = SolidityParser.parse(contract.source);
Expand Down
7 changes: 7 additions & 0 deletions lib/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ parse.ForStatement = function parseForStatement(contract, expression) {

parse.FunctionDeclaration = function parseFunctionDeclaration(contract, expression) {
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) {
instrumenter.instrumentFunctionDeclaration(contract, expression);

Expand Down
16 changes: 11 additions & 5 deletions test/cli/totallyPure.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,35 +11,41 @@ contract('TotallyPure', accounts => {

it('calls an imported, inherited pure function', async () => {
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);
});

it('calls an imported, inherited view function', async () => {
const instance = await TotallyPure.deployed();
const value = await instance.isView.call();
const value = await instance.isView();
assert.equal(value.toNumber(), 5);
});

it('calls an imported, inherited constant function', async () => {
const instance = await TotallyPure.deployed();
const value = await instance.isConstant.call();
const value = await instance.isConstant();
assert.equal(value.toNumber(), 99);
});

it('overrides an imported, inherited abstract pure function', async () => {
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);
});

it('overrides an imported, inherited abstract view function', async () => {
const instance = await TotallyPure.deployed();
const value = await instance.beView.call();
const value = await instance.beView();
assert.equal(value.toNumber(), 99);
});

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 value = await instance.beConstant.call();
assert.equal(value.toNumber(), 99);
Expand Down