From 9ea10859b1c32f0c3b4c27e2a1cb26f8d7abcb9b Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sun, 8 Sep 2019 18:09:29 -0700 Subject: [PATCH] Add tests and fixtures for full project scenarios (#385) * Full project tests fixtures for repeated use of evm_revert, * ... and the skipped folders option, * ... and solidity imports with relative paths reaching outside the `contracts/` folder. --- dist/truffle.plugin.js | 20 ++-- .../projects/import-paths/.gitignore | 1 + .../projects/import-paths/.solcover.js | 3 + .../assets/RelativePathImport.sol | 10 ++ .../import-paths/contracts/Migrations.sol | 23 +++++ .../import-paths/contracts/UsesImports.sol | 17 ++++ .../migrations/1_initial_migration.js | 5 + .../package/NodeModulesImport.sol | 10 ++ .../import-paths/test/uses_imports.js | 16 +++ .../projects/import-paths/truffle-config.js | 99 +++++++++++++++++++ .../projects/multiple-migrations/.solcover.js | 3 + .../contracts/ContractA.sol | 17 ++++ .../contracts/ContractB.sol | 17 ++++ .../contracts/ContractC.sol | 17 ++++ .../contracts/Migrations.sol | 23 +++++ .../migrations/1_initial_migration.js | 5 + .../migrations/2_contracta.js | 5 + .../migrations/3_contractb.js | 5 + .../migrations/4_contractc.js | 5 + .../multiple-migrations/test/contracta.js | 15 +++ .../multiple-migrations/test/contractb.js | 15 +++ .../multiple-migrations/test/contractc.js | 20 ++++ .../multiple-migrations/truffle-config.js | 99 +++++++++++++++++++ .../projects/skipping/.solcover.js | 4 + .../projects/skipping/contracts/ContractA.sol | 17 ++++ .../skipping/contracts/Migrations.sol | 23 +++++ .../contracts/skipped-folder/ContractB.sol | 17 ++++ .../migrations/1_initial_migration.js | 5 + .../projects/skipping/test/contract.js | 29 ++++++ .../projects/skipping/truffle-config.js | 99 +++++++++++++++++++ test/units/app.js | 27 ++--- test/util/integration.truffle.js | 20 +++- 32 files changed, 668 insertions(+), 23 deletions(-) create mode 100644 test/integration/projects/import-paths/.gitignore create mode 100644 test/integration/projects/import-paths/.solcover.js create mode 100644 test/integration/projects/import-paths/assets/RelativePathImport.sol create mode 100644 test/integration/projects/import-paths/contracts/Migrations.sol create mode 100644 test/integration/projects/import-paths/contracts/UsesImports.sol create mode 100644 test/integration/projects/import-paths/migrations/1_initial_migration.js create mode 100644 test/integration/projects/import-paths/node_modules/package/NodeModulesImport.sol create mode 100644 test/integration/projects/import-paths/test/uses_imports.js create mode 100644 test/integration/projects/import-paths/truffle-config.js create mode 100644 test/integration/projects/multiple-migrations/.solcover.js create mode 100644 test/integration/projects/multiple-migrations/contracts/ContractA.sol create mode 100644 test/integration/projects/multiple-migrations/contracts/ContractB.sol create mode 100644 test/integration/projects/multiple-migrations/contracts/ContractC.sol create mode 100644 test/integration/projects/multiple-migrations/contracts/Migrations.sol create mode 100644 test/integration/projects/multiple-migrations/migrations/1_initial_migration.js create mode 100644 test/integration/projects/multiple-migrations/migrations/2_contracta.js create mode 100644 test/integration/projects/multiple-migrations/migrations/3_contractb.js create mode 100644 test/integration/projects/multiple-migrations/migrations/4_contractc.js create mode 100644 test/integration/projects/multiple-migrations/test/contracta.js create mode 100644 test/integration/projects/multiple-migrations/test/contractb.js create mode 100644 test/integration/projects/multiple-migrations/test/contractc.js create mode 100644 test/integration/projects/multiple-migrations/truffle-config.js create mode 100644 test/integration/projects/skipping/.solcover.js create mode 100644 test/integration/projects/skipping/contracts/ContractA.sol create mode 100644 test/integration/projects/skipping/contracts/Migrations.sol create mode 100644 test/integration/projects/skipping/contracts/skipped-folder/ContractB.sol create mode 100644 test/integration/projects/skipping/migrations/1_initial_migration.js create mode 100644 test/integration/projects/skipping/test/contract.js create mode 100644 test/integration/projects/skipping/truffle-config.js diff --git a/dist/truffle.plugin.js b/dist/truffle.plugin.js index c41d7f1..bb3021c 100644 --- a/dist/truffle.plugin.js +++ b/dist/truffle.plugin.js @@ -37,20 +37,29 @@ const ganache = require('ganache-core-sc'); async function plugin(truffleConfig){ let app; let error; + let truffle; let testsErrored = false; + let coverageConfig; + let coverageConfigPath; + // Load truffle lib, .solcover.js & launch app try { - // Load truffle lib & coverage config - const truffle = loadTruffleLibrary(); + truffle = loadTruffleLibrary(); - const coverageConfigPath = path.join(truffleConfig.working_directory, '.solcover.js'); - const coverageConfig = req.silent(coverageConfigPath) || {}; + coverageConfigPath = path.join(truffleConfig.working_directory, '.solcover.js'); + coverageConfig = req.silent(coverageConfigPath) || {}; coverageConfig.cwd = truffleConfig.working_directory; coverageConfig.contractsDir = truffleConfig.contracts_directory; - // Start app = new App(coverageConfig); + + } catch (err) { + throw err; + } + + // Instrument and test.. + try { death(app.cleanUp); // Write instrumented sources to temp folder @@ -97,7 +106,6 @@ async function plugin(truffleConfig){ } catch (e) { error = e.stack; } - // Run Istanbul await app.report(); diff --git a/test/integration/projects/import-paths/.gitignore b/test/integration/projects/import-paths/.gitignore new file mode 100644 index 0000000..736e8ae --- /dev/null +++ b/test/integration/projects/import-paths/.gitignore @@ -0,0 +1 @@ +!node_modules \ No newline at end of file diff --git a/test/integration/projects/import-paths/.solcover.js b/test/integration/projects/import-paths/.solcover.js new file mode 100644 index 0000000..f328e37 --- /dev/null +++ b/test/integration/projects/import-paths/.solcover.js @@ -0,0 +1,3 @@ +module.exports = { + silent: process.env.SILENT ? true : false, +}; diff --git a/test/integration/projects/import-paths/assets/RelativePathImport.sol b/test/integration/projects/import-paths/assets/RelativePathImport.sol new file mode 100644 index 0000000..449f2c3 --- /dev/null +++ b/test/integration/projects/import-paths/assets/RelativePathImport.sol @@ -0,0 +1,10 @@ +pragma solidity >=0.4.21 <0.6.0; + +contract RelativePathImport { + uint r; + constructor() public {} + + function isRelativePathMethod() public { + r = 5; + } +} diff --git a/test/integration/projects/import-paths/contracts/Migrations.sol b/test/integration/projects/import-paths/contracts/Migrations.sol new file mode 100644 index 0000000..c378ffb --- /dev/null +++ b/test/integration/projects/import-paths/contracts/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity >=0.4.21 <0.6.0; + +contract Migrations { + address public owner; + uint public last_completed_migration; + + constructor() public { + owner = msg.sender; + } + + modifier restricted() { + if (msg.sender == owner) _; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) public restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/test/integration/projects/import-paths/contracts/UsesImports.sol b/test/integration/projects/import-paths/contracts/UsesImports.sol new file mode 100644 index 0000000..b309d7e --- /dev/null +++ b/test/integration/projects/import-paths/contracts/UsesImports.sol @@ -0,0 +1,17 @@ +pragma solidity >=0.4.21 <0.6.0; + +import "../assets/RelativePathImport.sol"; +import "package/NodeModulesImport.sol"; + +contract UsesImports is RelativePathImport, NodeModulesImport { + + constructor() public {} + + function wrapsRelativePathMethod() public { + isRelativePathMethod(); + } + + function wrapsNodeModulesMethod() public { + isNodeModulesMethod(); + } +} \ No newline at end of file diff --git a/test/integration/projects/import-paths/migrations/1_initial_migration.js b/test/integration/projects/import-paths/migrations/1_initial_migration.js new file mode 100644 index 0000000..ee2135d --- /dev/null +++ b/test/integration/projects/import-paths/migrations/1_initial_migration.js @@ -0,0 +1,5 @@ +const Migrations = artifacts.require("Migrations"); + +module.exports = function(deployer) { + deployer.deploy(Migrations); +}; diff --git a/test/integration/projects/import-paths/node_modules/package/NodeModulesImport.sol b/test/integration/projects/import-paths/node_modules/package/NodeModulesImport.sol new file mode 100644 index 0000000..7e56f73 --- /dev/null +++ b/test/integration/projects/import-paths/node_modules/package/NodeModulesImport.sol @@ -0,0 +1,10 @@ +pragma solidity >=0.4.21 <0.6.0; + +contract NodeModulesImport { + uint x; + constructor() public {} + + function isNodeModulesMethod() public { + x = 5; + } +} diff --git a/test/integration/projects/import-paths/test/uses_imports.js b/test/integration/projects/import-paths/test/uses_imports.js new file mode 100644 index 0000000..437dcde --- /dev/null +++ b/test/integration/projects/import-paths/test/uses_imports.js @@ -0,0 +1,16 @@ +var UsesImports = artifacts.require("UsesImports"); + +contract("UsesImports", function(accounts) { + let instance; + + before(async () => instance = await UsesImports.new()); + + it('uses a method from a relative import', async () => { + await instance.wrapsRelativePathMethod(); + }) + + it('uses an import from node_modules', async () => { + await instance.wrapsNodeModulesMethod(); + }) + +}); diff --git a/test/integration/projects/import-paths/truffle-config.js b/test/integration/projects/import-paths/truffle-config.js new file mode 100644 index 0000000..3865690 --- /dev/null +++ b/test/integration/projects/import-paths/truffle-config.js @@ -0,0 +1,99 @@ +/** + * 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 + 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" + // } + } + } +} diff --git a/test/integration/projects/multiple-migrations/.solcover.js b/test/integration/projects/multiple-migrations/.solcover.js new file mode 100644 index 0000000..f328e37 --- /dev/null +++ b/test/integration/projects/multiple-migrations/.solcover.js @@ -0,0 +1,3 @@ +module.exports = { + silent: process.env.SILENT ? true : false, +}; diff --git a/test/integration/projects/multiple-migrations/contracts/ContractA.sol b/test/integration/projects/multiple-migrations/contracts/ContractA.sol new file mode 100644 index 0000000..9d8d134 --- /dev/null +++ b/test/integration/projects/multiple-migrations/contracts/ContractA.sol @@ -0,0 +1,17 @@ +pragma solidity ^0.5.0; + + +contract ContractA { + uint x; + constructor() public { + } + + function sendFn() public { + x = 5; + } + + function callFn() public pure returns (uint){ + uint y = 5; + return y; + } +} diff --git a/test/integration/projects/multiple-migrations/contracts/ContractB.sol b/test/integration/projects/multiple-migrations/contracts/ContractB.sol new file mode 100644 index 0000000..daa42f7 --- /dev/null +++ b/test/integration/projects/multiple-migrations/contracts/ContractB.sol @@ -0,0 +1,17 @@ +pragma solidity ^0.5.0; + + +contract ContractB { + uint x; + constructor() public { + } + + function sendFn() public { + x = 5; + } + + function callFn() public pure returns (uint){ + uint y = 5; + return y; + } +} diff --git a/test/integration/projects/multiple-migrations/contracts/ContractC.sol b/test/integration/projects/multiple-migrations/contracts/ContractC.sol new file mode 100644 index 0000000..454c86c --- /dev/null +++ b/test/integration/projects/multiple-migrations/contracts/ContractC.sol @@ -0,0 +1,17 @@ +pragma solidity ^0.5.0; + + +contract ContractC { + uint x; + constructor() public { + } + + function sendFn() public { + x = 5; + } + + function callFn() public pure returns (uint){ + uint y = 5; + return y; + } +} diff --git a/test/integration/projects/multiple-migrations/contracts/Migrations.sol b/test/integration/projects/multiple-migrations/contracts/Migrations.sol new file mode 100644 index 0000000..c378ffb --- /dev/null +++ b/test/integration/projects/multiple-migrations/contracts/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity >=0.4.21 <0.6.0; + +contract Migrations { + address public owner; + uint public last_completed_migration; + + constructor() public { + owner = msg.sender; + } + + modifier restricted() { + if (msg.sender == owner) _; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) public restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/test/integration/projects/multiple-migrations/migrations/1_initial_migration.js b/test/integration/projects/multiple-migrations/migrations/1_initial_migration.js new file mode 100644 index 0000000..ee2135d --- /dev/null +++ b/test/integration/projects/multiple-migrations/migrations/1_initial_migration.js @@ -0,0 +1,5 @@ +const Migrations = artifacts.require("Migrations"); + +module.exports = function(deployer) { + deployer.deploy(Migrations); +}; diff --git a/test/integration/projects/multiple-migrations/migrations/2_contracta.js b/test/integration/projects/multiple-migrations/migrations/2_contracta.js new file mode 100644 index 0000000..ff990ec --- /dev/null +++ b/test/integration/projects/multiple-migrations/migrations/2_contracta.js @@ -0,0 +1,5 @@ +const ContractA = artifacts.require("ContractA"); + +module.exports = function(deployer) { + deployer.deploy(ContractA); +}; \ No newline at end of file diff --git a/test/integration/projects/multiple-migrations/migrations/3_contractb.js b/test/integration/projects/multiple-migrations/migrations/3_contractb.js new file mode 100644 index 0000000..f81775a --- /dev/null +++ b/test/integration/projects/multiple-migrations/migrations/3_contractb.js @@ -0,0 +1,5 @@ +const ContractB = artifacts.require("ContractB"); + +module.exports = function(deployer) { + deployer.deploy(ContractB); +}; \ No newline at end of file diff --git a/test/integration/projects/multiple-migrations/migrations/4_contractc.js b/test/integration/projects/multiple-migrations/migrations/4_contractc.js new file mode 100644 index 0000000..279761a --- /dev/null +++ b/test/integration/projects/multiple-migrations/migrations/4_contractc.js @@ -0,0 +1,5 @@ +const ContractC = artifacts.require("ContractC"); + +module.exports = function(deployer) { + deployer.deploy(ContractC); +}; \ No newline at end of file diff --git a/test/integration/projects/multiple-migrations/test/contracta.js b/test/integration/projects/multiple-migrations/test/contracta.js new file mode 100644 index 0000000..0559b95 --- /dev/null +++ b/test/integration/projects/multiple-migrations/test/contracta.js @@ -0,0 +1,15 @@ +const ContractA = artifacts.require("ContractA"); + +contract("contracta", function(accounts) { + let instance; + + before(async () => instance = await ContractA.deployed()) + + it('sends', async function(){ + await instance.sendFn(); + }); + + it('calls', async function(){ + await instance.callFn(); + }) +}); diff --git a/test/integration/projects/multiple-migrations/test/contractb.js b/test/integration/projects/multiple-migrations/test/contractb.js new file mode 100644 index 0000000..14b4cc2 --- /dev/null +++ b/test/integration/projects/multiple-migrations/test/contractb.js @@ -0,0 +1,15 @@ +const ContractB = artifacts.require("ContractB"); + +contract("contractB", function(accounts) { + let instance; + + before(async () => instance = await ContractB.deployed()) + + it('sends', async function(){ + await instance.sendFn(); + }); + + it('calls', async function(){ + await instance.callFn(); + }) +}); diff --git a/test/integration/projects/multiple-migrations/test/contractc.js b/test/integration/projects/multiple-migrations/test/contractc.js new file mode 100644 index 0000000..124f47a --- /dev/null +++ b/test/integration/projects/multiple-migrations/test/contractc.js @@ -0,0 +1,20 @@ +const ContractC = artifacts.require("ContractC"); + +contract("contractc", function(accounts) { + let instance; + + before(async () => instance = await ContractC.deployed()) + + it('sends', async function(){ + await instance.sendFn(); + }); + + it('calls', async function(){ + await instance.callFn(); + }) + + it('sends', async function(){ + await instance.sendFn(); + }); + +}); diff --git a/test/integration/projects/multiple-migrations/truffle-config.js b/test/integration/projects/multiple-migrations/truffle-config.js new file mode 100644 index 0000000..3865690 --- /dev/null +++ b/test/integration/projects/multiple-migrations/truffle-config.js @@ -0,0 +1,99 @@ +/** + * 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 + 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" + // } + } + } +} diff --git a/test/integration/projects/skipping/.solcover.js b/test/integration/projects/skipping/.solcover.js new file mode 100644 index 0000000..681cd56 --- /dev/null +++ b/test/integration/projects/skipping/.solcover.js @@ -0,0 +1,4 @@ +module.exports = { + silent: process.env.SILENT ? true : false, + skipFiles: ['skipped-folder'] +} diff --git a/test/integration/projects/skipping/contracts/ContractA.sol b/test/integration/projects/skipping/contracts/ContractA.sol new file mode 100644 index 0000000..9d8d134 --- /dev/null +++ b/test/integration/projects/skipping/contracts/ContractA.sol @@ -0,0 +1,17 @@ +pragma solidity ^0.5.0; + + +contract ContractA { + uint x; + constructor() public { + } + + function sendFn() public { + x = 5; + } + + function callFn() public pure returns (uint){ + uint y = 5; + return y; + } +} diff --git a/test/integration/projects/skipping/contracts/Migrations.sol b/test/integration/projects/skipping/contracts/Migrations.sol new file mode 100644 index 0000000..c378ffb --- /dev/null +++ b/test/integration/projects/skipping/contracts/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity >=0.4.21 <0.6.0; + +contract Migrations { + address public owner; + uint public last_completed_migration; + + constructor() public { + owner = msg.sender; + } + + modifier restricted() { + if (msg.sender == owner) _; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) public restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/test/integration/projects/skipping/contracts/skipped-folder/ContractB.sol b/test/integration/projects/skipping/contracts/skipped-folder/ContractB.sol new file mode 100644 index 0000000..daa42f7 --- /dev/null +++ b/test/integration/projects/skipping/contracts/skipped-folder/ContractB.sol @@ -0,0 +1,17 @@ +pragma solidity ^0.5.0; + + +contract ContractB { + uint x; + constructor() public { + } + + function sendFn() public { + x = 5; + } + + function callFn() public pure returns (uint){ + uint y = 5; + return y; + } +} diff --git a/test/integration/projects/skipping/migrations/1_initial_migration.js b/test/integration/projects/skipping/migrations/1_initial_migration.js new file mode 100644 index 0000000..ee2135d --- /dev/null +++ b/test/integration/projects/skipping/migrations/1_initial_migration.js @@ -0,0 +1,5 @@ +const Migrations = artifacts.require("Migrations"); + +module.exports = function(deployer) { + deployer.deploy(Migrations); +}; diff --git a/test/integration/projects/skipping/test/contract.js b/test/integration/projects/skipping/test/contract.js new file mode 100644 index 0000000..f449e1c --- /dev/null +++ b/test/integration/projects/skipping/test/contract.js @@ -0,0 +1,29 @@ +const ContractA = artifacts.require("ContractA"); +const ContractB = artifacts.require("ContractB"); + +contract("contracts", function(accounts) { + let instanceA; + let instanceB; + + before(async () => { + instanceA = await ContractA.new(); + instanceB = await ContractB.new(); + }); + + it('A sends', async function(){ + await instanceA.sendFn(); + }); + + it('A calls', async function(){ + await instanceA.callFn(); + }); + + it('B sends', async function(){ + await instanceB.sendFn(); + }); + + it('B calls', async function(){ + await instanceB.callFn(); + }); + +}); diff --git a/test/integration/projects/skipping/truffle-config.js b/test/integration/projects/skipping/truffle-config.js new file mode 100644 index 0000000..3865690 --- /dev/null +++ b/test/integration/projects/skipping/truffle-config.js @@ -0,0 +1,99 @@ +/** + * 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 + 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" + // } + } + } +} diff --git a/test/units/app.js b/test/units/app.js index f35f21e..1583028 100644 --- a/test/units/app.js +++ b/test/units/app.js @@ -80,24 +80,25 @@ describe('app', function() { await plugin(truffleConfig); }); - it.skip('with pure and view modifiers and libraries', () => { + it('project uses multiple migrations', async function() { assertCleanInitialState(); + mock.installFullProject('multiple-migrations'); + await plugin(truffleConfig); + }); - mock.installDouble(config); - shell.exec(script); - assert(shell.error() === null, 'script should not error'); - - assertCoverageGenerated(); - - // Coverage should be real. - // This test is tightly bound to the function names in TotallyPure.sol - const produced = JSON.parse(fs.readFileSync('./coverage.json', 'utf8')); - const path = Object.keys(produced)[0]; - assert(produced[path].fnMap['1'].name === 'usesThem', 'should map "usesThem"'); - assert(produced[path].fnMap['2'].name === 'isPure', 'should map "getX"'); + it('project skips a folder', async function() { + assertCleanInitialState(); + mock.installFullProject('skipping'); + await plugin(truffleConfig); + }); + it.skip('project with node_modules packages and relative path solidity imports', async function() { + assertCleanInitialState(); + mock.installFullProject('import-paths'); + await plugin(truffleConfig); }); + it('contract only uses ".call"', async function(){ assertCleanInitialState(); diff --git a/test/util/integration.truffle.js b/test/util/integration.truffle.js index 165be8d..b136050 100644 --- a/test/util/integration.truffle.js +++ b/test/util/integration.truffle.js @@ -16,6 +16,7 @@ const testPath = './test/sources/js/'; const sourcesPath = './test/sources/solidity/contracts/app/'; const migrationPath = `${temp}/migrations/2_deploy.js`; const templatePath = './test/integration/truffle/*'; +const projectPath = './test/integration/projects/' function getDefaultTruffleConfig(){ const logger = process.env.SILENT ? { log: () => {} } : console; @@ -77,6 +78,11 @@ function deployDouble(contractNames){ `; } +function decacheConfigs(){ + decache(`${process.cwd()}/${temp}/.solcover.js`); + decache(`${process.cwd()}/${temp}/${truffleConfigName}`); +} + /** * Installs mock truffle project at ./temp with a single contract * and test specified by the params. @@ -112,8 +118,7 @@ function install( fs.writeFileSync(`${temp}/${truffleConfigName}`, trufflejs); fs.writeFileSync(configPath, configjs); - decache(`${process.cwd()}/${temp}/.solcover.js`); - decache(`${process.cwd()}/${temp}/${truffleConfigName}`); + decacheConfigs(); }; @@ -144,10 +149,16 @@ function installDouble(contracts, test, config) { fs.writeFileSync(`${temp}/${truffleConfigName}`, getTruffleConfigJS()); fs.writeFileSync(configPath, configjs); - decache(`${process.cwd()}/${temp}/.solcover.js`); - decache(`${process.cwd()}/${temp}/${truffleConfigName}`); + decacheConfigs(); }; +function installFullProject(name) { + shell.mkdir(temp); + shell.cp('-Rf', `${projectPath}${name}/{.,}*`, temp); + + decacheConfigs(); +} + /** * Removes mock truffle project and coverage reports generated by test @@ -167,6 +178,7 @@ module.exports = { getDefaultTruffleConfig: getDefaultTruffleConfig, install: install, installDouble: installDouble, + installFullProject: installFullProject, clean: clean }