From 901430ddf95a3504a881b182a5fbee6ac763e0f2 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Fri, 13 Sep 2019 20:56:24 -0700 Subject: [PATCH] Handle native solidity tests / add test logger fixtures (#398) --- README.md | 11 +++--- dist/truffle.plugin.js | 14 +++++--- lib/app.js | 2 +- lib/ui.js | 16 +++++---- scripts/run-metacoin.sh | 12 ++++--- test/sources/js/TestSimple.sol | 16 +++++++++ test/units/app.js | 43 ++++++++++++++++++++-- test/util/integration.truffle.js | 62 ++++++++++++++++++++++---------- 8 files changed, 134 insertions(+), 42 deletions(-) create mode 100644 test/sources/js/TestSimple.sol diff --git a/README.md b/README.md index 937322f..abc1d19 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ $ npm install --save-dev solidity-coverage ### Usage notes: + For pragma solidity >=0.4.22 <0.6.0 / Petersburg + Coverage runs tests a little more slowly. -+ Coverage distorts gas consumption. ++ Coverage distorts gas consumption. + Coverage launches its own in-process ganache instance. You can set [ganache options](https://github.com/trufflesuite/ganache-core#options) via the `providerOptions` key in your `.solcover.js` config file. @@ -45,10 +45,10 @@ truffle run coverage [options] ### Command Options | Option | Example | Description | |--------------|--------------------------------|-------------| -| --file | --file="test/registry/*.js" | Filename or glob describing a subset of JS tests to run. (Globs must be enclosed by quotes.)| -| --solcoverjs | --solcoverjs ./../.solcover.js | Relative path from working directory to config. Useful for monorepo packages that share settings. (Path must be "./" prefixed) | -| --version | | Version info | -| --help | | Usage notes | +| `--file` | `--file="test/registry/*.js"` | Filename or glob describing a subset of JS tests to run. (Globs must be enclosed by quotes.)| +| `--solcoverjs` | `--solcoverjs ./../.solcover.js` | Relative path from working directory to config. Useful for monorepo packages that share settings. (Path must be "./" prefixed) | +| `--version` | | Version info | +| `--help` | | Usage notes | || || ### Config Options @@ -70,6 +70,7 @@ module.exports = { | providerOptions | *Object* | {} | ganache-core options (ex: `network_id: 55`). Complete list of options [here](https://github.com/trufflesuite/ganache-core#options). | | skipFiles | *Array* | `['Migrations.sol']` | Array of contracts or folders (with paths expressed relative to the `contracts` directory) that should be skipped when doing instrumentation. `Migrations.sol` is skipped by default, and does not need to be added to this configuration option if it is used. | | istanbulReporter | *Array* | ['html', 'lcov', 'text'] | Coverage reporters for Istanbul. Optional reporter replaces the default reporters. | +| silent | *Boolean* | false | suppress logging output | ### FAQ diff --git a/dist/truffle.plugin.js b/dist/truffle.plugin.js index 8eed257..0fba982 100644 --- a/dist/truffle.plugin.js +++ b/dist/truffle.plugin.js @@ -53,6 +53,7 @@ async function plugin(truffleConfig){ coverageConfig = req.silent(solcoverjs) || {}; coverageConfig.cwd = truffleConfig.working_directory; coverageConfig.originalContractsDir = truffleConfig.contracts_directory; + coverageConfig.log = coverageConfig.log || truffleConfig.logger.log; app = new App(coverageConfig); @@ -94,7 +95,7 @@ async function plugin(truffleConfig){ // Additional config truffleConfig.all = true; - truffleConfig.test_files = tests(truffleConfig); + truffleConfig.test_files = tests(app, truffleConfig); truffleConfig.compilers.solc.settings.optimizer.enabled = false; // Compile @@ -141,15 +142,20 @@ async function plugin(truffleConfig){ // -------------------------------------- Helpers -------------------------------------------------- -function tests(truffle){ +function tests(app, truffle){ let target; (typeof truffle.file === 'string') ? target = globby.sync([truffle.file]) : target = dir.files(truffle.test_directory, { sync: true }) || []; - const regex = /.*\.(js|ts|es|es6|jsx|sol)$/; - return target.filter(f => f.match(regex) != null); + const solregex = /.*\.(sol)$/; + const hasSols = target.filter(f => f.match(solregex) != null); + + if (hasSols.length > 0) app.ui.report('sol-tests', [hasSols.length]); + + const testregex = /.*\.(js|ts|es|es6|jsx)$/; + return target.filter(f => f.match(testregex) != null); } diff --git a/lib/app.js b/lib/app.js index bf79b0f..16c8058 100644 --- a/lib/app.js +++ b/lib/app.js @@ -38,7 +38,7 @@ class App { this.skippedFolders = []; this.skipFiles = config.skipFiles || []; - this.log = config.logger ? config.logger.log : console.log; + this.log = config.log || console.log; this.setLoggingLevel(config.silent); this.gasLimit = 0xfffffffffff; diff --git a/lib/ui.js b/lib/ui.js index 88cb170..b0b2dd3 100644 --- a/lib/ui.js +++ b/lib/ui.js @@ -20,18 +20,22 @@ class UI { report(kind, args=[]){ const ct = c.bold.green('>'); const ds = c.bold.yellow('>'); + const w = ":warning:"; const kinds = { - 'vm-fail': `:warning: ${c.red('There was a problem attaching to the ganache-core VM.')} `+ - `${c.red('Check the provider option syntax in solidity-coverage docs.')}\n`+ - `:warning: ${c.red('Using ganache-core-sc (eq. core v2.7.0) instead.')}\n`, + 'vm-fail': `${w} ${c.red('There was a problem attaching to the ganache-core VM.')} `+ + `${c.red('Check the provider option syntax in solidity-coverage docs.')}\n`+ + `${w} ${c.red('Using ganache-core-sc (eq. core v2.7.0) instead.')}\n`, + + 'sol-tests': `\n${w} ${c.red("This plugin cannot run Truffle's native solidity tests: ")}`+ + `${args[0]} test(s) will be skipped.\n`, 'truffle-local': `\n${ct} ${c.grey('Using Truffle library from local node_modules.')}\n`, 'truffle-global': `\n${ct} ${c.grey('Using Truffle library from global node_modules.')}\n`, - 'truffle-warn': `:warning: ${c.red('Unable to require Truffle library locally or globally. ')} `+ - `${c.red('Expected to find installed Truffle >= v5.0.31 ...')}\n` + - `:warning: ${c.red('Using fallback Truffle library instead (v5.0.31)')}\n`, + 'truffle-warn': `${w} ${c.red('Unable to require Truffle library locally or globally. ')} `+ + `${c.red('Expected to find installed Truffle >= v5.0.31 ...')}\n` + + `${w} ${c.red('Using fallback Truffle library instead (v5.0.31)')}\n`, 'truffle-help': `Usage: truffle run coverage [options]\n\n` + `Options:\n` + diff --git a/scripts/run-metacoin.sh b/scripts/run-metacoin.sh index 0a68f51..29debd0 100755 --- a/scripts/run-metacoin.sh +++ b/scripts/run-metacoin.sh @@ -2,6 +2,8 @@ # # E2E CI: installs PR candidate on Truffle's MetaCoin and runs coverage # +# Also verifies that everything works w/ truffle installed globally. +# set -o errexit @@ -22,18 +24,18 @@ echo "PR_PATH >>>>> $PR_PATH" # Install truffle and metacoin box npm install -g truffle +npm install -g yarn + mkdir metacoin cd metacoin truffle unbox metacoin --force + +# Install config with plugin rm truffle-config.js echo "module.exports={plugins:['solidity-coverage']}" > truffle-config.js cat truffle-config.js -# TODO: Remove this rm. -# Unknown bug running truffle native solidity tests -rm test/TestMetaCoin.sol - # Install and run solidity-coverage @ PR npm init --yes -npm install --save-dev $PR_PATH +yarn add $PR_PATH --dev npx truffle run coverage diff --git a/test/sources/js/TestSimple.sol b/test/sources/js/TestSimple.sol new file mode 100644 index 0000000..bf36bee --- /dev/null +++ b/test/sources/js/TestSimple.sol @@ -0,0 +1,16 @@ +pragma solidity >=0.4.25 <0.6.0; + +import "truffle/Assert.sol"; +import "truffle/DeployedAddresses.sol"; +import "../contracts/Simple.sol"; + +contract TestSimple { + + function test_x_is_0() public { + Simple simple = Simple(DeployedAddresses.Simple()); + + uint expected = 0; + + Assert.equal(simple.x, expected, "x should equal 0"); + } +} \ No newline at end of file diff --git a/test/units/app.js b/test/units/app.js index c3422f5..c296108 100644 --- a/test/units/app.js +++ b/test/units/app.js @@ -59,11 +59,9 @@ describe('app', function() { beforeEach(() => { mock.clean(); + mock.loggerOutput.val = ''; solcoverConfig = {}; truffleConfig = mock.getDefaultTruffleConfig(); - - if (process.env.SILENT) - solcoverConfig.silent = true; }) afterEach(() => mock.clean()); @@ -149,6 +147,20 @@ describe('app', function() { await plugin(truffleConfig); }); + it('project contains native solidity tests', async function(){ + assertCleanInitialState(); + + mock.install('Simple', 'TestSimple.sol', solcoverConfig); + + truffleConfig.logger = mock.testLogger; + await plugin(truffleConfig); + + assert( + mock.loggerOutput.val.includes('native solidity tests'), + `Should warn it is skipping native solidity tests (output --> ${mock.loggerOutput.val}` + ); + }); + it('truffle run coverage --config ../.solcover.js', async function() { assertCleanInitialState(); @@ -179,15 +191,40 @@ describe('app', function() { it('truffle run coverage --help', async function(){ assertCleanInitialState(); truffleConfig.help = "true"; + + truffleConfig.logger = mock.testLogger; mock.install('Simple', 'simple.js', solcoverConfig); await plugin(truffleConfig); + + assert( + mock.loggerOutput.val.includes('Usage'), + `Should output help with Usage instruction (output --> ${mock.loggerOutput.val}` + ); }) it('truffle run coverage --version', async function(){ assertCleanInitialState(); truffleConfig.version = "true"; + + truffleConfig.logger = mock.testLogger; mock.install('Simple', 'simple.js', solcoverConfig); await plugin(truffleConfig); + + assert( + mock.loggerOutput.val.includes('truffle'), + `Should output truffle version (output --> ${mock.loggerOutput.val}` + ); + + assert( + mock.loggerOutput.val.includes('ganache-core'), + `Should output ganache-core version (output --> ${mock.loggerOutput.val}` + ); + + assert( + mock.loggerOutput.val.includes('solidity-coverage'), + `Should output solidity-coverage version (output --> ${mock.loggerOutput.val}` + ); + }) it('truffle run coverage --file test/', async function() { diff --git a/test/util/integration.truffle.js b/test/util/integration.truffle.js index c119890..14039b7 100644 --- a/test/util/integration.truffle.js +++ b/test/util/integration.truffle.js @@ -18,6 +18,29 @@ const migrationPath = `${temp}/migrations/2_deploy.js`; const templatePath = './test/integration/truffle/*'; const projectPath = './test/integration/projects/' + +// ========================== +// Misc Utils +// ========================== +function decacheConfigs(){ + decache(`${process.cwd()}/${temp}/.solcover.js`); + decache(`${process.cwd()}/${temp}/${truffleConfigName}`); +} + +function clean() { + shell.config.silent = true; + + shell.rm('-Rf', temp); + shell.rm('-Rf', 'coverage'); + shell.rm('coverage.json'); + shell.rm('.solcover.js'); + + shell.config.silent = false; +}; + +// ========================== +// Configuration +// ========================== function getDefaultTruffleConfig(){ const logger = process.env.SILENT ? { log: () => {} } : console; const reporter = process.env.SILENT ? 'dot' : 'spec'; @@ -59,6 +82,9 @@ function getTruffleConfigJS(config){ return `module.exports = ${JSON.stringify(getDefaultTruffleConfig(), null, ' ')}` } +// ========================== +// Migration Generators +// ========================== function deploySingle(contractName){ return ` const A = artifacts.require("${contractName}"); @@ -78,11 +104,9 @@ function deployDouble(contractNames){ `; } -function decacheConfigs(){ - decache(`${process.cwd()}/${temp}/.solcover.js`); - decache(`${process.cwd()}/${temp}/${truffleConfigName}`); -} - +// ========================== +// Project Installers +// ========================== /** * Installs mock truffle project at ./temp with a single contract * and test specified by the params. @@ -161,23 +185,25 @@ function installFullProject(name) { decacheConfigs(); } - -/** - * Removes mock truffle project and coverage reports generated by test - */ -function clean() { - shell.config.silent = true; - - shell.rm('-Rf', temp); - shell.rm('-Rf', 'coverage'); - shell.rm('coverage.json'); - shell.rm('.solcover.js'); - - shell.config.silent = false; +// ========================== +// Logging +// ========================== +const loggerOutput = { + val: '' }; +const testLogger = { + log: (val) => { + if (val !== undefined) loggerOutput.val += val; + if (!process.env.SILENT && val !== undefined) + console.log(val) + } +} + module.exports = { + testLogger: testLogger, + loggerOutput: loggerOutput, getDefaultTruffleConfig: getDefaultTruffleConfig, install: install, installDouble: installDouble,