Passing: standard test suite

buidler/draft
cgewecke 5 years ago
parent c91838793e
commit f798671713
  1. 107
      dist/buidler.plugin.js
  2. 71
      dist/plugin-assets/buidler.utils.js
  3. 9
      test/integration/projects/bad-solcoverjs/buidler.config.js
  4. 9
      test/integration/projects/import-paths/buidler.config.js
  5. 1
      test/integration/projects/import-paths/node_modules/package/package.json
  6. 8
      test/integration/projects/libraries/buidler.config.js
  7. 2
      test/integration/projects/multiple-migrations/buidler.config.js
  8. 4
      test/integration/projects/multiple-suites/.solcover.js
  9. 8
      test/integration/projects/multiple-suites/buidler.config.js
  10. 17
      test/integration/projects/multiple-suites/contracts/ContractA.sol
  11. 17
      test/integration/projects/multiple-suites/contracts/ContractB.sol
  12. 17
      test/integration/projects/multiple-suites/contracts/ContractC.sol
  13. 23
      test/integration/projects/multiple-suites/contracts/Migrations.sol
  14. 15
      test/integration/projects/multiple-suites/test/contracta.js
  15. 15
      test/integration/projects/multiple-suites/test/contractb.js
  16. 20
      test/integration/projects/multiple-suites/test/contractc.js
  17. 7
      test/integration/projects/multiple-suites/truffle-config.js
  18. 8
      test/integration/projects/no-sources/buidler.config.js
  19. 9
      test/integration/projects/skipping/buidler.config.js
  20. 1
      test/integration/projects/test-files/.solcover.js
  21. 8
      test/integration/projects/test-files/buidler.config.js
  22. 7
      test/sources/js/truffle-test-fail.js
  23. 222
      test/units/buidler/standard.js
  24. 24
      test/util/integration.js
  25. 12
      test/util/verifiers.js

@ -9,86 +9,44 @@ const path = require('path');
const Web3 = require('web3');
const ganache = require('ganache-cli');
const { task, internalTask, types } = require("@nomiclabs/buidler/config");
const { task, types } = require("@nomiclabs/buidler/config");
const { ensurePluginLoadedWithUsePlugin } = require("@nomiclabs/buidler/plugins");
const { BuidlerPluginError } = require("@nomiclabs/buidler/internal/core/errors");
const { createProvider } = require("@nomiclabs/buidler/internal/core/providers/construction");
const {
TASK_TEST_RUN_MOCHA_TESTS,
TASK_TEST,
TASK_COMPILE,
} = require("@nomiclabs/buidler/builtin-tasks/task-names");
const util = require('util');
function plugin() {
let api;
let address;
let network;
let error;
let testsErrored = false;
// UI for the task flags...
const ui = new PluginUI();
extendEnvironment(env => {
env.config.logger = {log: null};
env.config = buidlerUtils.normalizeConfig(env.config);
api = new API(utils.loadSolcoverJS(env.config));
const networkConfig = {
url: `http://${api.host}:${api.port}`,
gas: api.gasLimit,
gasPrice: api.gasPrice
}
const provider = createProvider(api.defaultNetworkName, networkConfig);
env.config.networks[api.defaultNetworkName] = networkConfig;
env.config.defaultNetwork = api.defaultNetworkName;
env.network = {
name: api.defaultNetworkName,
config: networkConfig,
provider: provider,
}
env.ethereum = provider;
// Keep a reference so we can set the from account
network = env.network;
})
function myTimeout(){
return new Promise(resolve => {
setTimeout(()=>{
console.log('TIMEOUT')
}, 2000)
})
}
task("timeout", 'desc').setAction(async function(taskArguments, { config }, runSuper){
await myTimeout();
});
task("coverage", "Generates a code coverage report for tests")
.addOptionalParam("file", ui.flags.file, null, types.string)
.addOptionalParam("solcoverjs", ui.flags.solcoverjs, null, types.string)
.addOptionalParam('temp', ui.flags.temp, null, types.string)
.setAction(async function(taskArguments, { run, config }, runSuper){
console.log(util.inspect())
.setAction(async function(taskArguments, env){
let error;
let ui;
let api;
let config;
try {
death(buidlerUtils.finish.bind(null, config, api)); // Catch interrupt signals
config.logger = {log: null};
config = buidlerUtils.normalizeConfig(config);
config = buidlerUtils.normalizeConfig(env.config);
ui = new PluginUI(config.logger.log);
api = new API(utils.loadSolcoverJS(config));
// ==============
// Server launch
// ==============
const network = buidlerUtils.setupNetwork(env, api);
const address = await api.ganache(ganache);
const web3 = new Web3(address);
const accounts = await web3.eth.getAccounts();
@ -113,7 +71,10 @@ function plugin() {
// Run post-launch server hook;
await api.onServerReady(config);
// Instrument
// ================
// Instrumentation
// ================
const skipFiles = api.skipFiles || [];
let {
@ -124,7 +85,10 @@ function plugin() {
targets = api.instrument(targets);
utils.reportSkipped(config, skipped);
// Filesystem & Compiler Re-configuration
// ==============
// Compilation
// ==============
const {
tempArtifactsDir,
tempContractsDir
@ -136,27 +100,38 @@ function plugin() {
config.paths.sources = tempContractsDir;
config.paths.artifacts = tempArtifactsDir;
config.paths.cache = buidlerUtils.tempCacheDir(config);
console.log(config.paths.cache)
config.solc.optimizer.enabled = false;
await run(TASK_COMPILE);
await env.run(TASK_COMPILE);
await api.onCompileComplete(config);
// ======
// Tests
// ======
const testFiles = buidlerUtils.getTestFilePaths(config);
try {
failures = await run(TASK_TEST, {testFiles: []})
await env.run(TASK_TEST, {testFiles: testFiles})
} catch (e) {
error = e.stack;
console.log(e.message + error)
error = e;
}
await api.onTestsComplete(config);
// Run Istanbul
// ========
// Istanbul
// ========
await api.report();
await api.onIstanbulComplete(config);
} catch(e) {
error = e;
}
await utils.finish(config, api);
if (error !== undefined ) throw error;
if (process.exitCode > 0) throw new Error(ui.generate('tests-fail', [process.exitCode]));
})
}

@ -1,24 +1,85 @@
const shell = require('shelljs');
const globby = require('globby');
const pluginUtils = require("./plugin.utils");
const path = require('path')
const util = require('util')
const path = require('path');
const util = require('util');
const { createProvider } = require("@nomiclabs/buidler/internal/core/providers/construction");
// =============================
// Buidler Specific Plugin Utils
// =============================
/**
* Returns a list of test files to pass to TASK_TEST.
* @param {BuidlerConfig} config
* @return {String[]} list of files to pass to mocha
*/
function getTestFilePaths(config){
let target;
// Handle --file <path|glob> cli option (subset of tests)
(typeof config.file === 'string')
? target = globby.sync([config.file])
: target = [];
// Return list of test files
const testregex = /.*\.(js|ts|es|es6|jsx)$/;
return target.filter(f => f.match(testregex) != null);
}
/**
* Normalizes buidler paths / logging for use by the plugin utilities and
* attaches them to the config
* @param {BuidlerConfig} config
* @return {BuidlerConfig} updated config
*/
function normalizeConfig(config){
config.workingDir = config.paths.root;
config.contractsDir = config.paths.sources;
config.testDir = config.paths.tests;
config.artifactsDir = config.paths.artifacts;
config.logger = config.logger ? config.logger : {log: null};
return config;
}
function setupNetwork(env, api){
const networkConfig = {
url: `http://${api.host}:${api.port}`,
gas: api.gasLimit,
gasPrice: api.gasPrice
}
const provider = createProvider(api.defaultNetworkName, networkConfig);
env.config.networks[api.defaultNetworkName] = networkConfig;
env.config.defaultNetwork = api.defaultNetworkName;
env.network = {
name: api.defaultNetworkName,
config: networkConfig,
provider: provider,
}
env.ethereum = provider;
// Return a reference so we can set the from account
return env.network;
}
/**
* Generates a path to a temporary compilation cache directory
* @param {BuidlerConfig} config
* @return {String} .../.coverage_cache
*/
function tempCacheDir(config){
return path.join(config.paths.root, '.coverage_cache');
}
/**
* Silently removes temporary folders and calls api.finish to shut server down
* @param {buidlerConfig} config
* @param {BuidlerConfig} config
* @param {SolidityCoverage} api
* @return {Promise}
*/
@ -40,6 +101,8 @@ async function finish(config, api){
module.exports = {
normalizeConfig: normalizeConfig,
finish: finish,
tempCacheDir: tempCacheDir
tempCacheDir: tempCacheDir,
getTestFilePaths: getTestFilePaths,
setupNetwork: setupNetwork
}

@ -1 +1,8 @@
modules.exports={};
const { loadPluginFile } = require("@nomiclabs/buidler/plugins-testing");
loadPluginFile(__dirname + "/../dist/buidler.plugin");
usePlugin("@nomiclabs/buidler-truffle5");
module.exports={
defaultNetwork: "buidlerevm",
logger: process.env.SILENT ? { log: () => {} } : console,
};

@ -1 +1,8 @@
modules.exports={};
const { loadPluginFile } = require("@nomiclabs/buidler/plugins-testing");
loadPluginFile(__dirname + "/../dist/buidler.plugin");
usePlugin("@nomiclabs/buidler-truffle5");
module.exports={
defaultNetwork: "buidlerevm",
logger: process.env.SILENT ? { log: () => {} } : console,
};

@ -1 +1,7 @@
modules.exports={};
const { loadPluginFile } = require("@nomiclabs/buidler/plugins-testing");
loadPluginFile(__dirname + "/../dist/buidler.plugin");
usePlugin("@nomiclabs/buidler-truffle5");
module.exports={
logger: process.env.SILENT ? { log: () => {} } : console,
};

@ -0,0 +1,4 @@
module.exports = {
"silent": false,
"istanbulReporter": [ "json-summary", "text"]
}

@ -0,0 +1,8 @@
const { loadPluginFile } = require("@nomiclabs/buidler/plugins-testing");
loadPluginFile(__dirname + "/../dist/buidler.plugin");
usePlugin("@nomiclabs/buidler-truffle5");
module.exports={
defaultNetwork: "buidlerevm",
logger: process.env.SILENT ? { log: () => {} } : console,
};

@ -0,0 +1,17 @@
pragma solidity ^0.5.0;
contract ContractA {
uint x;
constructor() public {
}
function sendFn() public {
x = 5;
}
function callFn() public pure returns (uint){
uint y = 5;
return y;
}
}

@ -0,0 +1,17 @@
pragma solidity ^0.5.0;
contract ContractB {
uint x;
constructor() public {
}
function sendFn() public {
x = 5;
}
function callFn() public pure returns (uint){
uint y = 5;
return y;
}
}

@ -0,0 +1,17 @@
pragma solidity ^0.5.0;
contract ContractC {
uint x;
constructor() public {
}
function sendFn() public {
x = 5;
}
function callFn() public pure returns (uint){
uint y = 5;
return y;
}
}

@ -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);
}
}

@ -0,0 +1,15 @@
const ContractA = artifacts.require("ContractA");
contract("contracta", function(accounts) {
let instance;
before(async () => instance = await ContractA.new())
it('sends [ @skipForCoverage ]', async function(){
await instance.sendFn();
});
it('calls [ @skipForCoverage ]', async function(){
await instance.callFn();
})
});

@ -0,0 +1,15 @@
const ContractB = artifacts.require("ContractB");
contract("contractB [ @skipForCoverage ]", function(accounts) {
let instance;
before(async () => instance = await ContractB.new())
it('sends', async function(){
await instance.sendFn();
});
it('calls', async function(){
await instance.callFn();
})
});

@ -0,0 +1,20 @@
const ContractC = artifacts.require("ContractC");
contract("contractc", function(accounts) {
let instance;
before(async () => instance = await ContractC.new())
it('sends', async function(){
await instance.sendFn();
});
it('calls', async function(){
await instance.callFn();
})
it('sends', async function(){
await instance.sendFn();
});
});

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

@ -1 +1,7 @@
modules.exports={};
const { loadPluginFile } = require("@nomiclabs/buidler/plugins-testing");
loadPluginFile(__dirname + "/../dist/buidler.plugin");
usePlugin("@nomiclabs/buidler-truffle5");
module.exports={
logger: process.env.SILENT ? { log: () => {} } : console,
};

@ -1 +1,8 @@
modules.exports={};
const { loadPluginFile } = require("@nomiclabs/buidler/plugins-testing");
loadPluginFile(__dirname + "/../dist/buidler.plugin");
usePlugin("@nomiclabs/buidler-truffle5");
module.exports={
defaultNetwork: "buidlerevm",
logger: process.env.SILENT ? { log: () => {} } : console,
};

@ -2,6 +2,7 @@
const fn = (msg, config) => config.logger.log(msg);
module.exports = {
skipFiles: ['Migrations.sol'],
silent: process.env.SILENT ? true : false,
istanbulReporter: ['json-summary', 'text'],
onServerReady: fn.bind(null, 'running onServerReady'),

@ -1 +1,7 @@
modules.exports={};
const { loadPluginFile } = require("@nomiclabs/buidler/plugins-testing");
loadPluginFile(__dirname + "/../dist/buidler.plugin");
usePlugin("@nomiclabs/buidler-truffle5");
module.exports={
defaultNetwork: "buidlerevm",
};

@ -10,4 +10,11 @@ contract('Simple', () => {
const val = await simple.getX();
assert.equal(val.toNumber(), 4) // <-- Wrong result: test fails
});
it('should set x to 2', async function() {
let simple = await Simple.new();
await simple.test(5);
const val = await simple.getX();
assert.equal(val.toNumber(), 2) // <-- Wrong result: test fails
});
});

@ -7,15 +7,11 @@ const verify = require('../../util/verifiers')
const mock = require('../../util/integration');
const plugin = require('../../../dist/buidler.plugin');
const {
TASK_TEST
} = require("@nomiclabs/buidler/builtin-tasks/task-names");
// =======================
// Standard Use-case Tests
// =======================
describe.only('Buidler Plugin: standard use cases', function() {
describe('Buidler Plugin: standard use cases', function() {
let buidlerConfig;
let solcoverConfig;
@ -23,7 +19,7 @@ describe.only('Buidler Plugin: standard use cases', function() {
mock.clean();
mock.loggerOutput.val = '';
solcoverConfig = {};
solcoverConfig = { skipFiles: ['Migrations.sol']};
buidlerConfig = mock.getDefaultBuidlerConfig();
verify.cleanInitialState();
})
@ -33,18 +29,13 @@ describe.only('Buidler Plugin: standard use cases', function() {
mock.clean();
});
/*it.only('timeout', async function(){
mock.buidlerSetupEnv(this);
this.env.run('timeout')
});*/
it('simple contract', async function(){
mock.install('Simple', 'simple.js', solcoverConfig);
mock.buidlerSetupEnv(this);
await this.env.run("coverage");
/*verify.coverageGenerated(buidlerConfig);
verify.coverageGenerated(buidlerConfig);
const output = mock.getOutput(buidlerConfig);
const path = Object.keys(output)[0];
@ -57,6 +48,211 @@ describe.only('Buidler Plugin: standard use cases', function() {
assert(
output[path].fnMap['2'].name === 'getX',
'coverage.json missing "getX"'
);*/
);
});
it('with relative path solidity imports', async function() {
mock.installFullProject('import-paths');
mock.buidlerSetupEnv(this);
await this.env.run("coverage");
});
it('uses inheritance', async function() {
mock.installDouble(
['Proxy', 'Owned'],
'inheritance.js',
solcoverConfig
);
mock.buidlerSetupEnv(this);
await this.env.run("coverage");
verify.coverageGenerated(buidlerConfig);
const output = mock.getOutput(buidlerConfig);
const ownedPath = Object.keys(output)[0];
const proxyPath = Object.keys(output)[1];
assert(
output[ownedPath].fnMap['1'].name === 'constructor',
'"constructor" not covered'
);
assert(
output[proxyPath].fnMap['1'].name === 'isOwner',
'"isOwner" not covered'
);
});
it('only uses ".call"', async function(){
mock.install('OnlyCall', 'only-call.js', solcoverConfig);
mock.buidlerSetupEnv(this);
await this.env.run("coverage");
verify.coverageGenerated(buidlerConfig);
const output = mock.getOutput(buidlerConfig);
const path = Object.keys(output)[0];
assert(
output[path].fnMap['1'].name === 'addTwo',
'cov should map "addTwo"'
);
});
it('sends / transfers to instrumented fallback', async function(){
mock.install('Wallet', 'wallet.js', solcoverConfig);
mock.buidlerSetupEnv(this);
await this.env.run("coverage");
verify.coverageGenerated(buidlerConfig);
const output = mock.getOutput(buidlerConfig);
const path = Object.keys(output)[0];
assert(
output[path].fnMap['1'].name === 'transferPayment',
'cov should map "transferPayment"'
);
});
// Truffle test asserts deployment cost is greater than 20,000,000 gas
it('deployment cost > block gasLimit', async function() {
mock.install('Expensive', 'block-gas-limit.js', solcoverConfig);
mock.buidlerSetupEnv(this);
await this.env.run("coverage");
});
// Simple.sol with a failing assertion in a truffle test
it('unit tests failing', async function() {
mock.install('Simple', 'truffle-test-fail.js', solcoverConfig);
mock.buidlerSetupEnv(this);
try {
await this.env.run("coverage");
assert.fail()
} catch(err){
assert(err.message.includes('failed under coverage'));
}
verify.coverageGenerated(buidlerConfig);
const output = mock.getOutput(buidlerConfig);
const path = Object.keys(output)[0];
assert(output[path].fnMap['1'].name === 'test', 'cov missing "test"');
assert(output[path].fnMap['2'].name === 'getX', 'cov missing "getX"');
});
// 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-suites', solcoverConfig);
mock.buidlerSetupEnv(this);
await this.env.run("coverage");
const expected = [
{
file: mock.pathToContract(buidlerConfig, 'ContractA.sol'),
pct: 0
},
{
file: mock.pathToContract(buidlerConfig, 'ContractB.sol'),
pct: 0,
},
{
file: mock.pathToContract(buidlerConfig, '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);
mock.buidlerSetupEnv(this);
await this.env.run("coverage");
});
it('config: skipped file', async function() {
solcoverConfig.skipFiles = ['Migrations.sol', 'Owned.sol'];
mock.installDouble(
['Proxy', 'Owned'],
'inheritance.js',
solcoverConfig
);
mock.buidlerSetupEnv(this);
await this.env.run("coverage");
verify.coverageGenerated(buidlerConfig);
const output = mock.getOutput(buidlerConfig);
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');
mock.buidlerSetupEnv(this);
await this.env.run("coverage");
const expected = [{
file: mock.pathToContract(buidlerConfig, 'ContractA.sol'),
pct: 100
}];
const missing = [{
file: mock.pathToContract(buidlerConfig, 'skipped-folder/ContractB.sol'),
}];
verify.lineCoverage(expected);
verify.coverageMissing(missing);
});
it('config: "onServerReady", "onTestsComplete", ...', async function() {
mock.installFullProject('test-files');
mock.buidlerSetupEnv(this);
await this.env.run("coverage");
assert(
mock.loggerOutput.val.includes('running onServerReady') &&
mock.loggerOutput.val.includes('running onTestsComplete') &&
mock.loggerOutput.val.includes('running onCompileComplete') &&
mock.loggerOutput.val.includes('running onIstanbulComplete'),
`Should run "on" hooks : ${mock.loggerOutput.val}`
);
});
})

@ -47,8 +47,9 @@ function pathToContract(config, file) {
return path.join('contracts', file);
}
function getOutput(truffleConfig){
const jsonPath = path.join(truffleConfig.working_directory, "coverage.json");
function getOutput(config){
const workingDir = config.working_directory || config.paths.root;
const jsonPath = path.join(workingDir, "coverage.json");
return JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
}
@ -58,6 +59,8 @@ function buidlerSetupEnv(mocha) {
previousCWD = process.cwd();
process.chdir(mockwd);
mocha.env = require("@nomiclabs/buidler");
mocha.env.config.logger = testLogger
mocha.logger = testLogger
};
// Buidler env tear down
@ -119,24 +122,23 @@ function getDefaultBuidlerConfig() {
const mockwd = path.join(process.cwd(), temp);
const vals = {
root: mockwd,
artifacts: path.join(mockwd, 'artifacts'),
cache: path.join(mockwd, 'cache'),
sources: path.join(mockwd, 'contracts'),
tests: path.join(mockwd, 'test'),
paths : {
root: mockwd,
artifacts: path.join(mockwd, 'artifacts'),
cache: path.join(mockwd, 'cache'),
sources: path.join(mockwd, 'contracts'),
tests: path.join(mockwd, 'test'),
},
logger: logger,
mocha: {
reporter: reporter
},
defaultNetwork: "buidlerevm",
networks: {
development: {
url: "http://127.0.0.1:8545",
}
},
solc: {
version: "0.5.3",
optimizer: {}
}
}
return vals;

@ -28,14 +28,18 @@ function cleanInitialState(){
assert(pathExists('./coverage.json') === false, 'should start without: coverage.json');
}
function coverageGenerated(truffleConfig){
const jsonPath = path.join(truffleConfig.working_directory, "coverage.json");
function coverageGenerated(config){
const workingDir = config.working_directory || config.paths.root;
const jsonPath = path.join(workingDir, "coverage.json");
assert(pathExists('./coverage') === true, 'should gen coverage folder');
assert(pathExists(jsonPath) === true, 'should gen coverage.json');
}
function coverageNotGenerated(truffleConfig){
const jsonPath = path.join(truffleConfig.working_directory, "coverage.json");
function coverageNotGenerated(config){
const workingDir = config.working_directory || config.paths.root;
const jsonPath = path.join(workingDir, "coverage.json");
assert(pathExists('./coverage') !== true, 'should NOT gen coverage folder');
assert(pathExists(jsonPath) !== true, 'should NOT gen coverage.json');
}

Loading…
Cancel
Save