Skip to content

Commit d71c962

Browse files
authored
Merge pull request #333 from sc-forks/test/brackets
Let preprocessor manage nested conditionals
2 parents 1b4de69 + 1ec36ca commit d71c962

File tree

8 files changed

+1206
-19
lines changed

8 files changed

+1206
-19
lines changed

lib/preprocessor.js

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@ const SolExplore = require('sol-explore');
22
const SolidityParser = require('solidity-parser-antlr');
33

44
const crRegex = /[\r\n ]+$/g;
5+
const OPEN = '{';
6+
const CLOSE = '}';
7+
58
/**
69
* Splices enclosing brackets into `contract` around `expression`;
710
* @param {String} contract solidity source
811
* @param {Object} node AST node to bracket
912
* @return {String} contract
1013
*/
11-
function blockWrap(contract, expression) {
12-
return contract.slice(0, expression.range[0]) + '{' + contract.slice(expression.range[0], expression.range[1] + 1) + '}' + contract.slice(expression.range[1] + 1);
14+
function insertBrace(contract, item, offset) {
15+
return contract.slice(0,item.pos + offset) + item.type + contract.slice(item.pos + offset)
1316
}
1417

1518
/** Remove 'pure' and 'view' from the function declaration.
@@ -47,24 +50,28 @@ module.exports.run = function r(contract) {
4750

4851
try {
4952
const ast = SolidityParser.parse(contract, { range: true });
50-
blocksToWrap = [];
53+
insertions = [];
5154
viewPureToRemove = [];
5255
SolidityParser.visit(ast, {
5356
IfStatement: function(node) {
5457
if (node.trueBody.type !== 'Block') {
55-
blocksToWrap.push(node.trueBody);
56-
} else if (node.falseBody && node.falseBody.type !== 'Block'){
57-
blocksToWrap.push(node.falseBody);
58+
insertions.push({type: OPEN, pos: node.trueBody.range[0]});
59+
insertions.push({type: CLOSE, pos: node.trueBody.range[1] + 1});
60+
} else if ( node.falseBody && node.falseBody.type !== 'Block' ) {
61+
insertions.push({type: OPEN, pos: node.falseBody.range[0]});
62+
insertions.push({type: CLOSE, pos: node.falseBody.range[1] + 1});
5863
}
5964
},
6065
ForStatement: function(node){
6166
if (node.body.type !== 'Block'){
62-
blocksToWrap.push(node.body);
67+
insertions.push({type: OPEN, pos: node.body.range[0]});
68+
insertions.push({type: CLOSE, pos: node.body.range[1] + 1});
6369
}
6470
},
6571
WhileStatement: function(node){
6672
if (node.body.type !== 'Block'){
67-
blocksToWrap.push(node.body);
73+
insertions.push({type: OPEN, pos: node.body.range[0]});
74+
insertions.push({type: CLOSE, pos: node.body.range[1] + 1});
6875
}
6976
},
7077
FunctionDefinition: function(node){
@@ -73,12 +80,13 @@ module.exports.run = function r(contract) {
7380
}
7481
}
7582
})
76-
// Firstly, remove pures and views. Note that we replace 'pure' and 'view' with spaces, so
83+
// Firstly, remove pures and views. Note that we replace 'pure' and 'view' with spaces, so
7784
// character counts remain the same, so we can do this in any order
7885
viewPureToRemove.forEach(node => contract = removePureView(contract, node));
79-
// We apply the blocks we found in reverse order to avoid extra characters messing things up.
80-
blocksToWrap.sort((a,b) => a.range[0] < b.range[0]);
81-
blocksToWrap.forEach(block => contract = blockWrap(contract, block))
86+
// Sort the insertion points.
87+
insertions.sort((a,b) => a.pos - b.pos);
88+
insertions.forEach((item, idx) => contract = insertBrace(contract, item, idx));
89+
8290
} catch (err) {
8391
contract = err;
8492
keepRunning = false;

test/app.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ describe('app', () => {
2828
};
2929

3030
before(done => {
31-
const command = `./node_modules/.bin/testrpc-sc --gasLimit 0xfffffffffff --port ${port}`;
31+
const command = `./node_modules/.bin/testrpc-sc --allowUnlimitedContractSize --gasLimit 0xfffffffffff --port ${port}`;
3232
testrpcProcess = childprocess.exec(command);
3333

3434
testrpcProcess.stdout.on('data', data => {
@@ -194,6 +194,36 @@ describe('app', () => {
194194
}
195195
});
196196

197+
it('large contract w/ many unbracketed statements (Oraclize)', () => {
198+
const trufflejs =
199+
`module.exports = {
200+
networks: {
201+
coverage: {
202+
host: "localhost",
203+
network_id: "*",
204+
port: 8555,
205+
gas: 0xfffffffffff,
206+
gasPrice: 0x01
207+
},
208+
},
209+
compilers: {
210+
solc: {
211+
version: "0.4.24",
212+
}
213+
}
214+
};`;
215+
216+
// Directory should be clean
217+
assert(pathExists('./coverage') === false, 'should start without: coverage');
218+
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
219+
220+
// Run script (exits 0);
221+
mock.install('Oraclize.sol', 'oraclize.js', config, trufflejs, null, true);
222+
shell.exec(script);
223+
assert(shell.error() === null, 'script should not error');
224+
225+
});
226+
197227
it('simple contract: should generate coverage, cleanup & exit(0)', () => {
198228
// Directory should be clean
199229
assert(pathExists('./coverage') === false, 'should start without: coverage');

test/cli/oraclize.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* eslint-env node, mocha */
2+
/* global artifacts, contract, assert */
3+
const usingOraclize = artifacts.require('usingOraclize');
4+
5+
contract('Nothing', () => {
6+
it('nothing', async () => {
7+
const ora = await usingOraclize.new();
8+
await ora.test();
9+
});
10+
});

test/if.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,30 @@ describe('if, else, and else if statements', () => {
186186
}).catch(done);
187187
});
188188

189+
it('should cover an else if statement with an unbracketed alternate', done => {
190+
const contract = util.getCode('if/else-if-without-brackets.sol');
191+
const info = getInstrumentedVersion(contract, filePath);
192+
const coverage = new CoverageMap();
193+
coverage.addContract(info, filePath);
194+
195+
vm.execute(info.contract, 'a', [2]).then(events => {
196+
const mapping = coverage.generate(events, pathPrefix);
197+
assert.deepEqual(mapping[filePath].l, {
198+
5: 1, 6: 0, 8: 0,
199+
});
200+
assert.deepEqual(mapping[filePath].b, {
201+
1: [0, 1], 2: [0, 1]
202+
});
203+
assert.deepEqual(mapping[filePath].s, {
204+
1: 1, 2: 0, 3: 1, 4: 0
205+
});
206+
assert.deepEqual(mapping[filePath].f, {
207+
1: 1,
208+
});
209+
done();
210+
}).catch(done);
211+
});
212+
189213
it('should cover nested if statements with missing else statements', done => {
190214
const contract = util.getCode('if/nested-if-missing-else.sol');
191215
const info = getInstrumentedVersion(contract, filePath);

0 commit comments

Comments
 (0)