Modify preprocessor for nested conditionals, test w/ oraclize

pull/333/head
cgewecke 5 years ago
parent de233a93b4
commit 1ec36cab12
  1. 32
      lib/preprocessor.js
  2. 32
      test/app.js
  3. 10
      test/cli/oraclize.js
  4. 24
      test/if.js
  5. 44
      test/sources/cli/Oraclize.sol
  6. 10
      test/sources/if/else-if-without-brackets.sol
  7. 7
      test/statements.js
  8. 18
      test/util/mockTruffle.js

@ -2,14 +2,17 @@ const SolExplore = require('sol-explore');
const SolidityParser = require('solidity-parser-antlr');
const crRegex = /[\r\n ]+$/g;
const OPEN = '{';
const CLOSE = '}';
/**
* Splices enclosing brackets into `contract` around `expression`;
* @param {String} contract solidity source
* @param {Object} node AST node to bracket
* @return {String} contract
*/
function blockWrap(contract, expression) {
return contract.slice(0, expression.range[0]) + '{' + contract.slice(expression.range[0], expression.range[1] + 1) + '}' + contract.slice(expression.range[1] + 1);
function insertBrace(contract, item, offset) {
return contract.slice(0,item.pos + offset) + item.type + contract.slice(item.pos + offset)
}
/** Remove 'pure' and 'view' from the function declaration.
@ -47,24 +50,28 @@ module.exports.run = function r(contract) {
try {
const ast = SolidityParser.parse(contract, { range: true });
blocksToWrap = [];
insertions = [];
viewPureToRemove = [];
SolidityParser.visit(ast, {
IfStatement: function(node) {
if (node.trueBody.type !== 'Block') {
blocksToWrap.push(node.trueBody);
} else if (node.falseBody && node.falseBody.type !== 'Block'){
blocksToWrap.push(node.falseBody);
insertions.push({type: OPEN, pos: node.trueBody.range[0]});
insertions.push({type: CLOSE, pos: node.trueBody.range[1] + 1});
} else if ( node.falseBody && node.falseBody.type !== 'Block' ) {
insertions.push({type: OPEN, pos: node.falseBody.range[0]});
insertions.push({type: CLOSE, pos: node.falseBody.range[1] + 1});
}
},
ForStatement: function(node){
if (node.body.type !== 'Block'){
blocksToWrap.push(node.body);
insertions.push({type: OPEN, pos: node.body.range[0]});
insertions.push({type: CLOSE, pos: node.body.range[1] + 1});
}
},
WhileStatement: function(node){
if (node.body.type !== 'Block'){
blocksToWrap.push(node.body);
insertions.push({type: OPEN, pos: node.body.range[0]});
insertions.push({type: CLOSE, pos: node.body.range[1] + 1});
}
},
FunctionDefinition: function(node){
@ -73,12 +80,13 @@ module.exports.run = function r(contract) {
}
}
})
// Firstly, remove pures and views. Note that we replace 'pure' and 'view' with spaces, so
// Firstly, remove pures and views. Note that we replace 'pure' and 'view' with spaces, so
// character counts remain the same, so we can do this in any order
viewPureToRemove.forEach(node => contract = removePureView(contract, node));
// We apply the blocks we found in reverse order to avoid extra characters messing things up.
blocksToWrap.sort((a,b) => a.range[0] < b.range[0]);
blocksToWrap.forEach(block => contract = blockWrap(contract, block))
// Sort the insertion points.
insertions.sort((a,b) => a.pos - b.pos);
insertions.forEach((item, idx) => contract = insertBrace(contract, item, idx));
} catch (err) {
contract = err;
keepRunning = false;

@ -28,7 +28,7 @@ describe('app', () => {
};
before(done => {
const command = `./node_modules/.bin/testrpc-sc --gasLimit 0xfffffffffff --port ${port}`;
const command = `./node_modules/.bin/testrpc-sc --allowUnlimitedContractSize --gasLimit 0xfffffffffff --port ${port}`;
testrpcProcess = childprocess.exec(command);
testrpcProcess.stdout.on('data', data => {
@ -194,6 +194,36 @@ describe('app', () => {
}
});
it('large contract w/ many unbracketed statements (Oraclize)', () => {
const trufflejs =
`module.exports = {
networks: {
coverage: {
host: "localhost",
network_id: "*",
port: 8555,
gas: 0xfffffffffff,
gasPrice: 0x01
},
},
compilers: {
solc: {
version: "0.4.24",
}
}
};`;
// Directory should be clean
assert(pathExists('./coverage') === false, 'should start without: coverage');
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
// Run script (exits 0);
mock.install('Oraclize.sol', 'oraclize.js', config, trufflejs, null, true);
shell.exec(script);
assert(shell.error() === null, 'script should not error');
});
it('simple contract: should generate coverage, cleanup & exit(0)', () => {
// Directory should be clean
assert(pathExists('./coverage') === false, 'should start without: coverage');

@ -0,0 +1,10 @@
/* eslint-env node, mocha */
/* global artifacts, contract, assert */
const usingOraclize = artifacts.require('usingOraclize');
contract('Nothing', () => {
it('nothing', async () => {
const ora = await usingOraclize.new();
await ora.test();
});
});

@ -186,6 +186,30 @@ describe('if, else, and else if statements', () => {
}).catch(done);
});
it('should cover an else if statement with an unbracketed alternate', done => {
const contract = util.getCode('if/else-if-without-brackets.sol');
const info = getInstrumentedVersion(contract, filePath);
const coverage = new CoverageMap();
coverage.addContract(info, filePath);
vm.execute(info.contract, 'a', [2]).then(events => {
const mapping = coverage.generate(events, pathPrefix);
assert.deepEqual(mapping[filePath].l, {
5: 1, 6: 0, 8: 0,
});
assert.deepEqual(mapping[filePath].b, {
1: [0, 1], 2: [0, 1]
});
assert.deepEqual(mapping[filePath].s, {
1: 1, 2: 0, 3: 1, 4: 0
});
assert.deepEqual(mapping[filePath].f, {
1: 1,
});
done();
}).catch(done);
});
it('should cover nested if statements with missing else statements', done => {
const contract = util.getCode('if/nested-if-missing-else.sol');
const info = getInstrumentedVersion(contract, filePath);

@ -1,4 +1,3 @@
pragma solidity ^0.4.0;
// <ORACLIZE_API>
/*
Copyright (c) 2015-2016 Oraclize SRL
@ -30,7 +29,7 @@ THE SOFTWARE.
*/
// This api is currently targeted at 0.4.18, please import oraclizeAPI_pre0.4.sol or oraclizeAPI_0.4 where necessary
pragma solidity ^0.4.18;
pragma solidity ^0.4.24;
contract OraclizeI {
address public cbAddress;
@ -44,7 +43,7 @@ contract OraclizeI {
function getPrice(string _datasource, uint gaslimit) public returns (uint _dsprice);
function setProofType(byte _proofType) external;
function setCustomGasPrice(uint _gasPrice) external;
function randomDS_getSessionPubKeyHash() external constant returns(bytes32);
function randomDS_getSessionPubKeyHash() external view returns(bytes32);
}
contract OraclizeAddrResolverI {
function getAddress() public returns (address _addr);
@ -525,7 +524,7 @@ contract usingOraclize {
return oraclize.randomDS_getSessionPubKeyHash();
}
function getCodeSize(address _addr) constant internal returns(uint _size) {
function getCodeSize(address _addr) view internal returns(uint _size) {
assembly {
_size := extcodesize(_addr)
}
@ -551,7 +550,7 @@ contract usingOraclize {
return address(iaddr);
}
function strCompare(string _a, string _b) internal pure returns (int) {
/*function strCompare(string _a, string _b) internal pure returns (int) {
bytes memory a = bytes(_a);
bytes memory b = bytes(_b);
uint minLength = a.length;
@ -567,7 +566,7 @@ contract usingOraclize {
return 1;
else
return 0;
}
}*/
function indexOf(string _haystack, string _needle) internal pure returns (int) {
bytes memory h = bytes(_haystack);
@ -638,8 +637,9 @@ contract usingOraclize {
for (uint i=0; i<bresult.length; i++){
if ((bresult[i] >= 48)&&(bresult[i] <= 57)){
if (decimals){
if (_b == 0) break;
else _b--;
if (_b == 0) {
break;
} else _b--;
}
mint *= 10;
mint += uint(bresult[i]) - 48;
@ -1043,29 +1043,39 @@ contract usingOraclize {
}
function nestingTest(string _a, string _b, string _c, string _d, string _e) {
uint i;
uint k;
uint[] _ba;
uint[] _bb;
uint[] _bc;
uint[] _bd;
uint[] _be;
uint[] babcde;
for (i = 0; i < _bb.length; i++)
for (i = 0; i < _bb.length; i++)
if (i = 0)
if (i == 0)
babcde[k++] = _bb[i];
for (i = 0; i < _bb.length; i++)
for (i = 0; i < _bb.length; i++)
if (i = 0)
while (i)
if (i == 0)
while (true)
babcde[k++] = _bb[i];
for (i = 0; i < _bb.length; i++)
for (i = 0; i < _bb.length; i++)
if (i = 0)
if (i == 0)
babcde[k++] = _bb[i];
for (i = 0; i < _bb.length; i++)
for (i = 0; i < _bb.length; i++)
if (i = 0)
if (i == 0)
babcde[k++] = _bb[i];
for (i = 0; i < _be.length; i++) babcde[k++] = _be[i];
for (uint i = 0; i < _ba.length; i++) babcde[k++] = _ba[i];
for (i = 0; i < _ba.length; i++) babcde[k++] = _ba[i];
for (i = 0; i < _bb.length; i++) babcde[k++] = _bb[i];
for (i = 0; i < _bb.length; i++)
for (i = 0; i < _bb.length; i++)
if (i = 0)
if (i == 0)
babcde[k++] = _bb[i];
for (i = 0; i < _bc.length; i++) babcde[k++] = _bc[i];
for (i = 0; i < _bd.length; i++) babcde[k++] = _bd[i];
@ -1079,5 +1089,9 @@ contract usingOraclize {
return;
}
function test() public {
uint tls = 5;
}
}
// </ORACLIZE_API>

@ -0,0 +1,10 @@
pragma solidity ^0.5.0;
contract Test {
function a(uint x) public {
if (x == 1) {
revert();
} else if (x == 50)
x = 5;
}
}

@ -59,13 +59,6 @@ describe('generic statements', () => {
util.report(output.errors);
});
it.only('should compile after instrumenting many unbracketed statements', () => {
const contract = util.getCode('statements/oraclize-plus.sol');
const info = getInstrumentedVersion(contract, filePath);
const output = JSON.parse(solc.compile(util.codeToCompilerInput(info.contract)));
util.report(output.errors);
});
it('should NOT pass tests if the contract has a compilation error', () => {
const contract = util.getCode('statements/compilation-error.sol');
const info = getInstrumentedVersion(contract, filePath);

@ -27,7 +27,14 @@ const defaultTruffleJs = `module.exports = {
* @param {String} contract <contractName.sol> located in /test/sources/cli/
* @param {[type]} test <testName.js> located in /test/cli/
*/
module.exports.install = function install(contract, test, config, _trufflejs, _trufflejsName) {
module.exports.install = function install(
contract,
test,
config,
_trufflejs,
_trufflejsName,
noMigrations
) {
const configjs = `module.exports = ${JSON.stringify(config)}`;
const contractLocation = `./${contract}`;
const trufflejsName = _trufflejsName || 'truffle.js';
@ -68,9 +75,12 @@ module.exports.install = function install(contract, test, config, _trufflejs, _t
shell.cp(`./test/sources/cli/${contract}`, `./mock/contracts/${contract}`);
}
shell.cp('./test/sources/cli/Migrations.sol', './mock/contracts/Migrations.sol');
fs.writeFileSync('./mock/migrations/1_initial_migration.js', initialMigration);
fs.writeFileSync('./mock/migrations/2_deploy_contracts.js', deployContracts);
if (!noMigrations){
shell.cp('./test/sources/cli/Migrations.sol', './mock/contracts/Migrations.sol');
fs.writeFileSync('./mock/migrations/1_initial_migration.js', initialMigration);
fs.writeFileSync('./mock/migrations/2_deploy_contracts.js', deployContracts);
}
fs.writeFileSync(`./mock/${trufflejsName}`, trufflejs);
fs.writeFileSync('./mock/assets/asset.js', asset);
fs.writeFileSync('./.solcover.js', configjs);

Loading…
Cancel
Save