Skip to content

Commit 986eead

Browse files
committed
Do not require tests to use .call()
1 parent 7ae7f54 commit 986eead

File tree

6 files changed

+62
-5
lines changed

6 files changed

+62
-5
lines changed

bin/exec.js

100644100755
File mode changed.

lib/app.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class App {
4545
this.copyPackages = config.copyPackages || []; // Only copy specific node_modules packages into coverageEnv
4646
this.testrpcOptions = config.testrpcOptions || null; // Options for testrpc-sc
4747
this.testCommand = config.testCommand || null; // Optional test command
48+
this.testCommand = config.compileCommand || null; // Optional compile command
4849

4950
this.setLoggingLevel(config.silent);
5051
}
@@ -207,6 +208,29 @@ class App {
207208
}
208209
}
209210

211+
/**
212+
* Run truffle compile (or config.compileCommand) over instrumented contracts in the
213+
* coverage environment folder. Shell cd command needs to be invoked
214+
* as its own statement for command line options to work, apparently.
215+
*/
216+
runCompileCommand() {
217+
try {
218+
const defaultCommand = `truffle compile ${this.network} ${this.silence}`;
219+
const command = this.compileCommand || defaultCommand;
220+
this.log(`Running: ${command}\n(this can take a few seconds)...`);
221+
shell.cd(this.coverageDir);
222+
shell.exec(command);
223+
this.testsErrored = shell.error();
224+
shell.cd('./..');
225+
} catch (err) {
226+
const msg =
227+
`
228+
There was an error compiling the contracts.
229+
`;
230+
this.cleanUp(msg + err);
231+
}
232+
}
233+
210234
/**
211235
* Generate coverage / write coverage report / run istanbul
212236
*/
@@ -305,6 +329,24 @@ class App {
305329
fs.writeFileSync(contractPath, contractProcessed);
306330
}
307331
});
332+
// First, compile the instrumented contracts
333+
this.runCompileCommand();
334+
// Now, run through the generated ABIs and reset all pure/view/constant functions
335+
// so that truffle etc uses 'call' on them.
336+
for (let i = 0; i < Object.keys(this.coverage.coverage).length; i += 1) {
337+
const canonicalPath = Object.keys(this.coverage.coverage)[i];
338+
const contractName = path.basename(canonicalPath, '.sol');
339+
const abiPath = this.platformNeutralPath(this.coverageDir + '/build/contracts/' + contractName + '.json');
340+
const abi = fs.readFileSync(abiPath);
341+
const abiJson = JSON.parse(abi);
342+
for (let j = 0; j < abiJson.abi.length; j += 1) {
343+
const func = abiJson.abi[j];
344+
if (this.coverage.coverage[canonicalPath].pureFunctionNames.indexOf(func.name) > -1) {
345+
func.constant = true;
346+
}
347+
}
348+
fs.writeFileSync(abiPath, JSON.stringify(abiJson));
349+
}
308350
}
309351

310352
/**

lib/coverageMap.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ module.exports = class CoverageMap {
6666
for (let x = 1; x <= Object.keys(info.statementMap).length; x++) {
6767
this.coverage[canonicalContractPath].s[x] = 0;
6868
}
69+
this.coverage[canonicalContractPath].pureFunctionNames = info.pureFunctionNames;
6970

7071
const keccakhex = (x => {
7172
const hash = new keccak(256); // eslint-disable-line new-cap

lib/instrumentSolidity.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module.exports = function instrumentSolidity(contractSource, fileName) {
1818
contract.statementMap = {};
1919
contract.statementId = 0;
2020
contract.injectionPoints = {};
21+
contract.pureFunctionNames = [];
2122

2223
// First, we run over the original contract to get the source mapping.
2324
let ast = SolidityParser.parse(contract.source);

lib/parse.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ parse.ForStatement = function parseForStatement(contract, expression) {
8181

8282
parse.FunctionDeclaration = function parseFunctionDeclaration(contract, expression) {
8383
parse.Modifiers(contract, expression.modifiers);
84+
if (expression.modifiers) {
85+
for (let i = 0; i < expression.modifiers.length; i++) {
86+
if (['pure', 'constant', 'view'].indexOf(expression.modifiers[i].name) > -1) {
87+
contract.pureFunctionNames.push(expression.name);
88+
}
89+
}
90+
}
8491
if (expression.body) {
8592
instrumenter.instrumentFunctionDeclaration(contract, expression);
8693

test/cli/totallyPure.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,35 +11,41 @@ contract('TotallyPure', accounts => {
1111

1212
it('calls an imported, inherited pure function', async () => {
1313
const instance = await TotallyPure.deployed();
14-
const value = await instance.isPure.call(4, 5);
14+
const value = await instance.isPure(4, 5);
1515
assert.equal(value.toNumber(), 20);
1616
});
1717

1818
it('calls an imported, inherited view function', async () => {
1919
const instance = await TotallyPure.deployed();
20-
const value = await instance.isView.call();
20+
const value = await instance.isView();
2121
assert.equal(value.toNumber(), 5);
2222
});
2323

2424
it('calls an imported, inherited constant function', async () => {
2525
const instance = await TotallyPure.deployed();
26-
const value = await instance.isConstant.call();
26+
const value = await instance.isConstant();
2727
assert.equal(value.toNumber(), 99);
2828
});
2929

3030
it('overrides an imported, inherited abstract pure function', async () => {
3131
const instance = await TotallyPure.deployed();
32-
const value = await instance.bePure.call(4, 5);
32+
const value = await instance.bePure(4, 5);
3333
assert.equal(value.toNumber(), 9);
3434
});
3535

3636
it('overrides an imported, inherited abstract view function', async () => {
3737
const instance = await TotallyPure.deployed();
38-
const value = await instance.beView.call();
38+
const value = await instance.beView();
3939
assert.equal(value.toNumber(), 99);
4040
});
4141

4242
it('overrides an imported, inherited abstract constant function', async () => {
43+
const instance = await TotallyPure.deployed();
44+
const value = await instance.beConstant();
45+
assert.equal(value.toNumber(), 99);
46+
});
47+
48+
it('overrides an imported, inherited abstract constant function, and uses .call()', async () => {
4349
const instance = await TotallyPure.deployed();
4450
const value = await instance.beConstant.call();
4551
assert.equal(value.toNumber(), 99);

0 commit comments

Comments
 (0)