Skip to content
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

[E2E alternative backend]: Backend choice #1864

Merged
merged 28 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c04f89a
Parse backend type
pmikolajczyk41 Aug 2, 2023
c13ceca
Config tests
pmikolajczyk41 Aug 2, 2023
816030b
Extract client building
pmikolajczyk41 Aug 2, 2023
96c124f
OCD
pmikolajczyk41 Aug 2, 2023
d2abb06
CHANGELOG.md
pmikolajczyk41 Aug 4, 2023
316bf58
Merge remote-tracking branch 'origin/master' into pmikolajczyk41/e2e-…
pmikolajczyk41 Aug 4, 2023
37f8d09
Merge remote-tracking branch 'origin/master' into pmikolajczyk41/e2e-…
pmikolajczyk41 Aug 7, 2023
f679f73
Merge remote-tracking branch 'origin/master' into pmikolajczyk41/e2e-…
pmikolajczyk41 Aug 16, 2023
f8e295a
Add drink dependency, fix with wasm-instrument
pmikolajczyk41 Aug 17, 2023
7be1f3a
ChainApi
pmikolajczyk41 Aug 18, 2023
42484ff
Instantiate
pmikolajczyk41 Aug 18, 2023
06cbc9b
Calling
pmikolajczyk41 Aug 18, 2023
c454db3
Upload
pmikolajczyk41 Aug 18, 2023
b21fc27
Use all arguments
pmikolajczyk41 Aug 18, 2023
ec3936e
Build client in macro
pmikolajczyk41 Aug 18, 2023
17bf359
fmt, implement e2e backend
pmikolajczyk41 Aug 18, 2023
dcc746d
remove actor types
pmikolajczyk41 Aug 18, 2023
624d20f
convert accounts and hashes
pmikolajczyk41 Aug 22, 2023
2b67dc8
get rid of session
pmikolajczyk41 Aug 23, 2023
7f9b764
add dedicated example
pmikolajczyk41 Aug 23, 2023
158dd64
example working
pmikolajczyk41 Aug 23, 2023
97ac62b
clean a bit
pmikolajczyk41 Aug 23, 2023
20a36ca
Merge remote-tracking branch 'origin/master' into pmikolajczyk41/e2e-…
pmikolajczyk41 Aug 23, 2023
96f647f
merging cleanup
pmikolajczyk41 Aug 23, 2023
15aaa48
use published drink
pmikolajczyk41 Aug 23, 2023
f416edc
Review
pmikolajczyk41 Aug 24, 2023
04bb5e9
Add DRink! as valid word
pmikolajczyk41 Aug 24, 2023
ea738c2
Give it up
pmikolajczyk41 Aug 24, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Make E2E testcases generic over `E2EBackend` trait - [#1867](https://github.com/paritytech/ink/pull/1867)
- Modify static buffer size via environmental variables - [#1869](https://github.com/paritytech/ink/pull/1869)
- Persist static buffer size in metadata - [#1880](https://github.com/paritytech/ink/pull/1880)
- Add backend choice to the E2E testcase configuration ‒ [#1864](https://github.com/paritytech/ink/pull/1864)

### Added
- Schema generation - [#1765](https://github.com/paritytech/ink/pull/1765)
Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ cargo_metadata = { version = "0.17.0" }
cfg-if = { version = "1.0" }
contract-build = { version = "3.2.0" }
derive_more = { version = "0.99.17", default-features = false }
drink = { version = "0.1.2" }
either = { version = "1.5", default-features = false }
funty = { version = "2.0.0" }
heck = { version = "0.4.0" }
Expand Down Expand Up @@ -72,6 +73,7 @@ tokio = { version = "1.18.2" }
tracing = { version = "0.1.37" }
tracing-subscriber = { version = "0.3.17" }
trybuild = { version = "1.0.60" }
wasm-instrument = { version = "0.4.0", features = ["sign_ext"] }
which = { version = "4.4.0" }
xxhash-rust = { version = "0.8" }
const_env = { version = "0.1"}
Expand Down
2 changes: 2 additions & 0 deletions crates/e2e/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ ink_primitives = { workspace = true, default-features = true }

cargo_metadata = { workspace = true }
contract-build = { workspace = true }
drink = { workspace = true }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider having a feature flag for this, since it is bringing in a few heavy substrate dependencies and (at the moment) is not the default backend.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair point, I'll hide it tomorrow

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it that heavy?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

~70 new dependencies, some of which are substrate crates (for state-machines, externalities, few pallets etc)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

funty = { workspace = true }
impl-serde = { workspace = true }
jsonrpsee = { workspace = true, features = ["ws-client"] }
Expand All @@ -33,6 +34,7 @@ tracing-subscriber = { workspace = true, features = ["env-filter"] }
scale = { package = "parity-scale-codec", workspace = true }
subxt = { workspace = true }
subxt-signer = { workspace = true, features = ["subxt", "sr25519"] }
wasm-instrument = { workspace = true }

# Substrate
pallet-contracts-primitives = { workspace = true }
Expand Down
91 changes: 54 additions & 37 deletions crates/e2e/macro/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::ir;
use crate::{
config::Backend,
ir,
};
use derive_more::From;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;

const DEFAULT_CONTRACTS_NODE: &str = "substrate-contracts-node";

/// Generates code for the `[ink::e2e_test]` macro.
#[derive(From)]
pub struct InkE2ETest {
Expand Down Expand Up @@ -61,23 +66,10 @@ impl InkE2ETest {
}
};

const DEFAULT_CONTRACTS_NODE: &str = "substrate-contracts-node";

// use the user supplied `CONTRACTS_NODE` or default to `substrate-contracts-node`
let contracts_node: &'static str =
option_env!("CONTRACTS_NODE").unwrap_or(DEFAULT_CONTRACTS_NODE);

// check the specified contracts node.
if which::which(contracts_node).is_err() {
if contracts_node == DEFAULT_CONTRACTS_NODE {
panic!(
"The '{DEFAULT_CONTRACTS_NODE}' executable was not found. Install '{DEFAULT_CONTRACTS_NODE}' on the PATH, \
or specify the `CONTRACTS_NODE` environment variable.",
)
} else {
panic!("The contracts node executable '{contracts_node}' was not found.")
}
}
let client_building = match self.test.config.backend() {
Backend::Full => build_full_client(&environment, exec_build_contracts),
Backend::RuntimeOnly => build_runtime_client(exec_build_contracts),
};

quote! {
#( #attrs )*
Expand All @@ -97,24 +89,7 @@ impl InkE2ETest {
log_info("creating new client");

let run = async {
// spawn a contracts node process just for this test
let node_proc = ::ink_e2e::TestNodeProcess::<::ink_e2e::PolkadotConfig>
::build(#contracts_node)
.spawn()
.await
.unwrap_or_else(|err|
::core::panic!("Error spawning substrate-contracts-node: {:?}", err)
);

let contracts = #exec_build_contracts;

let mut client = ::ink_e2e::Client::<
::ink_e2e::PolkadotConfig,
#environment
>::new(
node_proc.client(),
contracts,
).await;
#client_building

let __ret = {
#block
Expand All @@ -126,10 +101,52 @@ impl InkE2ETest {
return ::ink_e2e::tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap_or_else(|err| panic!("Failed building the Runtime: {}", err))
.unwrap_or_else(|err| panic!("Failed building the Runtime: {err}"))
.block_on(run);
}
}
}
}
}

fn build_full_client(environment: &syn::Path, contracts: TokenStream2) -> TokenStream2 {
// Use the user supplied `CONTRACTS_NODE` or default to `DEFAULT_CONTRACTS_NODE`.
let contracts_node: &'static str =
option_env!("CONTRACTS_NODE").unwrap_or(DEFAULT_CONTRACTS_NODE);

// Check the specified contracts node.
if which::which(contracts_node).is_err() {
if contracts_node == DEFAULT_CONTRACTS_NODE {
panic!(
"The '{DEFAULT_CONTRACTS_NODE}' executable was not found. Install '{DEFAULT_CONTRACTS_NODE}' on the PATH, \
or specify the `CONTRACTS_NODE` environment variable.",
)
} else {
panic!("The contracts node executable '{contracts_node}' was not found.")
}
}

quote! {
// Spawn a contracts node process just for this test.
let node_proc = ::ink_e2e::TestNodeProcess::<::ink_e2e::PolkadotConfig>
::build(#contracts_node)
.spawn()
.await
.unwrap_or_else(|err|
::core::panic!("Error spawning substrate-contracts-node: {err:?}")
);

let contracts = #contracts;
let mut client = ::ink_e2e::Client::<
::ink_e2e::PolkadotConfig,
#environment
>::new(node_proc.client(), contracts).await;
}
}

fn build_runtime_client(contracts: TokenStream2) -> TokenStream2 {
quote! {
let contracts = #contracts;
let mut client = ::ink_e2e::DrinkClient::new(contracts);
}
}
89 changes: 89 additions & 0 deletions crates/e2e/macro/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,38 @@ use ink_ir::{
utils::duplicate_config_err,
};

/// The type of the architecture that should be used to run test.
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
pub enum Backend {
/// The standard approach with running dedicated single-node blockchain in a
/// background process.
#[default]
Full,
/// The lightweight approach skipping node layer.
SkymanOne marked this conversation as resolved.
Show resolved Hide resolved
///
/// This runs a runtime emulator within `TestExternalities` (using drink! library) in
/// the same process as the test.
RuntimeOnly,
}

impl TryFrom<syn::LitStr> for Backend {
type Error = syn::Error;

fn try_from(value: syn::LitStr) -> Result<Self, Self::Error> {
match value.value().as_str() {
"full" => Ok(Self::Full),
"runtime_only" | "runtime-only" => Ok(Self::RuntimeOnly),
_ => {
Err(format_err_spanned!(
value,
"unknown backend `{}` for ink! E2E test configuration argument",
value.value()
))
}
}
}
}

/// The End-to-End test configuration.
#[derive(Debug, Default, PartialEq, Eq)]
pub struct E2EConfig {
Expand All @@ -30,6 +62,8 @@ pub struct E2EConfig {
/// [`DefaultEnvironment`](https://docs.rs/ink_env/4.1.0/ink_env/enum.DefaultEnvironment.html)
/// will be used.
environment: Option<syn::Path>,
/// The type of the architecture that should be used to run test.
backend: Backend,
}

impl TryFrom<ast::AttributeArgs> for E2EConfig {
Expand All @@ -38,6 +72,7 @@ impl TryFrom<ast::AttributeArgs> for E2EConfig {
fn try_from(args: ast::AttributeArgs) -> Result<Self, Self::Error> {
let mut additional_contracts: Option<(syn::LitStr, ast::MetaNameValue)> = None;
let mut environment: Option<(syn::Path, ast::MetaNameValue)> = None;
let mut backend: Option<(syn::LitStr, ast::MetaNameValue)> = None;

for arg in args.into_iter() {
if arg.name.is_ident("additional_contracts") {
Expand Down Expand Up @@ -69,6 +104,18 @@ impl TryFrom<ast::AttributeArgs> for E2EConfig {
"expected a path for `environment` ink! E2E test configuration argument",
));
}
} else if arg.name.is_ident("backend") {
if let Some((_, ast)) = backend {
return Err(duplicate_config_err(ast, arg, "backend", "E2E test"))
}
if let ast::MetaValue::Lit(syn::Lit::Str(lit_str)) = &arg.value {
backend = Some((lit_str.clone(), arg))
} else {
return Err(format_err_spanned!(
arg,
"expected a string literal for `backend` ink! E2E test configuration argument",
));
}
} else {
return Err(format_err_spanned!(
arg,
Expand All @@ -80,10 +127,15 @@ impl TryFrom<ast::AttributeArgs> for E2EConfig {
.map(|(value, _)| value.value().split(' ').map(String::from).collect())
.unwrap_or_else(Vec::new);
let environment = environment.map(|(path, _)| path);
let backend = backend
.map(|(b, _)| Backend::try_from(b))
.transpose()?
.unwrap_or_default();

Ok(E2EConfig {
additional_contracts,
environment,
backend,
})
}
}
Expand All @@ -99,6 +151,11 @@ impl E2EConfig {
pub fn environment(&self) -> Option<syn::Path> {
self.environment.clone()
}

/// The type of the architecture that should be used to run test.
pub fn backend(&self) -> Backend {
self.backend
}
}

#[cfg(test)]
Expand Down Expand Up @@ -180,19 +237,51 @@ mod tests {
);
}

#[test]
fn backend_must_be_literal() {
assert_try_from(
syn::parse_quote! { backend = full },
Err("expected a string literal for `backend` ink! E2E test configuration argument"),
);
}

#[test]
fn duplicate_backend_fails() {
assert_try_from(
syn::parse_quote! {
backend = "full",
backend = "runtime-only",
},
Err("encountered duplicate ink! E2E test `backend` configuration argument"),
);
}

#[test]
fn specifying_backend_works() {
assert_try_from(
syn::parse_quote! { backend = "runtime-only" },
Ok(E2EConfig {
backend: Backend::RuntimeOnly,
..Default::default()
}),
);
}

#[test]
fn full_config_works() {
assert_try_from(
syn::parse_quote! {
additional_contracts = "adder/Cargo.toml flipper/Cargo.toml",
environment = crate::CustomEnvironment,
backend = "full",
},
Ok(E2EConfig {
additional_contracts: vec![
"adder/Cargo.toml".into(),
"flipper/Cargo.toml".into(),
],
environment: Some(syn::parse_quote! { crate::CustomEnvironment }),
backend: Backend::Full,
}),
);
}
Expand Down
Loading