From 3bd99ab6dde1498d10434ca54a443d99d20a7e8e Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sun, 15 Sep 2019 00:18:28 -0700 Subject: [PATCH] Reorganize and cleanup truffle integration tests (#400) --- .circleci/config.yml | 3 +- package.json | 6 +- test/integration/projects/.solcover.js | 0 test/integration/projects/bad-solcover.js | 0 .../projects/bad-solcoverjs/truffle-config.js | 98 +--- .../projects/import-paths/truffle-config.js | 98 +--- .../multiple-migrations/truffle-config.js | 98 +--- .../projects/no-sources/truffle-config.js | 98 +--- .../projects/skipping/truffle-config.js | 98 +--- .../projects/test-files/truffle-config.js | 98 +--- test/units/app.js | 527 ------------------ test/units/truffle/errors.js | 151 +++++ test/units/truffle/flags.js | 200 +++++++ test/units/truffle/standard.js | 187 +++++++ test/util/integration.truffle.js | 13 +- test/util/util.js | 3 +- test/util/verifiers.js | 41 ++ 17 files changed, 616 insertions(+), 1103 deletions(-) delete mode 100644 test/integration/projects/.solcover.js delete mode 100644 test/integration/projects/bad-solcover.js delete mode 100644 test/units/app.js create mode 100644 test/units/truffle/errors.js create mode 100644 test/units/truffle/flags.js create mode 100644 test/units/truffle/standard.js create mode 100644 test/util/verifiers.js diff --git a/.circleci/config.yml b/.circleci/config.yml index 6148459..4715295 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -78,7 +78,8 @@ workflows: build: jobs: - unit-test - - e2e-zeppelin + # TODO: re-enable when server logic is added + #- e2e-zeppelin - e2e-metacoin # TODO: re-enable. # At the moment we're using forks so this is pointless diff --git a/package.json b/package.json index bb3bf89..8c70e44 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,9 @@ }, "scripts": { "nyc": "SILENT=true nyc --exclude '**/sc_temp/**' --exclude '**/test/**'", - "test": "npm run nyc -- mocha test/units --timeout 70000 --no-warnings --exit", - "test:ci": "SILENT=true nyc --reporter=lcov --exclude '**/sc_temp/**' --exclude '**/test/**' -- mocha test/units --timeout 70000 --no-warnings --exit", - "test:debug": "mocha test/units --timeout 70000 --no-warnings --exit" + "test": "npm run nyc -- mocha test/units/* --timeout 100000 --no-warnings --exit", + "test:ci": "SILENT=true nyc --reporter=lcov --exclude '**/sc_temp/**' --exclude '**/test/**/' -- mocha test/units/* --timeout 100000 --no-warnings --exit", + "test:debug": "mocha test/units/* --timeout 100000 --no-warnings --exit" }, "homepage": "https://github.com/sc-forks/solidity-coverage", "repository": { diff --git a/test/integration/projects/.solcover.js b/test/integration/projects/.solcover.js deleted file mode 100644 index e69de29..0000000 diff --git a/test/integration/projects/bad-solcover.js b/test/integration/projects/bad-solcover.js deleted file mode 100644 index e69de29..0000000 diff --git a/test/integration/projects/bad-solcoverjs/truffle-config.js b/test/integration/projects/bad-solcoverjs/truffle-config.js index 3865690..b398b07 100644 --- a/test/integration/projects/bad-solcoverjs/truffle-config.js +++ b/test/integration/projects/bad-solcoverjs/truffle-config.js @@ -1,99 +1,7 @@ -/** - * Use this file to configure your truffle project. It's seeded with some - * common settings for different networks and features like migrations, - * compilation and testing. Uncomment the ones you need or modify - * them to suit your project as necessary. - * - * More information about configuration can be found at: - * - * truffleframework.com/docs/advanced/configuration - * - * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider) - * to sign your transactions before they're sent to a remote public node. Infura accounts - * are available for free at: infura.io/register. - * - * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate - * public/private key pairs. If you're publishing your code to GitHub make sure you load this - * phrase from a file you've .gitignored so it doesn't accidentally become public. - * - */ - -// const HDWalletProvider = require('truffle-hdwallet-provider'); -// const infuraKey = "fj4jll3k....."; -// -// const fs = require('fs'); -// const mnemonic = fs.readFileSync(".secret").toString().trim(); - module.exports = { - /** - * Networks define how you connect to your ethereum client and let you set the - * defaults web3 uses to send transactions. If you don't specify one truffle - * will spin up a development blockchain for you on port 9545 when you - * run `develop` or `test`. You can ask a truffle command to use a specific - * network from the command line, e.g - * - * $ truffle test --network - */ - - networks: { - // Useful for testing. The `development` name is special - truffle uses it by default - // if it's defined here and no other network is specified at the command line. - // You should run a client (like ganache-cli, geth or parity) in a separate terminal - // tab if you use this network and you must also set the `host`, `port` and `network_id` - // options below to some value. - // - // development: { - // host: "127.0.0.1", // Localhost (default: none) - // port: 8545, // Standard Ethereum port (default: none) - // network_id: "*", // Any network (default: none) - // }, - - // Another network with more advanced options... - // advanced: { - // port: 8777, // Custom port - // network_id: 1342, // Custom network - // gas: 8500000, // Gas sent with each transaction (default: ~6700000) - // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) - // from:
, // Account to send txs from (default: accounts[0]) - // websockets: true // Enable EventEmitter interface for web3 (default: false) - // }, - - // Useful for deploying to a public network. - // NB: It's important to wrap the provider as a function. - // ropsten: { - // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`), - // network_id: 3, // Ropsten's id - // gas: 5500000, // Ropsten has a lower block limit than mainnet - // confirmations: 2, // # of confs to wait between deployments. (default: 0) - // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) - // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) - // }, - - // Useful for private networks - // private: { - // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), - // network_id: 2111, // This network is yours, in the cloud. - // production: true // Treats this network as if it was a public net. (default: false) - // } - }, - - // Set default mocha options here, use special reporters etc. - mocha: { - // timeout: 100000 - }, - - // Configure your compilers + networks: {}, + mocha: {}, compilers: { - solc: { - // version: "0.5.1", // Fetch exact version from solc-bin (default: truffle's version) - // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) - // settings: { // See the solidity docs for advice about optimization and evmVersion - // optimizer: { - // enabled: false, - // runs: 200 - // }, - // evmVersion: "byzantium" - // } - } + solc: {} } } diff --git a/test/integration/projects/import-paths/truffle-config.js b/test/integration/projects/import-paths/truffle-config.js index 3865690..b398b07 100644 --- a/test/integration/projects/import-paths/truffle-config.js +++ b/test/integration/projects/import-paths/truffle-config.js @@ -1,99 +1,7 @@ -/** - * Use this file to configure your truffle project. It's seeded with some - * common settings for different networks and features like migrations, - * compilation and testing. Uncomment the ones you need or modify - * them to suit your project as necessary. - * - * More information about configuration can be found at: - * - * truffleframework.com/docs/advanced/configuration - * - * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider) - * to sign your transactions before they're sent to a remote public node. Infura accounts - * are available for free at: infura.io/register. - * - * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate - * public/private key pairs. If you're publishing your code to GitHub make sure you load this - * phrase from a file you've .gitignored so it doesn't accidentally become public. - * - */ - -// const HDWalletProvider = require('truffle-hdwallet-provider'); -// const infuraKey = "fj4jll3k....."; -// -// const fs = require('fs'); -// const mnemonic = fs.readFileSync(".secret").toString().trim(); - module.exports = { - /** - * Networks define how you connect to your ethereum client and let you set the - * defaults web3 uses to send transactions. If you don't specify one truffle - * will spin up a development blockchain for you on port 9545 when you - * run `develop` or `test`. You can ask a truffle command to use a specific - * network from the command line, e.g - * - * $ truffle test --network - */ - - networks: { - // Useful for testing. The `development` name is special - truffle uses it by default - // if it's defined here and no other network is specified at the command line. - // You should run a client (like ganache-cli, geth or parity) in a separate terminal - // tab if you use this network and you must also set the `host`, `port` and `network_id` - // options below to some value. - // - // development: { - // host: "127.0.0.1", // Localhost (default: none) - // port: 8545, // Standard Ethereum port (default: none) - // network_id: "*", // Any network (default: none) - // }, - - // Another network with more advanced options... - // advanced: { - // port: 8777, // Custom port - // network_id: 1342, // Custom network - // gas: 8500000, // Gas sent with each transaction (default: ~6700000) - // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) - // from:
, // Account to send txs from (default: accounts[0]) - // websockets: true // Enable EventEmitter interface for web3 (default: false) - // }, - - // Useful for deploying to a public network. - // NB: It's important to wrap the provider as a function. - // ropsten: { - // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`), - // network_id: 3, // Ropsten's id - // gas: 5500000, // Ropsten has a lower block limit than mainnet - // confirmations: 2, // # of confs to wait between deployments. (default: 0) - // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) - // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) - // }, - - // Useful for private networks - // private: { - // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), - // network_id: 2111, // This network is yours, in the cloud. - // production: true // Treats this network as if it was a public net. (default: false) - // } - }, - - // Set default mocha options here, use special reporters etc. - mocha: { - // timeout: 100000 - }, - - // Configure your compilers + networks: {}, + mocha: {}, compilers: { - solc: { - // version: "0.5.1", // Fetch exact version from solc-bin (default: truffle's version) - // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) - // settings: { // See the solidity docs for advice about optimization and evmVersion - // optimizer: { - // enabled: false, - // runs: 200 - // }, - // evmVersion: "byzantium" - // } - } + solc: {} } } diff --git a/test/integration/projects/multiple-migrations/truffle-config.js b/test/integration/projects/multiple-migrations/truffle-config.js index 3865690..b398b07 100644 --- a/test/integration/projects/multiple-migrations/truffle-config.js +++ b/test/integration/projects/multiple-migrations/truffle-config.js @@ -1,99 +1,7 @@ -/** - * Use this file to configure your truffle project. It's seeded with some - * common settings for different networks and features like migrations, - * compilation and testing. Uncomment the ones you need or modify - * them to suit your project as necessary. - * - * More information about configuration can be found at: - * - * truffleframework.com/docs/advanced/configuration - * - * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider) - * to sign your transactions before they're sent to a remote public node. Infura accounts - * are available for free at: infura.io/register. - * - * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate - * public/private key pairs. If you're publishing your code to GitHub make sure you load this - * phrase from a file you've .gitignored so it doesn't accidentally become public. - * - */ - -// const HDWalletProvider = require('truffle-hdwallet-provider'); -// const infuraKey = "fj4jll3k....."; -// -// const fs = require('fs'); -// const mnemonic = fs.readFileSync(".secret").toString().trim(); - module.exports = { - /** - * Networks define how you connect to your ethereum client and let you set the - * defaults web3 uses to send transactions. If you don't specify one truffle - * will spin up a development blockchain for you on port 9545 when you - * run `develop` or `test`. You can ask a truffle command to use a specific - * network from the command line, e.g - * - * $ truffle test --network - */ - - networks: { - // Useful for testing. The `development` name is special - truffle uses it by default - // if it's defined here and no other network is specified at the command line. - // You should run a client (like ganache-cli, geth or parity) in a separate terminal - // tab if you use this network and you must also set the `host`, `port` and `network_id` - // options below to some value. - // - // development: { - // host: "127.0.0.1", // Localhost (default: none) - // port: 8545, // Standard Ethereum port (default: none) - // network_id: "*", // Any network (default: none) - // }, - - // Another network with more advanced options... - // advanced: { - // port: 8777, // Custom port - // network_id: 1342, // Custom network - // gas: 8500000, // Gas sent with each transaction (default: ~6700000) - // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) - // from:
, // Account to send txs from (default: accounts[0]) - // websockets: true // Enable EventEmitter interface for web3 (default: false) - // }, - - // Useful for deploying to a public network. - // NB: It's important to wrap the provider as a function. - // ropsten: { - // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`), - // network_id: 3, // Ropsten's id - // gas: 5500000, // Ropsten has a lower block limit than mainnet - // confirmations: 2, // # of confs to wait between deployments. (default: 0) - // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) - // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) - // }, - - // Useful for private networks - // private: { - // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), - // network_id: 2111, // This network is yours, in the cloud. - // production: true // Treats this network as if it was a public net. (default: false) - // } - }, - - // Set default mocha options here, use special reporters etc. - mocha: { - // timeout: 100000 - }, - - // Configure your compilers + networks: {}, + mocha: {}, compilers: { - solc: { - // version: "0.5.1", // Fetch exact version from solc-bin (default: truffle's version) - // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) - // settings: { // See the solidity docs for advice about optimization and evmVersion - // optimizer: { - // enabled: false, - // runs: 200 - // }, - // evmVersion: "byzantium" - // } - } + solc: {} } } diff --git a/test/integration/projects/no-sources/truffle-config.js b/test/integration/projects/no-sources/truffle-config.js index 3865690..b398b07 100644 --- a/test/integration/projects/no-sources/truffle-config.js +++ b/test/integration/projects/no-sources/truffle-config.js @@ -1,99 +1,7 @@ -/** - * Use this file to configure your truffle project. It's seeded with some - * common settings for different networks and features like migrations, - * compilation and testing. Uncomment the ones you need or modify - * them to suit your project as necessary. - * - * More information about configuration can be found at: - * - * truffleframework.com/docs/advanced/configuration - * - * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider) - * to sign your transactions before they're sent to a remote public node. Infura accounts - * are available for free at: infura.io/register. - * - * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate - * public/private key pairs. If you're publishing your code to GitHub make sure you load this - * phrase from a file you've .gitignored so it doesn't accidentally become public. - * - */ - -// const HDWalletProvider = require('truffle-hdwallet-provider'); -// const infuraKey = "fj4jll3k....."; -// -// const fs = require('fs'); -// const mnemonic = fs.readFileSync(".secret").toString().trim(); - module.exports = { - /** - * Networks define how you connect to your ethereum client and let you set the - * defaults web3 uses to send transactions. If you don't specify one truffle - * will spin up a development blockchain for you on port 9545 when you - * run `develop` or `test`. You can ask a truffle command to use a specific - * network from the command line, e.g - * - * $ truffle test --network - */ - - networks: { - // Useful for testing. The `development` name is special - truffle uses it by default - // if it's defined here and no other network is specified at the command line. - // You should run a client (like ganache-cli, geth or parity) in a separate terminal - // tab if you use this network and you must also set the `host`, `port` and `network_id` - // options below to some value. - // - // development: { - // host: "127.0.0.1", // Localhost (default: none) - // port: 8545, // Standard Ethereum port (default: none) - // network_id: "*", // Any network (default: none) - // }, - - // Another network with more advanced options... - // advanced: { - // port: 8777, // Custom port - // network_id: 1342, // Custom network - // gas: 8500000, // Gas sent with each transaction (default: ~6700000) - // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) - // from:
, // Account to send txs from (default: accounts[0]) - // websockets: true // Enable EventEmitter interface for web3 (default: false) - // }, - - // Useful for deploying to a public network. - // NB: It's important to wrap the provider as a function. - // ropsten: { - // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`), - // network_id: 3, // Ropsten's id - // gas: 5500000, // Ropsten has a lower block limit than mainnet - // confirmations: 2, // # of confs to wait between deployments. (default: 0) - // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) - // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) - // }, - - // Useful for private networks - // private: { - // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), - // network_id: 2111, // This network is yours, in the cloud. - // production: true // Treats this network as if it was a public net. (default: false) - // } - }, - - // Set default mocha options here, use special reporters etc. - mocha: { - // timeout: 100000 - }, - - // Configure your compilers + networks: {}, + mocha: {}, compilers: { - solc: { - // version: "0.5.1", // Fetch exact version from solc-bin (default: truffle's version) - // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) - // settings: { // See the solidity docs for advice about optimization and evmVersion - // optimizer: { - // enabled: false, - // runs: 200 - // }, - // evmVersion: "byzantium" - // } - } + solc: {} } } diff --git a/test/integration/projects/skipping/truffle-config.js b/test/integration/projects/skipping/truffle-config.js index 3865690..b398b07 100644 --- a/test/integration/projects/skipping/truffle-config.js +++ b/test/integration/projects/skipping/truffle-config.js @@ -1,99 +1,7 @@ -/** - * Use this file to configure your truffle project. It's seeded with some - * common settings for different networks and features like migrations, - * compilation and testing. Uncomment the ones you need or modify - * them to suit your project as necessary. - * - * More information about configuration can be found at: - * - * truffleframework.com/docs/advanced/configuration - * - * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider) - * to sign your transactions before they're sent to a remote public node. Infura accounts - * are available for free at: infura.io/register. - * - * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate - * public/private key pairs. If you're publishing your code to GitHub make sure you load this - * phrase from a file you've .gitignored so it doesn't accidentally become public. - * - */ - -// const HDWalletProvider = require('truffle-hdwallet-provider'); -// const infuraKey = "fj4jll3k....."; -// -// const fs = require('fs'); -// const mnemonic = fs.readFileSync(".secret").toString().trim(); - module.exports = { - /** - * Networks define how you connect to your ethereum client and let you set the - * defaults web3 uses to send transactions. If you don't specify one truffle - * will spin up a development blockchain for you on port 9545 when you - * run `develop` or `test`. You can ask a truffle command to use a specific - * network from the command line, e.g - * - * $ truffle test --network - */ - - networks: { - // Useful for testing. The `development` name is special - truffle uses it by default - // if it's defined here and no other network is specified at the command line. - // You should run a client (like ganache-cli, geth or parity) in a separate terminal - // tab if you use this network and you must also set the `host`, `port` and `network_id` - // options below to some value. - // - // development: { - // host: "127.0.0.1", // Localhost (default: none) - // port: 8545, // Standard Ethereum port (default: none) - // network_id: "*", // Any network (default: none) - // }, - - // Another network with more advanced options... - // advanced: { - // port: 8777, // Custom port - // network_id: 1342, // Custom network - // gas: 8500000, // Gas sent with each transaction (default: ~6700000) - // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) - // from:
, // Account to send txs from (default: accounts[0]) - // websockets: true // Enable EventEmitter interface for web3 (default: false) - // }, - - // Useful for deploying to a public network. - // NB: It's important to wrap the provider as a function. - // ropsten: { - // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`), - // network_id: 3, // Ropsten's id - // gas: 5500000, // Ropsten has a lower block limit than mainnet - // confirmations: 2, // # of confs to wait between deployments. (default: 0) - // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) - // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) - // }, - - // Useful for private networks - // private: { - // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), - // network_id: 2111, // This network is yours, in the cloud. - // production: true // Treats this network as if it was a public net. (default: false) - // } - }, - - // Set default mocha options here, use special reporters etc. - mocha: { - // timeout: 100000 - }, - - // Configure your compilers + networks: {}, + mocha: {}, compilers: { - solc: { - // version: "0.5.1", // Fetch exact version from solc-bin (default: truffle's version) - // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) - // settings: { // See the solidity docs for advice about optimization and evmVersion - // optimizer: { - // enabled: false, - // runs: 200 - // }, - // evmVersion: "byzantium" - // } - } + solc: {} } } diff --git a/test/integration/projects/test-files/truffle-config.js b/test/integration/projects/test-files/truffle-config.js index 3865690..b398b07 100644 --- a/test/integration/projects/test-files/truffle-config.js +++ b/test/integration/projects/test-files/truffle-config.js @@ -1,99 +1,7 @@ -/** - * Use this file to configure your truffle project. It's seeded with some - * common settings for different networks and features like migrations, - * compilation and testing. Uncomment the ones you need or modify - * them to suit your project as necessary. - * - * More information about configuration can be found at: - * - * truffleframework.com/docs/advanced/configuration - * - * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider) - * to sign your transactions before they're sent to a remote public node. Infura accounts - * are available for free at: infura.io/register. - * - * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate - * public/private key pairs. If you're publishing your code to GitHub make sure you load this - * phrase from a file you've .gitignored so it doesn't accidentally become public. - * - */ - -// const HDWalletProvider = require('truffle-hdwallet-provider'); -// const infuraKey = "fj4jll3k....."; -// -// const fs = require('fs'); -// const mnemonic = fs.readFileSync(".secret").toString().trim(); - module.exports = { - /** - * Networks define how you connect to your ethereum client and let you set the - * defaults web3 uses to send transactions. If you don't specify one truffle - * will spin up a development blockchain for you on port 9545 when you - * run `develop` or `test`. You can ask a truffle command to use a specific - * network from the command line, e.g - * - * $ truffle test --network - */ - - networks: { - // Useful for testing. The `development` name is special - truffle uses it by default - // if it's defined here and no other network is specified at the command line. - // You should run a client (like ganache-cli, geth or parity) in a separate terminal - // tab if you use this network and you must also set the `host`, `port` and `network_id` - // options below to some value. - // - // development: { - // host: "127.0.0.1", // Localhost (default: none) - // port: 8545, // Standard Ethereum port (default: none) - // network_id: "*", // Any network (default: none) - // }, - - // Another network with more advanced options... - // advanced: { - // port: 8777, // Custom port - // network_id: 1342, // Custom network - // gas: 8500000, // Gas sent with each transaction (default: ~6700000) - // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) - // from:
, // Account to send txs from (default: accounts[0]) - // websockets: true // Enable EventEmitter interface for web3 (default: false) - // }, - - // Useful for deploying to a public network. - // NB: It's important to wrap the provider as a function. - // ropsten: { - // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`), - // network_id: 3, // Ropsten's id - // gas: 5500000, // Ropsten has a lower block limit than mainnet - // confirmations: 2, // # of confs to wait between deployments. (default: 0) - // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) - // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) - // }, - - // Useful for private networks - // private: { - // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), - // network_id: 2111, // This network is yours, in the cloud. - // production: true // Treats this network as if it was a public net. (default: false) - // } - }, - - // Set default mocha options here, use special reporters etc. - mocha: { - // timeout: 100000 - }, - - // Configure your compilers + networks: {}, + mocha: {}, compilers: { - solc: { - // version: "0.5.1", // Fetch exact version from solc-bin (default: truffle's version) - // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) - // settings: { // See the solidity docs for advice about optimization and evmVersion - // optimizer: { - // enabled: false, - // runs: 200 - // }, - // evmVersion: "byzantium" - // } - } + solc: {} } } diff --git a/test/units/app.js b/test/units/app.js deleted file mode 100644 index ff466eb..0000000 --- a/test/units/app.js +++ /dev/null @@ -1,527 +0,0 @@ -const assert = require('assert'); -const fs = require('fs'); -const shell = require('shelljs'); -const mock = require('../util/integration.truffle'); -const plugin = require('../../dist/truffle.plugin'); -const path = require('path') -const util = require('util') -const opts = { compact: false, depth: 5, breakLength: 80 }; - -// ======= -// Helpers -// ======= -function pathExists(path) { return shell.test('-e', path); } - -function pathToContract(config, file) { - return path.join('contracts', file); -} - -function assertLineCoverage(expected=[]){ - let summary = JSON.parse(fs.readFileSync('coverage/coverage-summary.json')); - expected.forEach(item => assert(summary[item.file].lines.pct === item.pct)) -} - -function assertCoverageMissing(expected=[]){ - let summary = JSON.parse(fs.readFileSync('coverage/coverage-summary.json')); - expected.forEach(item => assert(summary[item.file] === undefined)) -} - -function assertCleanInitialState(){ - assert(pathExists('./coverage') === false, 'should start without: coverage'); - assert(pathExists('./coverage.json') === false, 'should start without: coverage.json'); -} - -function assertCoverageGenerate(truffleConfig){ - const jsonPath = path.join(truffleConfig.working_directory, "coverage.json"); - assert(pathExists('./coverage') === true, 'should gen coverage folder'); - assert(pathExists(jsonPath) === true, 'should gen coverage.json'); -} - -function assertCoverageNotGenerated(truffleConfig){ - const jsonPath = path.join(truffleConfig.working_directory, "coverage.json"); - assert(pathExists('./coverage') !== true, 'should NOT gen coverage folder'); - assert(pathExists(jsonPath) !== true, 'should NOT gen coverage.json'); -} - -function getOutput(truffleConfig){ - const jsonPath = path.join(truffleConfig.working_directory, "coverage.json"); - return JSON.parse(fs.readFileSync(jsonPath, 'utf8')); -} - -// ======== -// Tests -// ======== -describe('app', function() { - let truffleConfig; - let solcoverConfig; - let collector; - - beforeEach(() => { - mock.clean(); - - mock.loggerOutput.val = ''; - solcoverConfig = {}; - truffleConfig = mock.getDefaultTruffleConfig(); - }) - - afterEach(() => mock.clean()); - - it('simple contract: should generate coverage, cleanup & exit(0)', async function(){ - assertCleanInitialState(); - - mock.install('Simple', 'simple.js', solcoverConfig); - await plugin(truffleConfig); - - assertCoverageGenerate(truffleConfig); - - const output = getOutput(truffleConfig); - const path = Object.keys(output)[0]; - - assert(output[path].fnMap['1'].name === 'test', 'coverage.json missing "test"'); - assert(output[path].fnMap['2'].name === 'getX', 'coverage.json missing "getX"'); - }); - - // 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); - await plugin(truffleConfig); - }); - - it('large contract with many unbracketed statements (time check)', async function() { - assertCleanInitialState(); - - truffleConfig.compilers.solc.version = "0.4.24"; - - mock.install('Oraclize', 'oraclize.js', solcoverConfig, truffleConfig, true); - await plugin(truffleConfig); - }); - - // 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. - it('project evm_reverts repeatedly', async function() { - assertCleanInitialState(); - mock.installFullProject('multiple-migrations'); - await plugin(truffleConfig); - - const expected = [ - { - file: pathToContract(truffleConfig, 'ContractA.sol'), - pct: 100 - }, - { - file: pathToContract(truffleConfig, 'ContractB.sol'), - pct: 100, - }, - { - file: pathToContract(truffleConfig, 'ContractC.sol'), - pct: 100, - }, - ]; - - assertLineCoverage(expected); - }); - - it('project skips a folder', async function() { - assertCleanInitialState(); - mock.installFullProject('skipping'); - await plugin(truffleConfig); - - const expected = [{ - file: pathToContract(truffleConfig, 'ContractA.sol'), - pct: 100 - }]; - - const missing = [{ - file: pathToContract(truffleConfig, 'ContractB.sol'), - }]; - - assertLineCoverage(expected); - assertCoverageMissing(missing); - }); - - it('project contains no contract sources folder', async function() { - assertCleanInitialState(); - mock.installFullProject('no-sources'); - - try { - await plugin(truffleConfig); - assert.fail() - } catch(err){ - assert( - err.message.includes('Cannot locate expected contract sources folder'), - `Should error when contract sources cannot be found: (output --> ${err.message}` - ); - - assert( - err.message.includes('sc_temp/contracts'), - `Error message should contain path: (output --> ${err.message}` - ); - } - - assertCoverageNotGenerated(truffleConfig); - }); - - it('project with relative path solidity imports', async function() { - assertCleanInitialState(); - mock.installFullProject('import-paths'); - 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('project .solcover.js has syntax error', async function(){ - assertCleanInitialState(); - - mock.installFullProject('bad-solcoverjs'); - try { - await plugin(truffleConfig); - assert.fail() - } catch(err){ - assert( - err.message.includes('Could not load .solcover.js config file.'), - `Should notify when solcoverjs has syntax error: (output --> ${err.message}` - ); - } - - assertCoverageNotGenerated(truffleConfig); - }) - - it('truffle run coverage --config ../.solcover.js', async function() { - assertCleanInitialState(); - - solcoverConfig = { - silent: process.env.SILENT ? true : false, - istanbulReporter: ['json-summary', 'text'] - }; - fs.writeFileSync('.solcover.js', `module.exports=${JSON.stringify(solcoverConfig)}`); - - // This relative path has to be ./ prefixed - // (because it's path.joined to truffle's working_directory) - truffleConfig.solcoverjs = './../.solcover.js'; - - mock.install('Simple', 'simple.js'); - await plugin(truffleConfig); - - // The relative solcoverjs uses the json-summary reporter which - // this assertion requires - const expected = [{ - file: pathToContract(truffleConfig, 'Simple.sol'), - pct: 100 - }]; - - assertLineCoverage(expected); - shell.rm('.solcover.js'); - }); - - 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 --useGlobalTruffle', async function(){ - assertCleanInitialState(); - truffleConfig.useGlobalTruffle = true; - - truffleConfig.logger = mock.testLogger; - mock.install('Simple', 'simple.js', solcoverConfig); - await plugin(truffleConfig); - - assert( - mock.loggerOutput.val.includes('global node_modules'), - `Should notify it's using global truffle (output --> ${mock.loggerOutput.val}` - ); - }); - - it('truffle run coverage --usePluginTruffle', async function(){ - assertCleanInitialState(); - truffleConfig.usePluginTruffle = true; - - truffleConfig.logger = mock.testLogger; - mock.install('Simple', 'simple.js', solcoverConfig); - await plugin(truffleConfig); - - assert( - mock.loggerOutput.val.includes('fallback Truffle library module'), - `Should notify it's using plugin truffle lib copy (output --> ${mock.loggerOutput.val}` - ); - }); - - it('lib module load failure', async function(){ - assertCleanInitialState(); - truffleConfig.usePluginTruffle = true; - truffleConfig.forceLibFailure = true; - - mock.install('Simple', 'simple.js', solcoverConfig); - - try { - await plugin(truffleConfig); - assert.fail() - } catch (err) { - assert( - err.message.includes('Unable to load plugin copy of Truffle library module'), - `Should error on failed lib module load (output --> ${err.message}` - ); - } - }); - - it('truffle run coverage --file test/', async function() { - assertCleanInitialState(); - - const testPath = path.join(truffleConfig.working_directory, 'test/specific_a.js'); - truffleConfig.file = testPath; - mock.installFullProject('test-files'); - await plugin(truffleConfig); - - const expected = [ - { - file: pathToContract(truffleConfig, 'ContractA.sol'), - pct: 100 - }, - { - file: pathToContract(truffleConfig, 'ContractB.sol'), - pct: 0, - }, - { - file: pathToContract(truffleConfig, 'ContractC.sol'), - pct: 0, - }, - ]; - - assertLineCoverage(expected); - }); - - it('truffle run coverage --file test/', async function() { - assertCleanInitialState(); - - const testPath = path.join(truffleConfig.working_directory, 'test/globby*'); - truffleConfig.file = testPath; - mock.installFullProject('test-files'); - await plugin(truffleConfig); - - const expected = [ - { - file: pathToContract(truffleConfig, 'ContractA.sol'), - pct: 0, - }, - { - file: pathToContract(truffleConfig, 'ContractB.sol'), - pct: 100, - }, - { - file: pathToContract(truffleConfig, 'ContractC.sol'), - pct: 100, - }, - ]; - - assertLineCoverage(expected); - }); - - it('truffle run coverage --file test/gl{o,b}*.js', async function() { - assertCleanInitialState(); - - const testPath = path.join(truffleConfig.working_directory, 'test/gl{o,b}*.js'); - truffleConfig.file = testPath; - mock.installFullProject('test-files'); - await plugin(truffleConfig); - - const expected = [ - { - file: pathToContract(truffleConfig, 'ContractA.sol'), - pct: 0, - }, - { - file: pathToContract(truffleConfig, 'ContractB.sol'), - pct: 100, - }, - { - file: pathToContract(truffleConfig, 'ContractC.sol'), - pct: 100, - }, - ]; - - assertLineCoverage(expected); - }); - - it('contract only uses ".call"', async function(){ - assertCleanInitialState(); - - mock.install('OnlyCall', 'only-call.js', solcoverConfig); - await plugin(truffleConfig); - - assertCoverageGenerate(truffleConfig); - - const output = getOutput(truffleConfig); - const path = Object.keys(output)[0]; - assert(output[path].fnMap['1'].name === 'addTwo', 'cov should map "addTwo"'); - }); - - it('contract sends / transfers to instrumented fallback', async function(){ - assertCleanInitialState(); - - mock.install('Wallet', 'wallet.js', solcoverConfig); - await plugin(truffleConfig); - - assertCoverageGenerate(truffleConfig); - - const output = getOutput(truffleConfig); - const path = Object.keys(output)[0]; - assert(output[path].fnMap['1'].name === 'transferPayment', 'cov should map "transferPayment"'); - }); - - it('contracts are skipped', async function() { - assertCleanInitialState(); - - solcoverConfig.skipFiles = ['Owned.sol']; - - mock.installDouble(['Proxy', 'Owned'], 'inheritance.js', solcoverConfig); - await plugin(truffleConfig); - - assertCoverageGenerate(truffleConfig); - - const output = 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('contract uses inheritance', async function() { - assertCleanInitialState(); - - mock.installDouble(['Proxy', 'Owned'], 'inheritance.js', solcoverConfig); - await plugin(truffleConfig); - - assertCoverageGenerate(truffleConfig); - - const output = getOutput(truffleConfig); - 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'); - }); - - // Simple.sol with a failing assertion in a truffle test - it('truffle tests failing', async function() { - assertCleanInitialState(); - - mock.install('Simple', 'truffle-test-fail.js', solcoverConfig); - - try { - await plugin(truffleConfig); - assert.fail() - } catch(err){ - assert(err.message.includes('failed under coverage')); - } - - assertCoverageGenerate(truffleConfig); - - const output = getOutput(truffleConfig); - 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"'); - }); - - // 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); - await plugin(truffleConfig); - }); - - // Truffle test contains syntax error - it('truffle crashes', async function() { - assertCleanInitialState(); - - mock.install('Simple', 'truffle-crash.js', solcoverConfig); - try { - await plugin(truffleConfig); - assert.fail() - } catch(err){ - assert(err.toString().includes('SyntaxError')); - } - }); - - // Solidity syntax errors - it('compilation failure', async function(){ - assertCleanInitialState(); - - mock.install('SimpleError', 'simple.js', solcoverConfig); - - try { - await plugin(truffleConfig); - assert.fail() - } catch(err){ - assert(err.toString().includes('Compilation failed')); - } - - assertCoverageNotGenerated(truffleConfig); - }); - - it('instrumentation failure', async function(){ - assertCleanInitialState(); - - mock.install('Unparseable', 'simple.js', solcoverConfig); - - try { - await plugin(truffleConfig); - assert.fail() - } catch(err){ - assert( - err.toString().includes('/Unparseable.sol.'), - `Should throw instrumentation errors with file name (output --> ${err.toString()}` - ); - - assert(err.stack !== undefined, 'Should have error trace') - } - - assertCoverageNotGenerated(truffleConfig); - }) - -}); diff --git a/test/units/truffle/errors.js b/test/units/truffle/errors.js new file mode 100644 index 0000000..5c00d9b --- /dev/null +++ b/test/units/truffle/errors.js @@ -0,0 +1,151 @@ +const assert = require('assert'); +const fs = require('fs'); +const path = require('path') +const shell = require('shelljs'); + +const verify = require('../../util/verifiers') +const mock = require('../../util/integration.truffle'); +const plugin = require('../../../dist/truffle.plugin'); + +describe('Truffle Plugin: error cases', function() { + let truffleConfig; + let solcoverConfig; + + beforeEach(() => { + mock.clean(); + + mock.loggerOutput.val = ''; + solcoverConfig = {}; + truffleConfig = mock.getDefaultTruffleConfig(); + }) + + afterEach(() => mock.clean()); + + it('project contains no contract sources folder', async function() { + verify.cleanInitialState(); + mock.installFullProject('no-sources'); + + try { + await plugin(truffleConfig); + assert.fail() + } catch(err){ + assert( + err.message.includes('Cannot locate expected contract sources folder'), + `Should error when contract sources cannot be found: (output --> ${err.message}` + ); + + assert( + err.message.includes('sc_temp/contracts'), + `Error message should contain path: (output --> ${err.message}` + ); + } + + verify.coverageNotGenerated(truffleConfig); + }); + + it('project .solcover.js has syntax error', async function(){ + verify.cleanInitialState(); + + mock.installFullProject('bad-solcoverjs'); + try { + await plugin(truffleConfig); + assert.fail() + } catch(err){ + assert( + err.message.includes('Could not load .solcover.js config file.'), + `Should notify when solcoverjs has syntax error: (output --> ${err.message}` + ); + } + + verify.coverageNotGenerated(truffleConfig); + }) + + it('lib module load failure', async function(){ + verify.cleanInitialState(); + truffleConfig.usePluginTruffle = true; + truffleConfig.forceLibFailure = true; + + mock.install('Simple', 'simple.js', solcoverConfig); + + try { + await plugin(truffleConfig); + assert.fail() + } catch (err) { + assert( + err.message.includes('Unable to load plugin copy of Truffle library module'), + `Should error on failed lib module load (output --> ${err.message}` + ); + } + }); + + // Simple.sol with a failing assertion in a truffle test + it('truffle tests failing', async function() { + verify.cleanInitialState(); + + mock.install('Simple', 'truffle-test-fail.js', solcoverConfig); + + try { + await plugin(truffleConfig); + assert.fail() + } catch(err){ + assert(err.message.includes('failed under coverage')); + } + + verify.coverageGenerated(truffleConfig); + + const output = mock.getOutput(truffleConfig); + 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"'); + }); + + // Truffle test contains syntax error + it('truffle crashes', async function() { + verify.cleanInitialState(); + + mock.install('Simple', 'truffle-crash.js', solcoverConfig); + try { + await plugin(truffleConfig); + assert.fail() + } catch(err){ + assert(err.toString().includes('SyntaxError')); + } + }); + + // Solidity syntax errors + it('compilation failure', async function(){ + verify.cleanInitialState(); + + mock.install('SimpleError', 'simple.js', solcoverConfig); + + try { + await plugin(truffleConfig); + assert.fail() + } catch(err){ + assert(err.toString().includes('Compilation failed')); + } + + verify.coverageNotGenerated(truffleConfig); + }); + + it('instrumentation failure', async function(){ + verify.cleanInitialState(); + + mock.install('Unparseable', 'simple.js', solcoverConfig); + + try { + await plugin(truffleConfig); + assert.fail() + } catch(err){ + assert( + err.toString().includes('/Unparseable.sol.'), + `Should throw instrumentation errors with file name (output --> ${err.toString()}` + ); + + assert(err.stack !== undefined, 'Should have error trace') + } + + verify.coverageNotGenerated(truffleConfig); + }) +}) diff --git a/test/units/truffle/flags.js b/test/units/truffle/flags.js new file mode 100644 index 0000000..7f4830f --- /dev/null +++ b/test/units/truffle/flags.js @@ -0,0 +1,200 @@ +const assert = require('assert'); +const fs = require('fs'); +const path = require('path') +const shell = require('shelljs'); + +const verify = require('../../util/verifiers') +const mock = require('../../util/integration.truffle'); +const plugin = require('../../../dist/truffle.plugin'); + +// ======================= +// Standard Use-case Tests +// ======================= + +describe('Truffle Plugin: command line options', function() { + let truffleConfig; + let solcoverConfig; + + beforeEach(() => { + mock.clean(); + + mock.loggerOutput.val = ''; + solcoverConfig = {}; + truffleConfig = mock.getDefaultTruffleConfig(); + }) + + afterEach(() => mock.clean()); + + it('truffle run coverage --file test/', async function() { + verify.cleanInitialState(); + + const testPath = path.join(truffleConfig.working_directory, 'test/specific_a.js'); + truffleConfig.file = testPath; + mock.installFullProject('test-files'); + await plugin(truffleConfig); + + const expected = [ + { + file: mock.pathToContract(truffleConfig, 'ContractA.sol'), + pct: 100 + }, + { + file: mock.pathToContract(truffleConfig, 'ContractB.sol'), + pct: 0, + }, + { + file: mock.pathToContract(truffleConfig, 'ContractC.sol'), + pct: 0, + }, + ]; + + verify.lineCoverage(expected); + }); + + it('truffle run coverage --file test/', async function() { + verify.cleanInitialState(); + + const testPath = path.join(truffleConfig.working_directory, 'test/globby*'); + truffleConfig.file = testPath; + mock.installFullProject('test-files'); + await plugin(truffleConfig); + + const expected = [ + { + file: mock.pathToContract(truffleConfig, 'ContractA.sol'), + pct: 0, + }, + { + file: mock.pathToContract(truffleConfig, 'ContractB.sol'), + pct: 100, + }, + { + file: mock.pathToContract(truffleConfig, 'ContractC.sol'), + pct: 100, + }, + ]; + + verify.lineCoverage(expected); + }); + + it('truffle run coverage --file test/gl{o,b}*.js', async function() { + verify.cleanInitialState(); + + const testPath = path.join(truffleConfig.working_directory, 'test/gl{o,b}*.js'); + truffleConfig.file = testPath; + mock.installFullProject('test-files'); + await plugin(truffleConfig); + + const expected = [ + { + file: mock.pathToContract(truffleConfig, 'ContractA.sol'), + pct: 0, + }, + { + file: mock.pathToContract(truffleConfig, 'ContractB.sol'), + pct: 100, + }, + { + file: mock.pathToContract(truffleConfig, 'ContractC.sol'), + pct: 100, + }, + ]; + + verify.lineCoverage(expected); + }); + + it('truffle run coverage --config ../.solcover.js', async function() { + verify.cleanInitialState(); + + solcoverConfig = { + silent: process.env.SILENT ? true : false, + istanbulReporter: ['json-summary', 'text'] + }; + fs.writeFileSync('.solcover.js', `module.exports=${JSON.stringify(solcoverConfig)}`); + + // This relative path has to be ./ prefixed + // (because it's path.joined to truffle's working_directory) + truffleConfig.solcoverjs = './../.solcover.js'; + + mock.install('Simple', 'simple.js'); + await plugin(truffleConfig); + + // The relative solcoverjs uses the json-summary reporter which + // this assertion requires + const expected = [{ + file: mock.pathToContract(truffleConfig, 'Simple.sol'), + pct: 100 + }]; + + verify.lineCoverage(expected); + shell.rm('.solcover.js'); + }); + + it('truffle run coverage --help', async function(){ + verify.cleanInitialState(); + 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(){ + verify.cleanInitialState(); + 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 --useGlobalTruffle', async function(){ + verify.cleanInitialState(); + truffleConfig.useGlobalTruffle = true; + + truffleConfig.logger = mock.testLogger; + mock.install('Simple', 'simple.js', solcoverConfig); + await plugin(truffleConfig); + + assert( + mock.loggerOutput.val.includes('global node_modules'), + `Should notify it's using global truffle (output --> ${mock.loggerOutput.val}` + ); + }); + + it('truffle run coverage --usePluginTruffle', async function(){ + verify.cleanInitialState(); + truffleConfig.usePluginTruffle = true; + + truffleConfig.logger = mock.testLogger; + mock.install('Simple', 'simple.js', solcoverConfig); + await plugin(truffleConfig); + + assert( + mock.loggerOutput.val.includes('fallback Truffle library module'), + `Should notify it's using plugin truffle lib copy (output --> ${mock.loggerOutput.val}` + ); + }); +}); + diff --git a/test/units/truffle/standard.js b/test/units/truffle/standard.js new file mode 100644 index 0000000..8f717c7 --- /dev/null +++ b/test/units/truffle/standard.js @@ -0,0 +1,187 @@ +const assert = require('assert'); +const fs = require('fs'); +const path = require('path') +const shell = require('shelljs'); + +const verify = require('../../util/verifiers') +const mock = require('../../util/integration.truffle'); +const plugin = require('../../../dist/truffle.plugin'); + +// ======================= +// Standard Use-case Tests +// ======================= + +describe('Truffle Plugin: standard use cases', function() { + let truffleConfig; + let solcoverConfig; + + beforeEach(() => { + mock.clean(); + + mock.loggerOutput.val = ''; + solcoverConfig = {}; + truffleConfig = mock.getDefaultTruffleConfig(); + }) + + afterEach(() => mock.clean()); + + it('simple contract: should generate coverage, cleanup & exit(0)', async function(){ + verify.cleanInitialState(); + + mock.install('Simple', 'simple.js', solcoverConfig); + await plugin(truffleConfig); + + verify.coverageGenerated(truffleConfig); + + const output = mock.getOutput(truffleConfig); + const path = Object.keys(output)[0]; + + assert(output[path].fnMap['1'].name === 'test', 'coverage.json missing "test"'); + assert(output[path].fnMap['2'].name === 'getX', 'coverage.json missing "getX"'); + }); + + // 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); + await plugin(truffleConfig); + }); + + it('large contract with many unbracketed statements (time check)', async function() { + verify.cleanInitialState(); + + truffleConfig.compilers.solc.version = "0.4.24"; + + mock.install('Oraclize', 'oraclize.js', solcoverConfig, truffleConfig, true); + await plugin(truffleConfig); + }); + + // 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. + it('project evm_reverts repeatedly', async function() { + verify.cleanInitialState(); + mock.installFullProject('multiple-migrations'); + await plugin(truffleConfig); + + const expected = [ + { + file: mock.pathToContract(truffleConfig, 'ContractA.sol'), + pct: 100 + }, + { + file: mock.pathToContract(truffleConfig, 'ContractB.sol'), + pct: 100, + }, + { + file: mock.pathToContract(truffleConfig, 'ContractC.sol'), + pct: 100, + }, + ]; + + verify.lineCoverage(expected); + }); + + it('project 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, 'ContractB.sol'), + }]; + + verify.lineCoverage(expected); + verify.coverageMissing(missing); + }); + + it('project with relative path solidity imports', async function() { + verify.cleanInitialState(); + mock.installFullProject('import-paths'); + await plugin(truffleConfig); + }); + + it('project contains native solidity tests', async function(){ + verify.cleanInitialState(); + + 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('contract only uses ".call"', async function(){ + verify.cleanInitialState(); + + mock.install('OnlyCall', 'only-call.js', solcoverConfig); + await plugin(truffleConfig); + + verify.coverageGenerated(truffleConfig); + + const output = mock.getOutput(truffleConfig); + const path = Object.keys(output)[0]; + assert(output[path].fnMap['1'].name === 'addTwo', 'cov should map "addTwo"'); + }); + + it('contract sends / transfers to instrumented fallback', async function(){ + verify.cleanInitialState(); + + mock.install('Wallet', 'wallet.js', solcoverConfig); + await plugin(truffleConfig); + + verify.coverageGenerated(truffleConfig); + + const output = mock.getOutput(truffleConfig); + const path = Object.keys(output)[0]; + assert(output[path].fnMap['1'].name === 'transferPayment', 'cov should map "transferPayment"'); + }); + + it('contracts are skipped', async function() { + verify.cleanInitialState(); + + 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('contract uses inheritance', async function() { + verify.cleanInitialState(); + + mock.installDouble(['Proxy', 'Owned'], 'inheritance.js', solcoverConfig); + await plugin(truffleConfig); + + verify.coverageGenerated(truffleConfig); + + const output = mock.getOutput(truffleConfig); + 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'); + }); + + // 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); + await plugin(truffleConfig); + }); +}) diff --git a/test/util/integration.truffle.js b/test/util/integration.truffle.js index 14039b7..6f5bd84 100644 --- a/test/util/integration.truffle.js +++ b/test/util/integration.truffle.js @@ -38,6 +38,15 @@ function clean() { shell.config.silent = false; }; +function pathToContract(config, file) { + return path.join('contracts', file); +} + +function getOutput(truffleConfig){ + const jsonPath = path.join(truffleConfig.working_directory, "coverage.json"); + return JSON.parse(fs.readFileSync(jsonPath, 'utf8')); +} + // ========================== // Configuration // ========================== @@ -208,6 +217,8 @@ module.exports = { install: install, installDouble: installDouble, installFullProject: installFullProject, - clean: clean + clean: clean, + pathToContract: pathToContract, + getOutput: getOutput } diff --git a/test/util/util.js b/test/util/util.js index 225dd98..987f337 100644 --- a/test/util/util.js +++ b/test/util/util.js @@ -11,7 +11,7 @@ const Instrumenter = require('./../../lib/instrumenter'); const DataCollector = require('./../../lib/collector') // ==================== -// Path constants +// Paths / Files // ==================== const filePath = path.resolve('./test.sol'); const pathPrefix = './'; @@ -124,4 +124,5 @@ module.exports = { instrumentAndCompile: instrumentAndCompile, bootstrapCoverage: bootstrapCoverage, initializeProvider: initializeProvider, + } diff --git a/test/util/verifiers.js b/test/util/verifiers.js new file mode 100644 index 0000000..e201745 --- /dev/null +++ b/test/util/verifiers.js @@ -0,0 +1,41 @@ +const fs = require('fs'); +const path = require('path'); +const shell = require('shelljs'); +const assert = require('assert'); + +function pathExists(path) { return shell.test('-e', path); } + +function lineCoverage(expected=[]){ + let summary = JSON.parse(fs.readFileSync('coverage/coverage-summary.json')); + expected.forEach(item => assert(summary[item.file].lines.pct === item.pct)) +} + +function coverageMissing(expected=[]){ + let summary = JSON.parse(fs.readFileSync('coverage/coverage-summary.json')); + expected.forEach(item => assert(summary[item.file] === undefined)) +} + +function cleanInitialState(){ + assert(pathExists('./coverage') === false, 'should start without: coverage'); + assert(pathExists('./coverage.json') === false, 'should start without: coverage.json'); +} + +function coverageGenerated(truffleConfig){ + const jsonPath = path.join(truffleConfig.working_directory, "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"); + assert(pathExists('./coverage') !== true, 'should NOT gen coverage folder'); + assert(pathExists(jsonPath) !== true, 'should NOT gen coverage.json'); +} + +module.exports = { + lineCoverage: lineCoverage, + coverageMissing: coverageMissing, + cleanInitialState: cleanInitialState, + coverageGenerated: coverageGenerated, + coverageNotGenerated: coverageNotGenerated, +}