-
Notifications
You must be signed in to change notification settings - Fork 480
Support custom environment in E2E tests #1645
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
HCastano
merged 19 commits into
use-ink:master
from
pmikolajczyk41:pmikolajczyk41/e2e/custom-env
Feb 14, 2023
Merged
Changes from 12 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
d66a194
Remove seemingly unused struct
pmikolajczyk41 d91ab9c
Customize environment in e2e
pmikolajczyk41 3ded505
Example contract
pmikolajczyk41 0821d69
Ignore test
pmikolajczyk41 1bd6c99
CHANGELOG.md, README.md
pmikolajczyk41 f968ffe
Remove feature leftover
pmikolajczyk41 e289ded
Review comments
pmikolajczyk41 6a63605
Remove dependencies
pmikolajczyk41 a7499b0
syn::Path
pmikolajczyk41 557b4ea
::ink
pmikolajczyk41 6a914d5
README.md
pmikolajczyk41 192047f
Example docs
pmikolajczyk41 d22506b
Review
pmikolajczyk41 5abff86
Add feature instead of ignoring
pmikolajczyk41 d1737eb
Merge remote-tracking branch 'origin/master' into pmikolajczyk41/e2e/…
pmikolajczyk41 9e9d0de
Doc rewording. Event type matching
pmikolajczyk41 21f0651
Missing testcase
pmikolajczyk41 0953a0f
Fix test
pmikolajczyk41 3921217
Fix test
pmikolajczyk41 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,8 @@ pub struct E2EConfig { | |
| whitelisted_attributes: WhitelistedAttributes, | ||
| /// Additional contracts that have to be built before executing the test. | ||
| additional_contracts: Vec<String>, | ||
| /// Custom environment for the contracts, if specified. Otherwise `DefaultEnvironment` is used. | ||
| environment: Option<syn::Path>, | ||
| } | ||
|
|
||
| impl TryFrom<ast::AttributeArgs> for E2EConfig { | ||
|
|
@@ -36,6 +38,7 @@ impl TryFrom<ast::AttributeArgs> for E2EConfig { | |
| fn try_from(args: ast::AttributeArgs) -> Result<Self, Self::Error> { | ||
| let mut whitelisted_attributes = WhitelistedAttributes::default(); | ||
| let mut additional_contracts: Option<(syn::LitStr, ast::MetaNameValue)> = None; | ||
| let mut environment: Option<(syn::Path, ast::MetaNameValue)> = None; | ||
|
|
||
| for arg in args.into_iter() { | ||
| if arg.name.is_ident("keep_attr") { | ||
|
|
@@ -46,15 +49,27 @@ impl TryFrom<ast::AttributeArgs> for E2EConfig { | |
| ast, | ||
| arg, | ||
| "additional_contracts", | ||
| "e2e test", | ||
| "E2E test", | ||
| )) | ||
| } | ||
| if let ast::PathOrLit::Lit(syn::Lit::Str(lit_str)) = &arg.value { | ||
| additional_contracts = Some((lit_str.clone(), arg)) | ||
| } else { | ||
| return Err(format_err_spanned!( | ||
| arg, | ||
| "expected a bool literal for `additional_contracts` ink! e2e test configuration argument", | ||
| "expected a string literal for `additional_contracts` ink! E2E test configuration argument", | ||
| )) | ||
| } | ||
| } else if arg.name.is_ident("environment") { | ||
| if let Some((_, ast)) = environment { | ||
| return Err(duplicate_config_err(ast, arg, "environment", "E2E test")) | ||
| } | ||
| if let ast::PathOrLit::Path(path) = &arg.value { | ||
| environment = Some((path.clone(), arg)) | ||
| } else { | ||
| return Err(format_err_spanned!( | ||
| arg, | ||
| "expected a path for `environment` ink! E2E test configuration argument", | ||
| )) | ||
| } | ||
| } else { | ||
|
|
@@ -67,9 +82,12 @@ impl TryFrom<ast::AttributeArgs> for E2EConfig { | |
| let additional_contracts = additional_contracts | ||
| .map(|(value, _)| value.value().split(' ').map(String::from).collect()) | ||
| .unwrap_or_else(Vec::new); | ||
| let environment = environment.map(|(path, _)| path); | ||
|
|
||
| Ok(E2EConfig { | ||
| additional_contracts, | ||
| whitelisted_attributes, | ||
| environment, | ||
| }) | ||
| } | ||
| } | ||
|
|
@@ -80,20 +98,10 @@ impl E2EConfig { | |
| pub fn additional_contracts(&self) -> Vec<String> { | ||
| self.additional_contracts.clone() | ||
| } | ||
| } | ||
|
|
||
| /// The environmental types definition. | ||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||
| pub struct Environment { | ||
| /// The underlying Rust type. | ||
| pub path: syn::Path, | ||
| } | ||
|
|
||
| impl Default for Environment { | ||
| fn default() -> Self { | ||
| Self { | ||
| path: syn::parse_quote! { ::ink_env::DefaultEnvironment }, | ||
| } | ||
| /// Custom environment for the contracts, if specified. | ||
| pub fn environment(&self) -> Option<syn::Path> { | ||
| self.environment.clone() | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -128,18 +136,59 @@ mod tests { | |
| } | ||
|
|
||
| #[test] | ||
| fn duplicate_args_fails() { | ||
| fn duplicate_additional_contracts_fails() { | ||
| assert_try_from( | ||
| syn::parse_quote! { | ||
| additional_contracts = "adder/Cargo.toml", | ||
| additional_contracts = "adder/Cargo.toml", | ||
| }, | ||
| Err( | ||
| "encountered duplicate ink! e2e test `additional_contracts` configuration argument", | ||
| "encountered duplicate ink! E2E test `additional_contracts` configuration argument", | ||
| ), | ||
| ); | ||
| } | ||
|
|
||
HCastano marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| #[test] | ||
| fn duplicate_environment_fails() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would also be cool to see some UI tests for the E2E macros, but that would be for a future PR |
||
| assert_try_from( | ||
| syn::parse_quote! { | ||
| environment = crate::CustomEnvironment, | ||
| environment = crate::CustomEnvironment, | ||
| }, | ||
| Err( | ||
| "encountered duplicate ink! E2E test `environment` configuration argument", | ||
| ), | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn environment_as_literal_fails() { | ||
| assert_try_from( | ||
| syn::parse_quote! { | ||
| environment = "crate::CustomEnvironment", | ||
| }, | ||
| Err("expected a path for `environment` ink! E2E test configuration argument"), | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn full_config_works() { | ||
| assert_try_from( | ||
| syn::parse_quote! { | ||
| additional_contracts = "adder/Cargo.toml flipper/Cargo.toml", | ||
| environment = crate::CustomEnvironment, | ||
HCastano marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }, | ||
| Ok(E2EConfig { | ||
| whitelisted_attributes: Default::default(), | ||
| additional_contracts: vec![ | ||
| "adder/Cargo.toml".into(), | ||
| "flipper/Cargo.toml".into(), | ||
| ], | ||
| environment: Some(syn::parse_quote! { crate::CustomEnvironment }), | ||
| }), | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn keep_attr_works() { | ||
| let mut attrs = WhitelistedAttributes::default(); | ||
|
|
@@ -152,6 +201,7 @@ mod tests { | |
| Ok(E2EConfig { | ||
| whitelisted_attributes: attrs, | ||
| additional_contracts: Vec::new(), | ||
| environment: None, | ||
| }), | ||
| ) | ||
| } | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| # Ignore build artifacts from the local tests sub-crate. | ||
| /target/ | ||
|
|
||
| # Ignore backup files creates by cargo fmt. | ||
| **/*.rs.bk | ||
|
|
||
| # Remove Cargo.lock when creating an executable, leave it for libraries | ||
| # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock | ||
| Cargo.lock |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| [package] | ||
| name = "custom-environment" | ||
| version = "4.0.0-rc" | ||
| authors = ["Parity Technologies <[email protected]>"] | ||
| edition = "2021" | ||
| publish = false | ||
|
|
||
| [dependencies] | ||
| ink = { path = "../../crates/ink", default-features = false } | ||
|
|
||
| scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } | ||
| scale-info = { version = "2.3", default-features = false, features = ["derive"], optional = true } | ||
|
|
||
| [dev-dependencies] | ||
| ink_e2e = { path = "../../crates/e2e" } | ||
|
|
||
| [lib] | ||
HCastano marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| path = "lib.rs" | ||
|
|
||
| [features] | ||
| default = ["std"] | ||
| std = [ | ||
| "ink/std", | ||
| "scale/std", | ||
| "scale-info/std", | ||
| ] | ||
| ink-as-dependency = [] | ||
| e2e-tests = [] | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| # `custom-environment` example | ||
|
|
||
| ## What is this example about? | ||
|
|
||
| It demonstrates how to use custom environment, both in the contract and in the E2E tests. | ||
|
|
||
| ## Chain-side configuration | ||
|
|
||
| To integrate this example into Substrate you need to adjust pallet contracts configuration in your runtime: | ||
|
|
||
| ```rust | ||
| // In your node's runtime configuration file (runtime.rs) | ||
| parameter_types! { | ||
| pub Schedule: pallet_contracts::Schedule<Runtime> = pallet_contracts::Schedule::<Runtime> { | ||
| limits: pallet_contracts::Limits { | ||
| event_topics: 6, | ||
| ..Default::default() | ||
| }, | ||
| ..Default::default() | ||
| }; | ||
| } | ||
|
|
||
| impl pallet_contracts::Config for Runtime { | ||
| … | ||
| type Schedule = Schedule; | ||
| … | ||
| } | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| #![cfg_attr(not(feature = "std"), no_std)] | ||
HCastano marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| use ink::env::Environment; | ||
HCastano marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| /// Our custom environment diverges from the `DefaultEnvironment` in the event topics limit. | ||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||
| #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] | ||
| pub enum EnvironmentWithManyTopics {} | ||
|
|
||
| impl Environment for EnvironmentWithManyTopics { | ||
| // We allow for 5 topics in the event, therefore the contract pallet's schedule must allow for | ||
| // 6 of them (to allow the implicit topic for the event signature). | ||
| const MAX_EVENT_TOPICS: usize = | ||
| <ink::env::DefaultEnvironment as Environment>::MAX_EVENT_TOPICS + 1; | ||
|
|
||
| type AccountId = <ink::env::DefaultEnvironment as Environment>::AccountId; | ||
| type Balance = <ink::env::DefaultEnvironment as Environment>::Balance; | ||
| type Hash = <ink::env::DefaultEnvironment as Environment>::Hash; | ||
| type BlockNumber = <ink::env::DefaultEnvironment as Environment>::BlockNumber; | ||
| type Timestamp = <ink::env::DefaultEnvironment as Environment>::Timestamp; | ||
|
|
||
| type ChainExtension = <ink::env::DefaultEnvironment as Environment>::ChainExtension; | ||
| } | ||
|
|
||
| #[ink::contract(env = crate::EnvironmentWithManyTopics)] | ||
| mod runtime_call { | ||
| /// Trivial contract with a single message that emits an event with many topics. | ||
| #[ink(storage)] | ||
| #[derive(Default)] | ||
| pub struct Topicer; | ||
HCastano marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| /// An event that would be forbidden in the default environment, but is completely valid in | ||
| /// our custom one. | ||
| #[ink(event)] | ||
| #[derive(Default)] | ||
| pub struct TopicedEvent { | ||
HCastano marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| #[ink(topic)] | ||
| first_topic: Balance, | ||
| #[ink(topic)] | ||
| second_topic: Balance, | ||
| #[ink(topic)] | ||
| third_topic: Balance, | ||
| #[ink(topic)] | ||
| fourth_topic: Balance, | ||
| #[ink(topic)] | ||
| fifth_topic: Balance, | ||
| } | ||
|
|
||
| impl Topicer { | ||
| #[ink(constructor)] | ||
| pub fn new() -> Self { | ||
| Self {} | ||
HCastano marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| /// Emit an event with many topics. | ||
| #[ink(message)] | ||
| pub fn trigger(&mut self) { | ||
| self.env().emit_event(TopicedEvent::default()); | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
|
|
||
| #[ink::test] | ||
| fn emits_event_with_many_topics() { | ||
| let mut contract = Topicer::new(); | ||
| contract.trigger(); | ||
|
|
||
| let emitted_events = ink::env::test::recorded_events().collect::<Vec<_>>(); | ||
| assert_eq!(1, emitted_events.len()); | ||
HCastano marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| let _ = <<Topicer as ink::reflect::ContractEventBase>::Type as scale::Decode>::decode(&mut &emitted_events[0].data[..]) | ||
| .expect("encountered invalid contract event data buffer"); | ||
HCastano marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
|
|
||
| #[cfg(all(test, feature = "e2e-tests"))] | ||
| mod e2e_tests { | ||
| use super::*; | ||
| use crate::EnvironmentWithManyTopics; | ||
HCastano marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| use ink_e2e::MessageBuilder; | ||
|
|
||
| type E2EResult<T> = Result<T, Box<dyn std::error::Error>>; | ||
|
|
||
| #[ink_e2e::test(environment = crate::EnvironmentWithManyTopics)] | ||
| #[ignore = "Requires that the pallet contract is configured with a schedule allowing for \ | ||
| more event topics. For example:\ | ||
| ```rust | ||
| pub Schedule: pallet_contracts::Schedule<Runtime> = pallet_contracts::Schedule::<Runtime> { | ||
| limits: pallet_contracts::Limits { | ||
| event_topics: 6, | ||
| ..Default::default() | ||
| }, | ||
| ..Default::default() | ||
| }; | ||
HCastano marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ```"] | ||
| async fn it_works(mut client: Client<C, E>) -> E2EResult<()> { | ||
HCastano marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // given | ||
| let constructor = TopicerRef::new(); | ||
| let contract_acc_id = client | ||
| .instantiate( | ||
| "custom-environment", | ||
| &ink_e2e::alice(), | ||
| constructor, | ||
| 0, | ||
| None, | ||
| ) | ||
| .await | ||
| .expect("instantiate failed") | ||
| .account_id; | ||
|
|
||
| // when | ||
| let message = | ||
| MessageBuilder::<EnvironmentWithManyTopics, TopicerRef>::from_account_id( | ||
HCastano marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| contract_acc_id, | ||
| ) | ||
| .call(|caller| caller.trigger()); | ||
|
|
||
| let call_res = client | ||
| .call(&ink_e2e::alice(), message, 0, None) | ||
| .await | ||
| .expect("call failed"); | ||
|
|
||
| // then | ||
| call_res.contains_event("Contracts", "ContractEmitted"); | ||
|
|
||
| Ok(()) | ||
| } | ||
| } | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.