Improve coverage (#409)

+ Remove unused code from `lib/parse.js`
+ Add invalid reporter test
+ Remove un-hittable try/catch around backup client load in  API.ganache
truffle-plugin
cgewecke 5 years ago
parent 438b2fef0f
commit 1c8701ecf9
  1. 12
      lib/api.js
  2. 34
      lib/parse.js
  3. 13
      lib/registrar.js
  4. 0
      test/integration/projects/libraries/contracts/CLibrary.sol
  5. 23
      test/integration/projects/libraries/contracts/Migrations.sol
  6. 0
      test/integration/projects/libraries/contracts/PureView.sol
  7. 8
      test/integration/projects/libraries/contracts/UsesPure.sol
  8. 2
      test/integration/projects/libraries/contracts/_Interface.sol
  9. 5
      test/integration/projects/libraries/migrations/1_initial_migration.js
  10. 8
      test/integration/projects/libraries/migrations/2_contracta.js
  11. 64
      test/integration/projects/libraries/test/libraries.js
  12. 7
      test/integration/projects/libraries/truffle-config.js
  13. 25
      test/sources/js/totallyPure.js
  14. 8
      test/sources/solidity/contracts/statements/unary.sol
  15. 9
      test/units/statements.js
  16. 38
      test/units/truffle/errors.js
  17. 27
      test/units/truffle/flags.js
  18. 276
      test/units/truffle/standard.js

@ -150,13 +150,7 @@ class API {
catch(err) { catch(err) {
this.ui.report('vm-fail', []); this.ui.report('vm-fail', []);
this.client = require('ganache-core-sc'); this.client = require('ganache-core-sc');
await this.attachToVM();
try { await this.attachToVM() }
catch(err) {
err.message = `${this.ui.generate('server-fail', [address])} ${err.message}`;
throw err;
}
} }
this.ui.report('server', [address]); this.ui.report('server', [address]);
@ -283,10 +277,6 @@ class API {
return newCoverage; return newCoverage;
} }
toRelativePath(absolutePath, parentDir){
return absolutePath.split(`/${parentDir}`)[1]
}
// ======= // =======
// Logging // Logging
// ======= // =======

@ -9,7 +9,6 @@ const register = new Registrar();
const parse = {}; const parse = {};
parse.AssignmentExpression = function(contract, expression) { parse.AssignmentExpression = function(contract, expression) {
register.prePosition(expression);
register.statement(contract, expression); register.statement(contract, expression);
}; };
@ -41,9 +40,10 @@ parse.FunctionCall = function(contract, expression) {
} }
}; };
parse.ConditionalExpression = function(contract, expression) { parse.Conditional = function(contract, expression) {
register.statement(contract, expression); register.statement(contract, expression);
register.conditionalExpression(contract, expression); // TODO: Investigate node structure
// There are potential substatements here we aren't measuring
}; };
parse.ContractDefinition = function(contract, expression) { parse.ContractDefinition = function(contract, expression) {
@ -108,18 +108,11 @@ parse.IfStatement = function(contract, expression) {
} }
}; };
parse.InterfaceStatement = function(contract, expression) { // TODO: Investigate Node structure
parse.ContractOrLibraryStatement(contract, expression); /*parse.MemberAccess = function(contract, expression) {
};
parse.LibraryStatement = function(contract, expression) {
parse.ContractOrLibraryStatement(contract, expression);
};
parse.MemberExpression = function(contract, expression) {
parse[expression.object.type] && parse[expression.object.type] &&
parse[expression.object.type](contract, expression.object); parse[expression.object.type](contract, expression.object);
}; };*/
parse.Modifiers = function(contract, modifiers) { parse.Modifiers = function(contract, modifiers) {
if (modifiers) { if (modifiers) {
@ -151,10 +144,11 @@ parse.ReturnStatement = function(contract, expression) {
register.statement(contract, expression); register.statement(contract, expression);
}; };
parse.UnaryExpression = function(contract, expression) { // TODO:Investigate node structure
parse[expression.argument.type] && /*parse.UnaryOperation = function(contract, expression) {
parse[expression.argument.type](contract, expression.argument); parse[subExpression.argument.type] &&
}; parse[subExpression.argument.type](contract, expression.argument);
};*/
parse.UsingStatement = function (contract, expression) { parse.UsingStatement = function (contract, expression) {
parse[expression.for.type] && parse[expression.for.type] &&
@ -165,12 +159,6 @@ parse.VariableDeclarationStatement = function (contract, expression) {
register.statement(contract, expression); register.statement(contract, expression);
}; };
parse.VariableDeclarationTuple = function (contract, expression) {
register.statement(contract, expression);
parse[expression.declarations[0].id.type] &&
parse[expression.declarations[0].id.type](contract, expression.declarations[0].id);
};
parse.WhileStatement = function (contract, expression) { parse.WhileStatement = function (contract, expression) {
register.statement(contract, expression); register.statement(contract, expression);
parse[expression.body.type] && parse[expression.body.type] &&

@ -19,19 +19,6 @@ class Registrar {
: contract.injectionPoints[key] = [value]; : contract.injectionPoints[key] = [value];
} }
/**
* TODO - idk what this is anymore
* @param {Object} expression AST node
*/
prePosition(expression) {
if (
expression.right.type === 'ConditionalExpression' &&
expression.left.type === 'MemberExpression'
) {
expression.range[0] -= 2;
}
}
/** /**
* Registers injections for statement measurements * Registers injections for statement measurements
* @param {Object} contract instrumentation target * @param {Object} contract instrumentation target

@ -0,0 +1,23 @@
pragma solidity >=0.4.21 <0.6.0;
contract Migrations {
address public owner;
uint public last_completed_migration;
constructor() public {
owner = msg.sender;
}
modifier restricted() {
if (msg.sender == owner) _;
}
function setCompleted(uint completed) public restricted {
last_completed_migration = completed;
}
function upgrade(address new_address) public restricted {
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
}

@ -1,10 +1,10 @@
pragma solidity ^0.5.0; pragma solidity ^0.5.0;
import "./../../externalSources/Face.sol"; import "./_Interface.sol";
import "./../../externalSources/PureView.sol"; import "./PureView.sol";
import "./../../externalSources/CLibrary.sol"; import "./CLibrary.sol";
contract TotallyPure is PureView, Face { contract UsesPure is PureView, _Interface {
uint onehundred = 99; uint onehundred = 99;
function usesThem() public view { function usesThem() public view {

@ -1,6 +1,6 @@
pragma solidity ^0.5.0; pragma solidity ^0.5.0;
interface Face { interface _Interface {
function stare(uint a, uint b) external; function stare(uint a, uint b) external;
function cry() external; function cry() external;
} }

@ -0,0 +1,5 @@
const Migrations = artifacts.require("Migrations");
module.exports = function(deployer) {
deployer.deploy(Migrations);
};

@ -0,0 +1,8 @@
const UsesPure = artifacts.require("UsesPure");
const CLibrary = artifacts.require("CLibrary");
module.exports = function(deployer) {
deployer.deploy(CLibrary);
deployer.link(CLibrary, UsesPure);
deployer.deploy(UsesPure);
};

@ -0,0 +1,64 @@
const UsesPure = artifacts.require('UsesPure');
contract('UsesPure', accounts => {
it('calls imported, inherited pure/view functions within its own function', async () => {
const instance = await UsesPure.deployed();
await instance.usesThem();
});
it('calls a library method', async() => {
const instance = await UsesPure.deployed();
const value = await instance.usesLibrary();
assert.equal(value.toNumber(), 1);
});
it('calls an imported, inherited pure function', async () => {
const instance = await UsesPure.deployed();
const value = await instance.isPure(4, 5);
assert.equal(value.toNumber(), 20);
});
it('calls an imported, inherited view function', async () => {
const instance = await UsesPure.deployed();
const value = await instance.isView();
assert.equal(value.toNumber(), 5);
});
it('overrides an imported, inherited abstract pure function', async () => {
const instance = await UsesPure.deployed();
const value = await instance.bePure(4, 5);
assert.equal(value.toNumber(), 9);
});
it('overrides an imported, inherited abstract view function', async () => {
const instance = await UsesPure.deployed();
const value = await instance.beView();
assert.equal(value.toNumber(), 99);
});
it('calls a pure method implemented in an inherited class', async() => {
const instance = await UsesPure.deployed();
const value = await instance.inheritedPure(4, 5);
assert.equal(value.toNumber(), 9);
});
it('calls a view method implemented in an inherited class', async () => {
const instance = await UsesPure.deployed();
const value = await instance.inheritedView();
assert.equal(value.toNumber(), 5);
});
it('calls a view method whose modifiers span lines', async () => {
const instance = await UsesPure.deployed();
const value = await instance.multiline(5, 7)
assert.equal(value.toNumber(), 99);
});
it('calls a method who signature is defined by an interface', async () => {
const instance = await UsesPure.deployed();
await instance.cry();
});
});

@ -0,0 +1,7 @@
module.exports = {
networks: {},
mocha: {},
compilers: {
solc: {}
}
}

@ -1,58 +1,55 @@
/* eslint-env node, mocha */ const UsesPure = artifacts.require('UsesPure');
/* global artifacts, contract, assert */
const TotallyPure = artifacts.require('./TotallyPure.sol'); contract('UsesPure', accounts => {
contract('TotallyPure', accounts => {
it('calls imported, inherited pure/view functions within its own function', async () => { it('calls imported, inherited pure/view functions within its own function', async () => {
const instance = await TotallyPure.deployed(); const instance = await UsesPure.deployed();
await instance.usesThem(); await instance.usesThem();
}); });
it('calls an imported, inherited pure function', async () => { it('calls an imported, inherited pure function', async () => {
const instance = await TotallyPure.deployed(); const instance = await UsesPure.deployed();
const value = await instance.isPure(4, 5); const value = await instance.isPure(4, 5);
assert.equal(value.toNumber(), 20); assert.equal(value.toNumber(), 20);
}); });
it('calls an imported, inherited view function', async () => { it('calls an imported, inherited view function', async () => {
const instance = await TotallyPure.deployed(); const instance = await UsesPure.deployed();
const value = await instance.isView(); const value = await instance.isView();
assert.equal(value.toNumber(), 5); assert.equal(value.toNumber(), 5);
}); });
it('overrides an imported, inherited abstract pure function', async () => { it('overrides an imported, inherited abstract pure function', async () => {
const instance = await TotallyPure.deployed(); const instance = await UsesPure.deployed();
const value = await instance.bePure(4, 5); const value = await instance.bePure(4, 5);
assert.equal(value.toNumber(), 9); assert.equal(value.toNumber(), 9);
}); });
it('overrides an imported, inherited abstract view function', async () => { it('overrides an imported, inherited abstract view function', async () => {
const instance = await TotallyPure.deployed(); const instance = await UsesPure.deployed();
const value = await instance.beView(); const value = await instance.beView();
assert.equal(value.toNumber(), 99); assert.equal(value.toNumber(), 99);
}); });
it('calls a pure method implemented in an inherited class', async() => { it('calls a pure method implemented in an inherited class', async() => {
const instance = await TotallyPure.deployed(); const instance = await UsesPure.deployed();
const value = await instance.inheritedPure(4, 5); const value = await instance.inheritedPure(4, 5);
assert.equal(value.toNumber(), 9); assert.equal(value.toNumber(), 9);
}); });
it('calls a view method implemented in an inherited class', async () => { it('calls a view method implemented in an inherited class', async () => {
const instance = await TotallyPure.deployed(); const instance = await UsesPure.deployed();
const value = await instance.inheritedView(); const value = await instance.inheritedView();
assert.equal(value.toNumber(), 5); assert.equal(value.toNumber(), 5);
}); });
it('calls a view method whose modifiers span lines', async () => { it('calls a view method whose modifiers span lines', async () => {
const instance = await TotallyPure.deployed(); const instance = await UsesPure.deployed();
const value = await instance.multiline(5, 7) const value = await instance.multiline(5, 7)
assert.equal(value.toNumber(), 99); assert.equal(value.toNumber(), 99);
}); });
it('calls a method who signature is defined by an interface', async () => { it('calls a method who signature is defined by an interface', async () => {
const instance = await TotallyPure.deployed(); const instance = await UsesPure.deployed();
await instance.cry(); await instance.cry();
}); });

@ -0,0 +1,8 @@
pragma solidity ^0.5.0;
contract Test {
function a() public {
uint x = 1;
x++;
}
}

@ -147,6 +147,15 @@ describe('generic statements', () => {
}); });
}); });
it.skip('should cover a unary statement', async function(){
const contract = await util.bootstrapCoverage('statements/unary', provider, collector);
coverage.addContract(contract.instrumented, util.filePath);
await contract.instance.a();
const mapping = coverage.generate(contract.data, util.pathPrefix);
// TODO: obtain both statements in unary.sol
})
it('should cover an empty bodied contract statement', async function() { it('should cover an empty bodied contract statement', async function() {
const contract = await util.bootstrapCoverage('statements/empty-contract-body', provider, collector); const contract = await util.bootstrapCoverage('statements/empty-contract-body', provider, collector);
coverage.addContract(contract.instrumented, util.filePath); coverage.addContract(contract.instrumented, util.filePath);

@ -23,12 +23,12 @@ describe('Truffle Plugin: error cases', function() {
mock.loggerOutput.val = ''; mock.loggerOutput.val = '';
solcoverConfig = {}; solcoverConfig = {};
truffleConfig = mock.getDefaultTruffleConfig(); truffleConfig = mock.getDefaultTruffleConfig();
verify.cleanInitialState();
}) })
afterEach(() => mock.clean()); afterEach(() => mock.clean());
it('project contains no contract sources folder', async function() { it('project contains no contract sources folder', async function() {
verify.cleanInitialState();
mock.installFullProject('no-sources'); mock.installFullProject('no-sources');
try { try {
@ -50,9 +50,8 @@ describe('Truffle Plugin: error cases', function() {
}); });
it('.solcover.js has syntax error', async function(){ it('.solcover.js has syntax error', async function(){
verify.cleanInitialState();
mock.installFullProject('bad-solcoverjs'); mock.installFullProject('bad-solcoverjs');
try { try {
await plugin(truffleConfig); await plugin(truffleConfig);
assert.fail() assert.fail()
@ -67,7 +66,6 @@ describe('Truffle Plugin: error cases', function() {
}) })
it('.solcover.js has incorrectly formatted option', async function(){ it('.solcover.js has incorrectly formatted option', async function(){
verify.cleanInitialState();
solcoverConfig.port = "Antwerpen"; solcoverConfig.port = "Antwerpen";
mock.install('Simple', 'simple.js', solcoverConfig); mock.install('Simple', 'simple.js', solcoverConfig);
@ -84,7 +82,6 @@ describe('Truffle Plugin: error cases', function() {
}); });
it('lib module load failure', async function(){ it('lib module load failure', async function(){
verify.cleanInitialState();
truffleConfig.usePluginTruffle = true; truffleConfig.usePluginTruffle = true;
truffleConfig.forceLibFailure = true; truffleConfig.forceLibFailure = true;
@ -102,8 +99,6 @@ describe('Truffle Plugin: error cases', function() {
}); });
it('--network <target> is not declared in truffle-config.js', async function(){ it('--network <target> is not declared in truffle-config.js', async function(){
verify.cleanInitialState();
truffleConfig.network = 'does-not-exist'; truffleConfig.network = 'does-not-exist';
mock.install('Simple', 'simple.js', solcoverConfig); mock.install('Simple', 'simple.js', solcoverConfig);
@ -127,7 +122,6 @@ describe('Truffle Plugin: error cases', function() {
// This case *does* throw an error, but it's uncatch-able; // This case *does* throw an error, but it's uncatch-able;
it('tries to launch with a port already in use', async function(){ it('tries to launch with a port already in use', async function(){
verify.cleanInitialState();
const server = ganache.server(); const server = ganache.server();
truffleConfig.network = 'development'; truffleConfig.network = 'development';
@ -149,11 +143,31 @@ describe('Truffle Plugin: error cases', function() {
await pify(server.close)(); await pify(server.close)();
}); });
it('uses an invalid istanbul reporter', async function() {
solcoverConfig = {
silent: process.env.SILENT ? true : false,
istanbulReporter: ['does-not-exist']
};
mock.install('Simple', 'simple.js', solcoverConfig);
try {
await plugin(truffleConfig);
assert.fail();
} catch(err){
assert(
err.message.includes('does-not-exist') &&
err.message.includes('coverage reports could not be generated'),
`Should error on invalid reporter: ${err.message}`
)
}
});
// Truffle test contains syntax error // Truffle test contains syntax error
it('truffle crashes', async function() { it('truffle crashes', async function() {
verify.cleanInitialState();
mock.install('Simple', 'truffle-crash.js', solcoverConfig); mock.install('Simple', 'truffle-crash.js', solcoverConfig);
try { try {
await plugin(truffleConfig); await plugin(truffleConfig);
assert.fail() assert.fail()
@ -164,8 +178,6 @@ describe('Truffle Plugin: error cases', function() {
// Solidity syntax errors // Solidity syntax errors
it('compilation failure', async function(){ it('compilation failure', async function(){
verify.cleanInitialState();
mock.install('SimpleError', 'simple.js', solcoverConfig); mock.install('SimpleError', 'simple.js', solcoverConfig);
try { try {
@ -179,8 +191,6 @@ describe('Truffle Plugin: error cases', function() {
}); });
it('instrumentation failure', async function(){ it('instrumentation failure', async function(){
verify.cleanInitialState();
mock.install('Unparseable', 'simple.js', solcoverConfig); mock.install('Unparseable', 'simple.js', solcoverConfig);
try { try {

@ -21,13 +21,12 @@ describe('Truffle Plugin: command line options', function() {
mock.loggerOutput.val = ''; mock.loggerOutput.val = '';
solcoverConfig = {}; solcoverConfig = {};
truffleConfig = mock.getDefaultTruffleConfig(); truffleConfig = mock.getDefaultTruffleConfig();
verify.cleanInitialState();
}) })
afterEach(() => mock.clean()); afterEach(() => mock.clean());
it('--file test/<fileName>', async function() { it('--file test/<fileName>', async function() {
verify.cleanInitialState();
truffleConfig.file = path.join( truffleConfig.file = path.join(
truffleConfig.working_directory, truffleConfig.working_directory,
'test/specific_a.js' 'test/specific_a.js'
@ -55,8 +54,6 @@ describe('Truffle Plugin: command line options', function() {
}); });
it('--file test/<glob*>', async function() { it('--file test/<glob*>', async function() {
verify.cleanInitialState();
truffleConfig.file = path.join( truffleConfig.file = path.join(
truffleConfig.working_directory, truffleConfig.working_directory,
'test/globby*' 'test/globby*'
@ -84,8 +81,6 @@ describe('Truffle Plugin: command line options', function() {
}); });
it('--file test/gl{o,b}*.js', async function() { it('--file test/gl{o,b}*.js', async function() {
verify.cleanInitialState();
truffleConfig.file = path.join( truffleConfig.file = path.join(
truffleConfig.working_directory, truffleConfig.working_directory,
'test/gl{o,b}*.js' 'test/gl{o,b}*.js'
@ -113,8 +108,6 @@ describe('Truffle Plugin: command line options', function() {
}); });
it('--config ../.solcover.js', async function() { it('--config ../.solcover.js', async function() {
verify.cleanInitialState();
solcoverConfig = { solcoverConfig = {
silent: process.env.SILENT ? true : false, silent: process.env.SILENT ? true : false,
istanbulReporter: ['json-summary', 'text'] istanbulReporter: ['json-summary', 'text']
@ -143,8 +136,6 @@ describe('Truffle Plugin: command line options', function() {
}); });
it('--help', async function(){ it('--help', async function(){
verify.cleanInitialState();
truffleConfig.help = "true"; truffleConfig.help = "true";
truffleConfig.logger = mock.testLogger; truffleConfig.logger = mock.testLogger;
@ -158,8 +149,6 @@ describe('Truffle Plugin: command line options', function() {
}) })
it('--version', async function(){ it('--version', async function(){
verify.cleanInitialState();
truffleConfig.version = "true"; truffleConfig.version = "true";
truffleConfig.logger = mock.testLogger; truffleConfig.logger = mock.testLogger;
@ -184,8 +173,6 @@ describe('Truffle Plugin: command line options', function() {
}) })
it('--useGlobalTruffle', async function(){ it('--useGlobalTruffle', async function(){
verify.cleanInitialState();
truffleConfig.useGlobalTruffle = true; truffleConfig.useGlobalTruffle = true;
truffleConfig.logger = mock.testLogger; truffleConfig.logger = mock.testLogger;
@ -199,8 +186,6 @@ describe('Truffle Plugin: command line options', function() {
}); });
it('--usePluginTruffle', async function(){ it('--usePluginTruffle', async function(){
verify.cleanInitialState();
truffleConfig.usePluginTruffle = true; truffleConfig.usePluginTruffle = true;
truffleConfig.logger = mock.testLogger; truffleConfig.logger = mock.testLogger;
@ -214,8 +199,6 @@ describe('Truffle Plugin: command line options', function() {
}); });
it('--usePluginTruffle', async function(){ it('--usePluginTruffle', async function(){
verify.cleanInitialState();
truffleConfig.usePluginTruffle = true; truffleConfig.usePluginTruffle = true;
truffleConfig.logger = mock.testLogger; truffleConfig.logger = mock.testLogger;
@ -229,8 +212,6 @@ describe('Truffle Plugin: command line options', function() {
}); });
it('--network (network_id mismatch in configs)', async function(){ it('--network (network_id mismatch in configs)', async function(){
verify.cleanInitialState();
truffleConfig.logger = mock.testLogger; truffleConfig.logger = mock.testLogger;
solcoverConfig = { providerOptions: { network_id: 5 }} solcoverConfig = { providerOptions: { network_id: 5 }}
@ -248,8 +229,6 @@ describe('Truffle Plugin: command line options', function() {
}); });
it('--network (truffle config missing port)', async function(){ it('--network (truffle config missing port)', async function(){
verify.cleanInitialState();
truffleConfig.logger = mock.testLogger; truffleConfig.logger = mock.testLogger;
truffleConfig.network = 'development'; truffleConfig.network = 'development';
@ -267,12 +246,9 @@ describe('Truffle Plugin: command line options', function() {
mock.loggerOutput.val.includes("8555"), mock.loggerOutput.val.includes("8555"),
`Should have used default coverage port 8555: ${mock.loggerOutput.val}` `Should have used default coverage port 8555: ${mock.loggerOutput.val}`
); );
}); });
it('--network (declared port mismatches)', async function(){ it('--network (declared port mismatches)', async function(){
verify.cleanInitialState();
truffleConfig.logger = mock.testLogger; truffleConfig.logger = mock.testLogger;
truffleConfig.network = 'development'; // 8545 truffleConfig.network = 'development'; // 8545
@ -290,7 +266,6 @@ describe('Truffle Plugin: command line options', function() {
mock.loggerOutput.val.includes("8545"), mock.loggerOutput.val.includes("8545"),
`Should have used default coverage port 8545: ${mock.loggerOutput.val}` `Should have used default coverage port 8545: ${mock.loggerOutput.val}`
); );
}); });
}); });

@ -21,13 +21,12 @@ describe('Truffle Plugin: standard use cases', function() {
mock.loggerOutput.val = ''; mock.loggerOutput.val = '';
solcoverConfig = {}; solcoverConfig = {};
truffleConfig = mock.getDefaultTruffleConfig(); truffleConfig = mock.getDefaultTruffleConfig();
verify.cleanInitialState();
}) })
afterEach(() => mock.clean()); afterEach(() => mock.clean());
it('simple contract', async function(){ it('simple contract', async function(){
verify.cleanInitialState();
mock.install('Simple', 'simple.js', solcoverConfig); mock.install('Simple', 'simple.js', solcoverConfig);
await plugin(truffleConfig); await plugin(truffleConfig);
@ -36,21 +35,18 @@ describe('Truffle Plugin: standard use cases', function() {
const output = mock.getOutput(truffleConfig); const output = mock.getOutput(truffleConfig);
const path = Object.keys(output)[0]; const path = Object.keys(output)[0];
assert(output[path].fnMap['1'].name === 'test', 'coverage.json missing "test"'); assert(
assert(output[path].fnMap['2'].name === 'getX', 'coverage.json missing "getX"'); output[path].fnMap['1'].name === 'test',
}); 'coverage.json missing "test"'
);
// Truffle test asserts balance is 777 ether
it('config with providerOptions', async function() {
solcoverConfig.providerOptions = { default_balance_ether: 777 }
mock.install('Simple', 'testrpc-options.js', solcoverConfig); assert(
await plugin(truffleConfig); output[path].fnMap['2'].name === 'getX',
'coverage.json missing "getX"'
);
}); });
it('large contract with many unbracketed statements (time check)', async function() { it('with many unbracketed statements (time check)', async function() {
verify.cleanInitialState();
truffleConfig.compilers.solc.version = "0.4.24"; truffleConfig.compilers.solc.version = "0.4.24";
mock.install('Oraclize', 'oraclize.js', solcoverConfig, truffleConfig, true); mock.install('Oraclize', 'oraclize.js', solcoverConfig, truffleConfig, true);
@ -59,8 +55,7 @@ describe('Truffle Plugin: standard use cases', function() {
// This project has three contract suites and uses .deployed() instances which // This project has three contract suites and uses .deployed() instances which
// depend on truffle's migratons and the inter-test evm_revert / evm_snapshot mechanism. // depend on truffle's migratons and the inter-test evm_revert / evm_snapshot mechanism.
it('project evm_reverts repeatedly', async function() { it('with multiple migrations (evm_reverts repeatedly)', async function() {
verify.cleanInitialState();
mock.installFullProject('multiple-migrations'); mock.installFullProject('multiple-migrations');
await plugin(truffleConfig); await plugin(truffleConfig);
@ -82,84 +77,17 @@ describe('Truffle Plugin: standard use cases', function() {
verify.lineCoverage(expected); verify.lineCoverage(expected);
}); });
// This project has [ @skipForCoverage ] tags in the test descriptions it('with relative path solidity imports', async function() {
// at selected 'contract' and 'it' blocks. mock.installFullProject('import-paths');
it('uses solcoverjs mocha options', async function() {
verify.cleanInitialState();
solcoverConfig.mocha = {
grep: '@skipForCoverage',
invert: true,
};
solcoverConfig.silent = process.env.SILENT ? true : false,
solcoverConfig.istanbulReporter = ['json-summary', 'text']
mock.installFullProject('multiple-migrations', solcoverConfig);
await plugin(truffleConfig);
const expected = [
{
file: mock.pathToContract(truffleConfig, 'ContractA.sol'),
pct: 0
},
{
file: mock.pathToContract(truffleConfig, 'ContractB.sol'),
pct: 0,
},
{
file: mock.pathToContract(truffleConfig, 'ContractC.sol'),
pct: 100,
},
];
verify.lineCoverage(expected);
});
it('skips a folder', async function() {
verify.cleanInitialState();
mock.installFullProject('skipping');
await plugin(truffleConfig);
const expected = [{
file: mock.pathToContract(truffleConfig, 'ContractA.sol'),
pct: 100
}];
const missing = [{
file: mock.pathToContract(truffleConfig, 'skipped-folder/ContractB.sol'),
}];
verify.lineCoverage(expected);
verify.coverageMissing(missing);
});
it('uses "onServerReady", "onTestsComplete", "onIstanbulComplete"', async function() {
verify.cleanInitialState();
truffleConfig.logger = mock.testLogger;
mock.installFullProject('test-files');
await plugin(truffleConfig); await plugin(truffleConfig);
assert(
mock.loggerOutput.val.includes('running onServerReady') &&
mock.loggerOutput.val.includes('running onTestsComplete') &&
mock.loggerOutput.val.includes('running onIstanbulComplete'),
`Should run "on" hooks : ${mock.loggerOutput.val}`
);
}); });
it('project with relative path solidity imports', async function() { it('uses libraries', async function() {
verify.cleanInitialState(); mock.installFullProject('libraries');
mock.installFullProject('import-paths');
await plugin(truffleConfig); await plugin(truffleConfig);
}); });
it('project contains native solidity tests', async function(){ it('uses native solidity tests', async function(){
verify.cleanInitialState();
mock.install('Simple', 'TestSimple.sol', solcoverConfig); mock.install('Simple', 'TestSimple.sol', solcoverConfig);
truffleConfig.logger = mock.testLogger; truffleConfig.logger = mock.testLogger;
@ -171,63 +99,58 @@ describe('Truffle Plugin: standard use cases', function() {
); );
}); });
it('contract only uses ".call"', async function(){ it('uses inheritance', async function() {
verify.cleanInitialState(); mock.installDouble(
['Proxy', 'Owned'],
'inheritance.js',
solcoverConfig
);
mock.install('OnlyCall', 'only-call.js', solcoverConfig);
await plugin(truffleConfig); await plugin(truffleConfig);
verify.coverageGenerated(truffleConfig); verify.coverageGenerated(truffleConfig);
const output = mock.getOutput(truffleConfig); const output = mock.getOutput(truffleConfig);
const path = Object.keys(output)[0]; const ownedPath = Object.keys(output)[0];
assert(output[path].fnMap['1'].name === 'addTwo', 'cov should map "addTwo"'); const proxyPath = Object.keys(output)[1];
});
it('contract sends / transfers to instrumented fallback', async function(){
verify.cleanInitialState();
mock.install('Wallet', 'wallet.js', solcoverConfig);
await plugin(truffleConfig);
verify.coverageGenerated(truffleConfig); assert(
output[ownedPath].fnMap['1'].name === 'constructor',
'"constructor" not covered'
);
const output = mock.getOutput(truffleConfig); assert(
const path = Object.keys(output)[0]; output[proxyPath].fnMap['1'].name === 'isOwner',
assert(output[path].fnMap['1'].name === 'transferPayment', 'cov should map "transferPayment"'); '"isOwner" not covered'
);
}); });
it('contracts are skipped', async function() { it('only uses ".call"', async function(){
verify.cleanInitialState(); mock.install('OnlyCall', 'only-call.js', solcoverConfig);
solcoverConfig.skipFiles = ['Owned.sol'];
mock.installDouble(['Proxy', 'Owned'], 'inheritance.js', solcoverConfig);
await plugin(truffleConfig); await plugin(truffleConfig);
verify.coverageGenerated(truffleConfig); verify.coverageGenerated(truffleConfig);
const output = mock.getOutput(truffleConfig); const output = mock.getOutput(truffleConfig);
const firstKey = Object.keys(output)[0]; const path = Object.keys(output)[0];
assert(
assert(Object.keys(output).length === 1, 'Wrong # of contracts covered'); output[path].fnMap['1'].name === 'addTwo',
assert(firstKey.substr(firstKey.length - 9) === 'Proxy.sol', 'Wrong contract covered'); 'cov should map "addTwo"'
);
}); });
it('contract uses inheritance', async function() { it('sends / transfers to instrumented fallback', async function(){
verify.cleanInitialState(); mock.install('Wallet', 'wallet.js', solcoverConfig);
mock.installDouble(['Proxy', 'Owned'], 'inheritance.js', solcoverConfig);
await plugin(truffleConfig); await plugin(truffleConfig);
verify.coverageGenerated(truffleConfig); verify.coverageGenerated(truffleConfig);
const output = mock.getOutput(truffleConfig); const output = mock.getOutput(truffleConfig);
const ownedPath = Object.keys(output)[0]; const path = Object.keys(output)[0];
const proxyPath = Object.keys(output)[1]; assert(
output[path].fnMap['1'].name === 'transferPayment',
assert(output[ownedPath].fnMap['1'].name === 'constructor', '"constructor" not covered'); 'cov should map "transferPayment"'
assert(output[proxyPath].fnMap['1'].name === 'isOwner', '"isOwner" not covered'); );
}); });
// Truffle test asserts deployment cost is greater than 20,000,000 gas // Truffle test asserts deployment cost is greater than 20,000,000 gas
@ -237,9 +160,7 @@ describe('Truffle Plugin: standard use cases', function() {
}); });
// Simple.sol with a failing assertion in a truffle test // Simple.sol with a failing assertion in a truffle test
it('truffle tests failing', async function() { it('unit tests failing', async function() {
verify.cleanInitialState();
mock.install('Simple', 'truffle-test-fail.js', solcoverConfig); mock.install('Simple', 'truffle-test-fail.js', solcoverConfig);
try { try {
@ -259,11 +180,8 @@ describe('Truffle Plugin: standard use cases', function() {
}); });
it('uses the fallback server', async function(){ it('uses the fallback server', async function(){
verify.cleanInitialState();
truffleConfig.logger = mock.testLogger; truffleConfig.logger = mock.testLogger;
solcoverConfig.forceBackupServer = true;
solcoverConfig = { forceBackupServer: true }
mock.install('Simple', 'simple.js', solcoverConfig); mock.install('Simple', 'simple.js', solcoverConfig);
await plugin(truffleConfig); await plugin(truffleConfig);
@ -272,6 +190,104 @@ describe('Truffle Plugin: standard use cases', function() {
mock.loggerOutput.val.includes("Using ganache-core-sc"), mock.loggerOutput.val.includes("Using ganache-core-sc"),
`Should notify about backup server module: ${mock.loggerOutput.val}` `Should notify about backup server module: ${mock.loggerOutput.val}`
); );
});
// This project has [ @skipForCoverage ] tags in the test descriptions
// at selected 'contract' and 'it' blocks.
it('config: mocha options', async function() {
solcoverConfig.mocha = {
grep: '@skipForCoverage',
invert: true,
};
solcoverConfig.silent = process.env.SILENT ? true : false,
solcoverConfig.istanbulReporter = ['json-summary', 'text']
mock.installFullProject('multiple-migrations', solcoverConfig);
await plugin(truffleConfig);
const expected = [
{
file: mock.pathToContract(truffleConfig, 'ContractA.sol'),
pct: 0
},
{
file: mock.pathToContract(truffleConfig, 'ContractB.sol'),
pct: 0,
},
{
file: mock.pathToContract(truffleConfig, 'ContractC.sol'),
pct: 100,
},
];
verify.lineCoverage(expected);
});
// Truffle test asserts balance is 777 ether
it('config: providerOptions', async function() {
solcoverConfig.providerOptions = { default_balance_ether: 777 }
mock.install('Simple', 'testrpc-options.js', solcoverConfig);
await plugin(truffleConfig);
});
it('config: skipped file', async function() {
solcoverConfig.skipFiles = ['Owned.sol'];
mock.installDouble(
['Proxy', 'Owned'],
'inheritance.js',
solcoverConfig
);
await plugin(truffleConfig);
verify.coverageGenerated(truffleConfig);
const output = mock.getOutput(truffleConfig);
const firstKey = Object.keys(output)[0];
assert(
Object.keys(output).length === 1,
'Wrong # of contracts covered'
);
assert(
firstKey.substr(firstKey.length - 9) === 'Proxy.sol',
'Wrong contract covered'
);
});
it('config: skipped folder', async function() {
mock.installFullProject('skipping');
await plugin(truffleConfig);
const expected = [{
file: mock.pathToContract(truffleConfig, 'ContractA.sol'),
pct: 100
}];
const missing = [{
file: mock.pathToContract(truffleConfig, 'skipped-folder/ContractB.sol'),
}];
verify.lineCoverage(expected);
verify.coverageMissing(missing);
});
it('config: "onServerReady", "onTestsComplete", ...', async function() {
truffleConfig.logger = mock.testLogger;
mock.installFullProject('test-files');
await plugin(truffleConfig);
assert(
mock.loggerOutput.val.includes('running onServerReady') &&
mock.loggerOutput.val.includes('running onTestsComplete') &&
mock.loggerOutput.val.includes('running onIstanbulComplete'),
`Should run "on" hooks : ${mock.loggerOutput.val}`
);
}); });
}) })

Loading…
Cancel
Save