Initial app test cleanup

test/vm-fixtures
cgewecke 5 years ago
parent 66fa5ea7a5
commit 168de45899
  1. 317
      test/units/app.js

@ -1,60 +1,42 @@
/* eslint-env node, mocha */ /* eslint-env node, mocha */
/*const assert = require('assert'); const assert = require('assert');
const shell = require('shelljs'); const shell = require('shelljs');
const fs = require('fs'); const fs = require('fs');
const childprocess = require('child_process'); const childprocess = require('child_process');
const mock = require('./util/mockTruffle.js'); const mock = require('../util/mockTruffle.js');
// shell.test alias for legibility // shell.test alias for legibility
function pathExists(path) { return shell.test('-e', path); } function pathExists(path) { return shell.test('-e', path); }
// tests run out of memory in CI without this function assertCleanInitialState(){
function collectGarbage() { assert(pathExists('./coverage') === false, 'should start without: coverage');
if (global.gc) { global.gc(); } assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
} }
describe('app', () => { function assertCoverageGenerated(){
let testrpcProcess = null; assert(pathExists('./coverage') === true, 'script should gen coverage folder');
const script = 'node ./bin/exec.js'; assert(pathExists('./coverage.json') === true, 'script should gen coverage.json');
const port = 8555;
const config = {
dir: './mock',
port,
testing: true,
silent: true, // <-- Set to false to debug tests
norpc: true,
};
before(done => {
const command = `npx testrpc-sc --gasLimit 0xfffffffffff --port ${port}`;
testrpcProcess = childprocess.exec(command);
testrpcProcess.stdout.on('data', data => {
if (data.includes('Listening')) {
done();
} }
});
});
afterEach(() => { function assertCoverageNotGenerated(){
mock.remove(); assert(pathExists('./coverage') !== true, 'script should NOT gen coverage folder');
}); assert(pathExists('./coverage.json') !== true, 'script should NOT gen coverage.json');
}
after(() => { function assertExecutionSucceeds(){
testrpcProcess.kill();
}); }
function assertExecutionFails(){
}
describe.skip('app', function() {
afterEach(() => mock.remove());
// #1: The 'config' tests ask exec.js to run testrpc on special ports, the subsequent tests use
// the testrpc launched in the before() block. For some reason config tests fail randomly
// unless they are at the top of the suite. Hard to debug since they pass if logging is turned
// on - there might be a timing issue around resource cleanup or something.
//
// #2: Creating repeated instances of testrpc hits the container memory limit on
// CI so these tests are disabled for that context
it('config with testrpc options string: should generate coverage, cleanup & exit(0)', () => { it('config with testrpc options string: should generate coverage, cleanup & exit(0)', () => {
if (!process.env.CI) {
const privateKey = '0x3af46c9ac38ee1f01b05f9915080133f644bf57443f504d339082cb5285ccae4'; const privateKey = '0x3af46c9ac38ee1f01b05f9915080133f644bf57443f504d339082cb5285ccae4';
const balance = '0xfffffffffffffff'; const balance = '0xfffffffffffffff';
const testConfig = Object.assign({}, config); const testConfig = Object.assign({}, config);
@ -69,12 +51,10 @@ describe('app', () => {
mock.install('Simple.sol', 'testrpc-options.js', testConfig); mock.install('Simple.sol', 'testrpc-options.js', testConfig);
shell.exec(script); shell.exec(script);
assert(shell.error() === null, 'script should not error'); assert(shell.error() === null, 'script should not error');
collectGarbage();
}
}); });
it('config with test command options string: should run test', () => { it('config with test command options string: should run test', () => {
if (!process.env.CI) {
assert(pathExists('./allFiredEvents') === false, 'should start without: events log'); assert(pathExists('./allFiredEvents') === false, 'should start without: events log');
const testConfig = Object.assign({}, config); const testConfig = Object.assign({}, config);
@ -88,100 +68,18 @@ describe('app', () => {
mock.install('Simple.sol', 'command-options.js', testConfig); mock.install('Simple.sol', 'command-options.js', testConfig);
shell.exec(script); shell.exec(script);
assert(shell.error() === null, 'script should not error'); assert(shell.error() === null, 'script should not error');
collectGarbage();
}
});
it('config racing test command: should run test after testrpc has started', () => {
if (!process.env.CI) {
assert(pathExists('./allFiredEvents') === false, 'should start without: events log');
const testConfig = Object.assign({}, config);
testConfig.testCommand = 'node ../test/util/mockTestCommand.js';
testConfig.dir = './mock';
testConfig.norpc = false;
testConfig.port = 8888;
// Installed test will write a fake allFiredEvents to ./ after 4000ms
// allowing test to pass
mock.install('Simple.sol', 'command-options.js', testConfig);
shell.exec(script);
assert(shell.error() === null, 'script should not error');
collectGarbage();
}
}); });
it('contract tests events: tests should pass without errors', () => { it('simple contract: should generate coverage, cleanup & exit(0)', () => {
if (!process.env.CI) { assertCleanInitialState();
assert(pathExists('./coverage') === false, 'should start without: coverage');
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
const testConfig = Object.assign({}, config);
testConfig.dir = './mock';
testConfig.norpc = false;
testConfig.port = 8889;
mock.install('Events.sol', 'events.js', testConfig);
shell.exec(script);
assert(shell.error() === null, 'script should not error');
// Directory should have coverage report
assert(pathExists('./coverage') === true, 'script should gen coverage folder');
assert(pathExists('./coverage.json') === true, 'script should gen coverage.json');
// Coverage should be real.
// This test is tightly bound to the function names in Simple.sol
const produced = JSON.parse(fs.readFileSync('./coverage.json', 'utf8'));
const path = Object.keys(produced)[0];
assert(produced[path].fnMap['1'].name === 'test', 'coverage.json should map "test"');
assert(produced[path].fnMap['2'].name === 'getX', 'coverage.json should map "getX"');
collectGarbage();
}
});
it('trufflejs specifies coverage network: should generate coverage, cleanup and exit(0)', () => {
if (!process.env.CI) {
const trufflejs =
`module.exports = {
networks: {
development: {
host: "localhost",
port: 8545,
network_id: "*"
},
coverage: {
host: "localhost",
port: 8999,
network_id: "*",
gas: 0xfffffffffff,
gasPrice: 0x01
}
},
compilers: {
solc: {
version: "0.5.3",
}
}
};`;
const testConfig = Object.assign({}, config);
testConfig.dir = './mock';
testConfig.norpc = false;
testConfig.port = 8555; // Manually inspect that port is actually set to 8999
// 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); // Run script (exits 0);
mock.install('Simple.sol', 'simple.js', testConfig, trufflejs); mock.install('Simple.sol', 'simple.js', config);
shell.exec(script); shell.exec(script);
assert(shell.error() === null, 'script should not error'); assert(shell.error() === null, 'script should not error');
// Directory should have coverage report assertCoverageGenerated();
assert(pathExists('./coverage') === true, 'script should gen coverage folder');
assert(pathExists('./coverage.json') === true, 'script should gen coverage.json');
// Coverage should be real. // Coverage should be real.
// This test is tightly bound to the function names in Simple.sol // This test is tightly bound to the function names in Simple.sol
@ -189,11 +87,10 @@ describe('app', () => {
const path = Object.keys(produced)[0]; const path = Object.keys(produced)[0];
assert(produced[path].fnMap['1'].name === 'test', 'coverage.json should map "test"'); assert(produced[path].fnMap['1'].name === 'test', 'coverage.json should map "test"');
assert(produced[path].fnMap['2'].name === 'getX', 'coverage.json should map "getX"'); assert(produced[path].fnMap['2'].name === 'getX', 'coverage.json should map "getX"');
collectGarbage();
}
}); });
it('large contract w/ many unbracketed statements (Oraclize)', () => { it('Oraclize @ solc v.0.4.24 (large, many unbracketed statements)', () => {
const trufflejs = const trufflejs =
`module.exports = { `module.exports = {
networks: { networks: {
@ -212,9 +109,7 @@ describe('app', () => {
} }
};`; };`;
// Directory should be clean assertCleanInitialState();
assert(pathExists('./coverage') === false, 'should start without: coverage');
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
// Run script (exits 0); // Run script (exits 0);
mock.install('Oraclize.sol', 'oraclize.js', config, trufflejs, null, true); mock.install('Oraclize.sol', 'oraclize.js', config, trufflejs, null, true);
@ -223,73 +118,15 @@ describe('app', () => {
}); });
it('simple contract: should generate coverage, cleanup & exit(0)', () => {
// 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('Simple.sol', 'simple.js', config);
shell.exec(script);
assert(shell.error() === null, 'script should not error');
// Directory should have coverage report
assert(pathExists('./coverage') === true, 'script should gen coverage folder');
assert(pathExists('./coverage.json') === true, 'script should gen coverage.json');
// Coverage should be real.
// This test is tightly bound to the function names in Simple.sol
const produced = JSON.parse(fs.readFileSync('./coverage.json', 'utf8'));
const path = Object.keys(produced)[0];
assert(produced[path].fnMap['1'].name === 'test', 'coverage.json should map "test"');
assert(produced[path].fnMap['2'].name === 'getX', 'coverage.json should map "getX"');
collectGarbage();
});
it('project uses truffle-config.js: should generate coverage, cleanup and exit(0)', () => {
// 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('Simple.sol', 'simple.js', config, null, 'truffle-config.js');
shell.exec(script);
assert(shell.error() === null, 'script should not error');
// Directory should have coverage report
assert(pathExists('./coverage') === true, 'script should gen coverage folder');
assert(pathExists('./coverage.json') === true, 'script should gen coverage.json');
// Coverage should be real.
// This test is tightly bound to the function names in Simple.sol
const produced = JSON.parse(fs.readFileSync('./coverage.json', 'utf8'));
const path = Object.keys(produced)[0];
assert(produced[path].fnMap['1'].name === 'test', 'coverage.json should map "test"');
assert(produced[path].fnMap['2'].name === 'getX', 'coverage.json should map "getX"');
collectGarbage();
});
it('testrpc-sc signs and recovers messages correctly', () => {
// sign.js signs and recovers
mock.install('Simple.sol', 'sign.js', config);
shell.exec(script);
assert(shell.error() === null, 'script should not error');
collectGarbage();
});
it('tests use pure and view modifiers, including with libraries', () => { it('tests use pure and view modifiers, including with libraries', () => {
// Directory should be clean assertCleanInitialState();
assert(pathExists('./coverage') === false, 'should start without: coverage');
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
// Run script (exits 0); // Run script (exits 0);
mock.installLibraryTest(config); mock.installLibraryTest(config);
shell.exec(script); shell.exec(script);
assert(shell.error() === null, 'script should not error'); assert(shell.error() === null, 'script should not error');
// Directory should have coverage report assertCoverageGenerated();
assert(pathExists('./coverage') === true, 'script should gen coverage folder');
assert(pathExists('./coverage.json') === true, 'script should gen coverage.json');
// Coverage should be real. // Coverage should be real.
// This test is tightly bound to the function names in TotallyPure.sol // This test is tightly bound to the function names in TotallyPure.sol
@ -297,22 +134,18 @@ describe('app', () => {
const path = Object.keys(produced)[0]; const path = Object.keys(produced)[0];
assert(produced[path].fnMap['1'].name === 'usesThem', 'coverage.json should map "usesThem"'); assert(produced[path].fnMap['1'].name === 'usesThem', 'coverage.json should map "usesThem"');
assert(produced[path].fnMap['2'].name === 'isPure', 'coverage.json should map "getX"'); assert(produced[path].fnMap['2'].name === 'isPure', 'coverage.json should map "getX"');
collectGarbage();
}); });
it('tests require assets outside of test folder: should generate coverage, cleanup & exit(0)', () => { it('tests require assets outside of test folder: should generate coverage, cleanup & exit(0)', () => {
// Directory should be clean assertCleanInitialState();
assert(pathExists('./coverage') === false, 'should start without: coverage');
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
// Run script (exits 0); // Run script (exits 0);
mock.install('Simple.sol', 'requires-externally.js', config); mock.install('Simple.sol', 'requires-externally.js', config);
shell.exec(script); shell.exec(script);
assert(shell.error() === null, 'script should not error'); assert(shell.error() === null, 'script should not error');
// Directory should have coverage report assertCoverageGenerated();
assert(pathExists('./coverage') === true, 'script should gen coverage folder');
assert(pathExists('./coverage.json') === true, 'script should gen coverage.json');
// Coverage should be real. // Coverage should be real.
// This test is tightly bound to the function names in Simple.sol // This test is tightly bound to the function names in Simple.sol
@ -320,70 +153,59 @@ describe('app', () => {
const path = Object.keys(produced)[0]; const path = Object.keys(produced)[0];
assert(produced[path].fnMap['1'].name === 'test', 'coverage.json should map "test"'); assert(produced[path].fnMap['1'].name === 'test', 'coverage.json should map "test"');
assert(produced[path].fnMap['2'].name === 'getX', 'coverage.json should map "getX"'); assert(produced[path].fnMap['2'].name === 'getX', 'coverage.json should map "getX"');
collectGarbage();
}); });
it('contract only uses .call: should generate coverage, cleanup & exit(0)', () => { it('contract only uses .call: should generate coverage, cleanup & exit(0)', () => {
// Run against contract that only uses method.call. assertCleanInitialState();
assert(pathExists('./coverage') === false, 'should start without: coverage');
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
mock.install('OnlyCall.sol', 'only-call.js', config); mock.install('OnlyCall.sol', 'only-call.js', config);
shell.exec(script); shell.exec(script);
assert(shell.error() === null, 'script should not error'); assert(shell.error() === null, 'script should not error');
assert(pathExists('./coverage') === true, 'script should gen coverage folder'); assertCoverageGenerated();
assert(pathExists('./coverage.json') === true, 'script should gen coverage.json');
const produced = JSON.parse(fs.readFileSync('./coverage.json', 'utf8')); const produced = JSON.parse(fs.readFileSync('./coverage.json', 'utf8'));
const path = Object.keys(produced)[0]; const path = Object.keys(produced)[0];
assert(produced[path].fnMap['1'].name === 'addTwo', 'coverage.json should map "addTwo"'); assert(produced[path].fnMap['1'].name === 'addTwo', 'coverage.json should map "addTwo"');
collectGarbage();
}); });
it('contract sends / transfers to instrumented fallback: coverage, cleanup & exit(0)', () => { it('contract sends / transfers to instrumented fallback: coverage, cleanup & exit(0)', () => {
// Skipped due to https://github.com/sc-forks/solidity-coverage/issues/106 assertCleanInitialState();
// Validate ethereumjs-vm hack to remove gas constraints on transfer() and send()
assert(pathExists('./coverage') === false, 'should start without: coverage');
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
mock.install('Wallet.sol', 'wallet.js', config); mock.install('Wallet.sol', 'wallet.js', config);
shell.exec(script); shell.exec(script);
assert(shell.error() === null, 'script should not error'); assert(shell.error() === null, 'script should not error');
assert(pathExists('./coverage') === true, 'script should gen coverage folder'); assertCoverageGenerated();
assert(pathExists('./coverage.json') === true, 'script should gen coverage.json');
const produced = JSON.parse(fs.readFileSync('./coverage.json', 'utf8')); const produced = JSON.parse(fs.readFileSync('./coverage.json', 'utf8'));
const path = Object.keys(produced)[0]; const path = Object.keys(produced)[0];
assert(produced[path].fnMap['1'].name === 'transferPayment', 'should map "transferPayment"'); assert(produced[path].fnMap['1'].name === 'transferPayment', 'should map "transferPayment"');
collectGarbage();
}); });
it('contract uses inheritance: should generate coverage, cleanup & exit(0)', () => { it('contract uses inheritance: should generate coverage, cleanup & exit(0)', () => {
// Run against a contract that 'is' another contract assertCleanInitialState();
assert(pathExists('./coverage') === false, 'should start without: coverage');
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
mock.installInheritanceTest(config);
mock.installInheritanceTest(config);
shell.exec(script); shell.exec(script);
assert(shell.error() === null, 'script should not error'); assert(shell.error() === null, 'script should not error');
assert(pathExists('./coverage') === true, 'script should gen coverage folder'); assertCoverageGenerated();
assert(pathExists('./coverage.json') === true, 'script should gen coverage.json');
const produced = JSON.parse(fs.readFileSync('./coverage.json', 'utf8')); const produced = JSON.parse(fs.readFileSync('./coverage.json', 'utf8'));
const ownedPath = Object.keys(produced)[0]; const ownedPath = Object.keys(produced)[0];
const proxyPath = Object.keys(produced)[1]; const proxyPath = Object.keys(produced)[1];
assert(produced[ownedPath].fnMap['1'].name === 'constructor', 'coverage.json should map "constructor"'); assert(produced[ownedPath].fnMap['1'].name === 'constructor', 'coverage.json should map "constructor"');
assert(produced[proxyPath].fnMap['1'].name === 'isOwner', 'coverage.json should map "isOwner"'); assert(produced[proxyPath].fnMap['1'].name === 'isOwner', 'coverage.json should map "isOwner"');
collectGarbage();
}); });
it('contracts are skipped: should generate coverage, cleanup & exit(0)', () => { it('contracts are skipped: should generate coverage, cleanup & exit(0)', () => {
// Skip instrumentation of some contracts assertCleanInitialState();
assert(pathExists('./coverage') === false, 'should start without: coverage');
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
const testConfig = Object.assign({}, config); const testConfig = Object.assign({}, config);
testConfig.skipFiles = ['Owned.sol']; testConfig.skipFiles = ['Owned.sol'];
@ -392,32 +214,30 @@ describe('app', () => {
shell.exec(script); shell.exec(script);
assert(shell.error() === null, 'script should not error'); assert(shell.error() === null, 'script should not error');
assert(pathExists('./coverage') === true, 'script should gen coverage folder'); assertCoverageGenerated();
assert(pathExists('./coverage.json') === true, 'script should gen coverage.json');
const produced = JSON.parse(fs.readFileSync('./coverage.json', 'utf8')); const produced = JSON.parse(fs.readFileSync('./coverage.json', 'utf8'));
const firstKey = Object.keys(produced)[0]; const firstKey = Object.keys(produced)[0];
assert(Object.keys(produced).length === 1, 'coverage.json should only contain instrumentation for one contract'); assert(Object.keys(produced).length === 1, 'coverage.json should only contain instrumentation for one contract');
assert(firstKey.substr(firstKey.length - 9) === 'Proxy.sol', 'coverage.json should only contain instrumentation for Proxy.sol'); assert(firstKey.substr(firstKey.length - 9) === 'Proxy.sol', 'coverage.json should only contain instrumentation for Proxy.sol');
collectGarbage();
}); });
it('truffle tests failing: should generate coverage, cleanup & exit(1)', () => { it('truffle tests failing: should generate coverage, cleanup & exit(1)', () => {
assert(pathExists('./coverage') === false, 'should start without: coverage'); assertCleanInitialState();
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
// Run with Simple.sol and a failing assertion in a truffle test // Run with Simple.sol and a failing assertion in a truffle test
mock.install('Simple.sol', 'truffle-test-fail.js', config); mock.install('Simple.sol', 'truffle-test-fail.js', config);
shell.exec(script); shell.exec(script);
assert(shell.error() !== null, 'script should exit 1'); assert(shell.error() !== null, 'script should exit 1');
assert(pathExists('./coverage') === true, 'script should gen coverage folder');
assert(pathExists('./coverage.json') === true, 'script should gen coverage.json'); assertCoverageGenerated();
const produced = JSON.parse(fs.readFileSync('./coverage.json', 'utf8')); const produced = JSON.parse(fs.readFileSync('./coverage.json', 'utf8'));
const path = Object.keys(produced)[0]; const path = Object.keys(produced)[0];
assert(produced[path].fnMap['1'].name === 'test', 'coverage.json should map "test"'); assert(produced[path].fnMap['1'].name === 'test', 'coverage.json should map "test"');
assert(produced[path].fnMap['2'].name === 'getX', 'coverage.json should map "getX"'); assert(produced[path].fnMap['2'].name === 'getX', 'coverage.json should map "getX"');
collectGarbage();
}); });
it('deployment cost > block gasLimit: should generate coverage, cleanup & exit(0)', () => { it('deployment cost > block gasLimit: should generate coverage, cleanup & exit(0)', () => {
@ -425,44 +245,27 @@ describe('app', () => {
mock.install('Expensive.sol', 'block-gas-limit.js', config); mock.install('Expensive.sol', 'block-gas-limit.js', config);
shell.exec(script); shell.exec(script);
assert(shell.error() === null, 'script should not error'); assert(shell.error() === null, 'script should not error');
collectGarbage();
}); });
it('truffle crashes: should generate NO coverage, cleanup and exit(1)', () => { it('truffle crashes: should generate NO coverage, cleanup and exit(1)', () => {
assert(pathExists('./coverage') === false, 'should start without: coverage'); assertCleanInitialState();
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
// Run with Simple.sol and a syntax error in the truffle test // Run with Simple.sol and a syntax error in the truffle test
mock.install('Simple.sol', 'truffle-crash.js', config); mock.install('Simple.sol', 'truffle-crash.js', config);
shell.exec(script); shell.exec(script);
assert(shell.error() !== null, 'script should error'); assert(shell.error() !== null, 'script should error');
assert(pathExists('./coverage') !== true, 'script should NOT gen coverage folder'); assertCoverageNotGenerated();
assert(pathExists('./coverage.json') !== true, 'script should NOT gen coverage.json');
collectGarbage();
}); });
it('instrumentation errors: should generate NO coverage, cleanup and exit(1)', () => { it('instrumentation errors: should generate NO coverage, cleanup and exit(1)', () => {
assert(pathExists('./coverage') === false, 'should start without: coverage'); assertCleanInitialState();
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
// Run with SimpleError.sol (has syntax error) and working truffle test // Run with SimpleError.sol (has syntax error) and working truffle test
mock.install('SimpleError.sol', 'simple.js', config); mock.install('SimpleError.sol', 'simple.js', config);
shell.exec(script); shell.exec(script);
assert(shell.error() !== null, 'script should error'); assert(shell.error() !== null, 'script should error');
assert(pathExists('./coverage') !== true, 'script should NOT gen coverage folder'); assertCoverageNotGenerated();
assert(pathExists('./coverage.json') !== true, 'script should NOT gen coverage.json');
collectGarbage();
}); });
it('no events log produced: should generate NO coverage, cleanup and exit(1)', () => {
// Run contract and test that pass but fire no events
assert(pathExists('./coverage') === false, 'should start without: coverage');
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
mock.install('Empty.sol', 'empty.js', config);
shell.exec(script);
assert(shell.error() !== null, 'script should error');
assert(pathExists('./coverage') !== true, 'script should NOT gen coverage folder');
assert(pathExists('./coverage.json') !== true, 'script should NOT gen coverage.json');
collectGarbage();
}); });
});*/

Loading…
Cancel
Save