Reorganize and cleanup truffle integration tests (#400)

pull/401/head
cgewecke 5 years ago committed by GitHub
parent c25697d5c6
commit 3bd99ab6dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      .circleci/config.yml
  2. 6
      package.json
  3. 0
      test/integration/projects/.solcover.js
  4. 0
      test/integration/projects/bad-solcover.js
  5. 98
      test/integration/projects/bad-solcoverjs/truffle-config.js
  6. 98
      test/integration/projects/import-paths/truffle-config.js
  7. 98
      test/integration/projects/multiple-migrations/truffle-config.js
  8. 98
      test/integration/projects/no-sources/truffle-config.js
  9. 98
      test/integration/projects/skipping/truffle-config.js
  10. 98
      test/integration/projects/test-files/truffle-config.js
  11. 527
      test/units/app.js
  12. 151
      test/units/truffle/errors.js
  13. 200
      test/units/truffle/flags.js
  14. 187
      test/units/truffle/standard.js
  15. 13
      test/util/integration.truffle.js
  16. 3
      test/util/util.js
  17. 41
      test/util/verifiers.js

@ -78,7 +78,8 @@ workflows:
build: build:
jobs: jobs:
- unit-test - unit-test
- e2e-zeppelin # TODO: re-enable when server logic is added
#- e2e-zeppelin
- e2e-metacoin - e2e-metacoin
# TODO: re-enable. # TODO: re-enable.
# At the moment we're using forks so this is pointless # At the moment we're using forks so this is pointless

@ -9,9 +9,9 @@
}, },
"scripts": { "scripts": {
"nyc": "SILENT=true nyc --exclude '**/sc_temp/**' --exclude '**/test/**'", "nyc": "SILENT=true nyc --exclude '**/sc_temp/**' --exclude '**/test/**'",
"test": "npm run nyc -- 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 70000 --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 70000 --no-warnings --exit" "test:debug": "mocha test/units/* --timeout 100000 --no-warnings --exit"
}, },
"homepage": "https://github.com/sc-forks/solidity-coverage", "homepage": "https://github.com/sc-forks/solidity-coverage",
"repository": { "repository": {

@ -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 = { module.exports = {
/** networks: {},
* Networks define how you connect to your ethereum client and let you set the mocha: {},
* 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 <network-name>
*/
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: <address>, // 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
compilers: { compilers: {
solc: { 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"
// }
}
} }
} }

@ -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 = { module.exports = {
/** networks: {},
* Networks define how you connect to your ethereum client and let you set the mocha: {},
* 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 <network-name>
*/
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: <address>, // 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
compilers: { compilers: {
solc: { 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"
// }
}
} }
} }

@ -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 = { module.exports = {
/** networks: {},
* Networks define how you connect to your ethereum client and let you set the mocha: {},
* 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 <network-name>
*/
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: <address>, // 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
compilers: { compilers: {
solc: { 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"
// }
}
} }
} }

@ -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 = { module.exports = {
/** networks: {},
* Networks define how you connect to your ethereum client and let you set the mocha: {},
* 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 <network-name>
*/
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: <address>, // 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
compilers: { compilers: {
solc: { 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"
// }
}
} }
} }

@ -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 = { module.exports = {
/** networks: {},
* Networks define how you connect to your ethereum client and let you set the mocha: {},
* 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 <network-name>
*/
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: <address>, // 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
compilers: { compilers: {
solc: { 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"
// }
}
} }
} }

@ -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 = { module.exports = {
/** networks: {},
* Networks define how you connect to your ethereum client and let you set the mocha: {},
* 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 <network-name>
*/
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: <address>, // 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
compilers: { compilers: {
solc: { 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"
// }
}
} }
} }

@ -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/<fileName>', 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/<glob*>', 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);
})
});

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

@ -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/<fileName>', 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/<glob*>', 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}`
);
});
});

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

@ -38,6 +38,15 @@ function clean() {
shell.config.silent = false; 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 // Configuration
// ========================== // ==========================
@ -208,6 +217,8 @@ module.exports = {
install: install, install: install,
installDouble: installDouble, installDouble: installDouble,
installFullProject: installFullProject, installFullProject: installFullProject,
clean: clean clean: clean,
pathToContract: pathToContract,
getOutput: getOutput
} }

@ -11,7 +11,7 @@ const Instrumenter = require('./../../lib/instrumenter');
const DataCollector = require('./../../lib/collector') const DataCollector = require('./../../lib/collector')
// ==================== // ====================
// Path constants // Paths / Files
// ==================== // ====================
const filePath = path.resolve('./test.sol'); const filePath = path.resolve('./test.sol');
const pathPrefix = './'; const pathPrefix = './';
@ -124,4 +124,5 @@ module.exports = {
instrumentAndCompile: instrumentAndCompile, instrumentAndCompile: instrumentAndCompile,
bootstrapCoverage: bootstrapCoverage, bootstrapCoverage: bootstrapCoverage,
initializeProvider: initializeProvider, initializeProvider: initializeProvider,
} }

@ -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,
}
Loading…
Cancel
Save