diff --git a/CHANGELOG.md b/CHANGELOG.md index 37c6ce0432..5f1cfc5409 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - Basic support for `dyn Trait` to allow cross-contract calls only with trait - [#1673](https://github.com/paritytech/ink/pull/1673) +- E2E: auto detect contracts to be built - [#1691](https://github.com/paritytech/ink/pull/1691) ## Version 4.0.1 diff --git a/crates/e2e/macro/Cargo.toml b/crates/e2e/macro/Cargo.toml index ab1dcd698d..14601e90c2 100644 --- a/crates/e2e/macro/Cargo.toml +++ b/crates/e2e/macro/Cargo.toml @@ -20,6 +20,7 @@ proc-macro = true [dependencies] ink_ir = { version = "4.0.1", path = "../../ink/ir" } +cargo_metadata = "0.15.3" contract-build = "2.0.2" derive_more = "0.99.17" env_logger = "0.10.0" diff --git a/crates/e2e/macro/src/codegen.rs b/crates/e2e/macro/src/codegen.rs index a20dcb25f4..046f35c8e1 100644 --- a/crates/e2e/macro/src/codegen.rs +++ b/crates/e2e/macro/src/codegen.rs @@ -13,6 +13,7 @@ // limitations under the License. use crate::ir; +use contract_build::ManifestPath; use core::cell::RefCell; use derive_more::From; use proc_macro2::TokenStream as TokenStream2; @@ -78,11 +79,20 @@ impl InkE2ETest { .environment() .unwrap_or_else(|| syn::parse_quote! { ::ink::env::DefaultEnvironment }); - let mut additional_contracts: Vec = - self.test.config.additional_contracts(); - let default_main_contract_manifest_path = String::from("Cargo.toml"); - let mut contracts_to_build_and_import = vec![default_main_contract_manifest_path]; - contracts_to_build_and_import.append(&mut additional_contracts); + let contract_manifests = ContractManifests::from_crate_metadata(); + + let contracts_to_build_and_import = + if self.test.config.additional_contracts().is_empty() { + contract_manifests.all_contracts_to_build() + } else { + // backwards compatibility if `additional_contracts` specified + let mut additional_contracts: Vec = + self.test.config.additional_contracts(); + let mut contracts_to_build_and_import: Vec = + contract_manifests.root_package.iter().cloned().collect(); + contracts_to_build_and_import.append(&mut additional_contracts); + contracts_to_build_and_import + }; let mut already_built_contracts = already_built_contracts(); if already_built_contracts.is_empty() { @@ -100,7 +110,7 @@ impl InkE2ETest { // `additional_contracts` for this particular test contain ones // that haven't been build before for manifest_path in contracts_to_build_and_import { - if already_built_contracts.get("Cargo.toml").is_none() { + if already_built_contracts.get(&manifest_path).is_none() { let dest_wasm = build_contract(&manifest_path); let _ = already_built_contracts.insert(manifest_path, dest_wasm); } @@ -188,6 +198,63 @@ impl InkE2ETest { } } +#[derive(Debug)] +struct ContractManifests { + /// The manifest path of the root package where the E2E test is defined. + /// `None` if the root package is not an `ink!` contract definition. + root_package: Option, + /// The manifest paths of any dependencies which are `ink!` contracts. + contract_dependencies: Vec, +} + +impl ContractManifests { + /// Load any manifests for packages which are detected to be `ink!` contracts. Any package + /// with the `ink-as-dependency` feature enabled is assumed to be an `ink!` contract. + fn from_crate_metadata() -> Self { + // The default manifest path is the `Cargo.toml` in the current directory. + // So in this case the package within which the E2E test is defined. + let manifest_path = ManifestPath::default(); + let crate_metadata = contract_build::CrateMetadata::collect(&manifest_path) + .unwrap_or_else(|err| { + panic!( + "Error loading crate metadata for '{}': {}", + manifest_path.as_ref().display(), + err + ) + }); + + fn maybe_contract_package(package: &cargo_metadata::Package) -> Option { + package + .features + .iter() + .any(|(feat, _)| feat == "ink-as-dependency") + .then(|| package.manifest_path.to_string()) + } + + let root_package = maybe_contract_package(&crate_metadata.root_package); + + let contract_dependencies = crate_metadata + .cargo_meta + .packages + .iter() + .filter_map(maybe_contract_package) + .collect(); + + Self { + root_package, + contract_dependencies, + } + } + + /// Returns all the contract manifests which are to be built, including the root package + /// if it is determined to be an `ink!` contract. + fn all_contracts_to_build(&self) -> Vec { + let mut all_manifests: Vec = self.root_package.iter().cloned().collect(); + all_manifests.append(&mut self.contract_dependencies.clone()); + all_manifests + } +} + /// Builds the contract at `manifest_path`, returns the path to the contract /// Wasm build artifact. fn build_contract(path_to_cargo_toml: &str) -> String { @@ -196,7 +263,6 @@ fn build_contract(path_to_cargo_toml: &str) -> String { BuildMode, ExecuteArgs, Features, - ManifestPath, Network, OptimizationPasses, OutputType, diff --git a/crates/e2e/src/client.rs b/crates/e2e/src/client.rs index 74735a9b6f..b92419311c 100644 --- a/crates/e2e/src/client.rs +++ b/crates/e2e/src/client.rs @@ -494,7 +494,14 @@ where let wasm_path = self .contracts .get(&contract.replace('-', "_")) - .unwrap_or_else(|| panic!("Unknown contract {contract}")); + .unwrap_or_else(|| + panic!( + "Unknown contract {contract}. Available contracts: {:?}.\n\ + For a contract to be built, add it as a dependency to the `Cargo.toml`, or add \ + the manifest path to `#[ink_e2e::test(additional_contracts = ..)]`", + self.contracts.keys() + ) + ); let code = std::fs::read(wasm_path).unwrap_or_else(|err| { panic!("Error loading '{}': {:?}", wasm_path.display(), err) }); diff --git a/integration-tests/delegator/lib.rs b/integration-tests/delegator/lib.rs index 880c3dcbd8..97e0c7de0e 100644 --- a/integration-tests/delegator/lib.rs +++ b/integration-tests/delegator/lib.rs @@ -118,9 +118,7 @@ mod delegator { type E2EResult = std::result::Result>; - #[ink_e2e::test( - additional_contracts = "accumulator/Cargo.toml adder/Cargo.toml subber/Cargo.toml" - )] + #[ink_e2e::test] async fn e2e_delegator(mut client: ink_e2e::Client) -> E2EResult<()> { // given let accumulator_hash = client diff --git a/integration-tests/lang-err-integration-tests/call-builder/lib.rs b/integration-tests/lang-err-integration-tests/call-builder/lib.rs index 092ad1d035..d2562bb1ff 100755 --- a/integration-tests/lang-err-integration-tests/call-builder/lib.rs +++ b/integration-tests/lang-err-integration-tests/call-builder/lib.rs @@ -166,9 +166,7 @@ mod call_builder { type E2EResult = std::result::Result>; - #[ink_e2e::test( - additional_contracts = "../integration-flipper/Cargo.toml ../constructors-return-value/Cargo.toml" - )] + #[ink_e2e::test] async fn e2e_invalid_message_selector_can_be_handled( mut client: ink_e2e::Client, ) -> E2EResult<()> { @@ -221,7 +219,7 @@ mod call_builder { Ok(()) } - #[ink_e2e::test(additional_contracts = "../integration-flipper/Cargo.toml")] + #[ink_e2e::test] async fn e2e_invalid_message_selector_panics_on_invoke( mut client: ink_e2e::Client, ) -> E2EResult<()> { @@ -258,7 +256,7 @@ mod call_builder { Ok(()) } - #[ink_e2e::test(additional_contracts = "../constructors-return-value/Cargo.toml")] + #[ink_e2e::test] async fn e2e_create_builder_works_with_valid_selector( mut client: ink_e2e::Client, ) -> E2EResult<()> { @@ -299,7 +297,7 @@ mod call_builder { Ok(()) } - #[ink_e2e::test(additional_contracts = "../constructors-return-value/Cargo.toml")] + #[ink_e2e::test] async fn e2e_create_builder_fails_with_invalid_selector( mut client: ink_e2e::Client, ) -> E2EResult<()> { @@ -340,7 +338,7 @@ mod call_builder { Ok(()) } - #[ink_e2e::test(additional_contracts = "../constructors-return-value/Cargo.toml")] + #[ink_e2e::test] async fn e2e_create_builder_with_infallible_revert_constructor_encodes_ok( mut client: ink_e2e::Client, ) -> E2EResult<()> { @@ -385,7 +383,7 @@ mod call_builder { Ok(()) } - #[ink_e2e::test(additional_contracts = "../constructors-return-value/Cargo.toml")] + #[ink_e2e::test] async fn e2e_create_builder_can_handle_fallible_constructor_success( mut client: ink_e2e::Client, ) -> E2EResult<()> { @@ -426,7 +424,7 @@ mod call_builder { Ok(()) } - #[ink_e2e::test(additional_contracts = "../constructors-return-value/Cargo.toml")] + #[ink_e2e::test] async fn e2e_create_builder_can_handle_fallible_constructor_error( mut client: ink_e2e::Client, ) -> E2EResult<()> { @@ -474,7 +472,7 @@ mod call_builder { Ok(()) } - #[ink_e2e::test(additional_contracts = "../constructors-return-value/Cargo.toml")] + #[ink_e2e::test] async fn e2e_create_builder_with_fallible_revert_constructor_encodes_ok( mut client: ink_e2e::Client, ) -> E2EResult<()> { @@ -519,7 +517,7 @@ mod call_builder { Ok(()) } - #[ink_e2e::test(additional_contracts = "../constructors-return-value/Cargo.toml")] + #[ink_e2e::test] async fn e2e_create_builder_with_fallible_revert_constructor_encodes_err( mut client: ink_e2e::Client, ) -> E2EResult<()> { diff --git a/integration-tests/lang-err-integration-tests/contract-ref/lib.rs b/integration-tests/lang-err-integration-tests/contract-ref/lib.rs index 963722d79d..f046ca821f 100755 --- a/integration-tests/lang-err-integration-tests/contract-ref/lib.rs +++ b/integration-tests/lang-err-integration-tests/contract-ref/lib.rs @@ -72,7 +72,7 @@ mod contract_ref { type E2EResult = std::result::Result>; - #[ink_e2e::test(additional_contracts = "../integration-flipper/Cargo.toml")] + #[ink_e2e::test] async fn e2e_ref_can_flip_correctly( mut client: ink_e2e::Client, ) -> E2EResult<()> { @@ -117,7 +117,7 @@ mod contract_ref { Ok(()) } - #[ink_e2e::test(additional_contracts = "../integration-flipper/Cargo.toml")] + #[ink_e2e::test] async fn e2e_fallible_ref_can_be_instantiated( mut client: ink_e2e::Client, ) -> E2EResult<()> { @@ -147,7 +147,7 @@ mod contract_ref { Ok(()) } - #[ink_e2e::test(additional_contracts = "../integration-flipper/Cargo.toml")] + #[ink_e2e::test] async fn e2e_fallible_ref_fails_to_be_instantiated( mut client: ink_e2e::Client, ) -> E2EResult<()> {