Merge pull request #333 from sc-forks/test/brackets

Let preprocessor manage nested conditionals
pull/334/head
cgewecke 5 years ago committed by GitHub
commit d71c962f86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 32
      lib/preprocessor.js
  2. 32
      test/app.js
  3. 10
      test/cli/oraclize.js
  4. 24
      test/if.js
  5. 1097
      test/sources/cli/Oraclize.sol
  6. 10
      test/sources/if/else-if-without-brackets.sol
  7. 2
      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);

File diff suppressed because it is too large Load Diff

@ -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,8 +59,6 @@ describe('generic statements', () => {
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