From a89802f25b77381cd3e97de5d3d91c4d121faf48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 22 Jan 2024 18:25:35 +0100 Subject: [PATCH] `drink::test` creates a session object (#94) --- Cargo.lock | 52 +++++++++++++++++---- Cargo.toml | 7 +-- drink/test-macro/Cargo.toml | 1 + drink/test-macro/src/lib.rs | 47 ++++++++++++++++--- examples/chain-extension/src/lib.rs | 6 +-- examples/contract-events/lib.rs | 8 +--- examples/cross-contract-call-tracing/lib.rs | 8 +--- examples/flipper/lib.rs | 13 ++---- examples/mocking/lib.rs | 5 +- examples/quick-start-with-drink/lib.rs | 23 ++++----- 10 files changed, 112 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7a864da..8dcc4ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -981,8 +981,18 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.14.4", + "darling_macro 0.14.4", +] + +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core 0.20.3", + "darling_macro 0.20.3", ] [[package]] @@ -999,17 +1009,42 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.32", +] + [[package]] name = "darling_macro" version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ - "darling_core", + "darling_core 0.14.4", "quote", "syn 1.0.109", ] +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core 0.20.3", + "quote", + "syn 2.0.32", +] + [[package]] name = "der" version = "0.7.6" @@ -1107,7 +1142,7 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "drink" -version = "0.8.5" +version = "0.8.6" dependencies = [ "contract-metadata", "contract-transcode", @@ -1132,7 +1167,7 @@ dependencies = [ [[package]] name = "drink-cli" -version = "0.8.5" +version = "0.8.6" dependencies = [ "anyhow", "clap", @@ -1148,12 +1183,13 @@ dependencies = [ [[package]] name = "drink-test-macro" -version = "0.8.5" +version = "0.8.6" dependencies = [ "cargo_metadata", "contract-build", "contract-metadata", "convert_case", + "darling 0.20.3", "proc-macro2", "quote", "syn 2.0.32", @@ -3339,7 +3375,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27873eb6005868f8cc72dcfe109fae664cf51223d35387bc2f28be4c28d94c47" dependencies = [ - "darling", + "darling 0.14.4", "proc-macro-crate 1.3.1", "proc-macro2", "quote", @@ -3365,7 +3401,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "995491f110efdc6bea96d6a746140e32bfceb4ea47510750a5467295a4707a25" dependencies = [ - "darling", + "darling 0.14.4", "proc-macro-crate 1.3.1", "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 714200c..4ceee4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ homepage = "https://github.com/Cardinal-Cryptography/drink" license = "Apache-2.0" readme = "README.md" repository = "https://github.com/Cardinal-Cryptography/drink" -version = "0.8.5" +version = "0.8.6" [workspace.dependencies] anyhow = { version = "1.0.71" } @@ -29,6 +29,7 @@ contract-metadata = { version = "4.0.0-rc.1" } contract-transcode = { version = "4.0.0-rc.1" } convert_case = { version = "0.6.0" } crossterm = { version = "0.26.0" } +darling = { version = "0.20.3" } parity-scale-codec = { version = "3.4" } parity-scale-codec-derive = { version = "3.4" } proc-macro2 = { version = "1" } @@ -56,5 +57,5 @@ sp-runtime-interface = { version = "19.0.0" } # Local dependencies -drink = { version = "0.8.5", path = "drink" } -drink-test-macro = { version = "0.8.5", path = "drink/test-macro" } +drink = { version = "0.8.6", path = "drink" } +drink-test-macro = { version = "0.8.6", path = "drink/test-macro" } diff --git a/drink/test-macro/Cargo.toml b/drink/test-macro/Cargo.toml index fb3bdcd..f308883 100644 --- a/drink/test-macro/Cargo.toml +++ b/drink/test-macro/Cargo.toml @@ -17,6 +17,7 @@ cargo_metadata = { workspace = true } contract-build = { workspace = true } contract-metadata = { workspace = true } convert_case = { workspace = true } +darling = { workspace = true } proc-macro2 = { workspace = true } syn = { workspace = true, features = ["full"] } quote = { workspace = true } diff --git a/drink/test-macro/src/lib.rs b/drink/test-macro/src/lib.rs index 5be65f7..b5b2b99 100644 --- a/drink/test-macro/src/lib.rs +++ b/drink/test-macro/src/lib.rs @@ -5,6 +5,7 @@ mod bundle_provision; mod contract_building; +use darling::{ast::NestedMeta, FromMeta}; use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::quote; @@ -18,6 +19,7 @@ type SynResult = Result; /// /// # Requirements /// +/// - Your crate must have `drink` in its dependencies (and it shouldn't be renamed). /// - You mustn't import `drink::test` in the scope, where the macro is used. In other words, you /// should always use the macro only with a qualified path `#[drink::test]`. /// - Your crate cannot be part of a cargo workspace. @@ -34,14 +36,22 @@ type SynResult = Result; /// /// Note: Depending on a non-local contract is not tested yet. /// +/// # Creating a session object +/// +/// The macro will also create a new mutable session object and pass it to the decorated function by value. You can +/// configure which runtime should be used (by specifying a path to a type implementing +/// `drink::runtime::RuntimeWithContracts` trait. Thus, your testcase function should accept a single argument: +/// `mut session: Session<_>`. +/// +/// By default, the macro will use `drink::runtime::MinimalRuntime`. +/// /// # Example /// /// ```rust, ignore /// #[drink::test] -/// fn testcase() { -/// Session::::new() -/// .unwrap() -/// .deploy(bytes(), "new", NO_ARGS, NO_SALT, NO_ENDOWMENT, &transcoder()) +/// fn testcase(mut session: Session) { +/// session +/// .deploy_bundle(&get_bundle(), "new", NO_ARGS, NO_SALT, NO_ENDOWMENT) /// .unwrap(); /// } /// ``` @@ -53,14 +63,39 @@ pub fn test(attr: TokenStream, item: TokenStream) -> TokenStream { } } +#[derive(FromMeta)] +struct TestAttributes { + runtime: Option, +} + /// Auxiliary function to enter ?-based error propagation. -fn test_internal(_attr: TokenStream2, item: TokenStream2) -> SynResult { +fn test_internal(attr: TokenStream2, item: TokenStream2) -> SynResult { let item_fn = syn::parse2::(item)?; + let macro_args = TestAttributes::from_list(&NestedMeta::parse_meta_list(attr)?)?; + build_contracts(); + let fn_vis = item_fn.vis; + let fn_attrs = item_fn.attrs; + let fn_block = item_fn.block; + let fn_name = item_fn.sig.ident; + let fn_async = item_fn.sig.asyncness; + let fn_generics = item_fn.sig.generics; + let fn_output = item_fn.sig.output; + let fn_const = item_fn.sig.constness; + let fn_unsafety = item_fn.sig.unsafety; + + let runtime = macro_args + .runtime + .unwrap_or(syn::parse2(quote! { ::drink::runtime::MinimalRuntime })?); + Ok(quote! { #[test] - #item_fn + #(#fn_attrs)* + #fn_vis #fn_async #fn_const #fn_unsafety fn #fn_name #fn_generics () #fn_output { + let mut session = Session::<#runtime>::new().expect("Failed to create a session"); + #fn_block + } }) } diff --git a/examples/chain-extension/src/lib.rs b/examples/chain-extension/src/lib.rs index 77c181b..9670318 100644 --- a/examples/chain-extension/src/lib.rs +++ b/examples/chain-extension/src/lib.rs @@ -51,9 +51,9 @@ mod tests { ); /// Test that we can call chain extension from ink! contract and get a correct result. - #[drink::test] - fn we_can_test_chain_extension() -> Result<(), Box> { - let result: u32 = Session::::new()? + #[drink::test(runtime = RuntimeWithCE)] + fn we_can_test_chain_extension(mut session: Session) -> Result<(), Box> { + let result: u32 = session .deploy_bundle_and( BundleProvider::local()?, "new", diff --git a/examples/contract-events/lib.rs b/examples/contract-events/lib.rs index 432249e..b68736e 100644 --- a/examples/contract-events/lib.rs +++ b/examples/contract-events/lib.rs @@ -37,20 +37,16 @@ mod flipper { mod tests { use std::error::Error; - use drink::{ - runtime::MinimalRuntime, - session::{Session, NO_ARGS, NO_ENDOWMENT}, - }; + use drink::session::{Session, NO_ARGS, NO_ENDOWMENT}; #[drink::contract_bundle_provider] enum BundleProvider {} #[drink::test] - fn we_can_inspect_emitted_events() -> Result<(), Box> { + fn we_can_inspect_emitted_events(mut session: Session) -> Result<(), Box> { let bundle = BundleProvider::local()?; // Firstly, we deploy the contract and call its `flip` method. - let mut session = Session::::new()?; session.deploy_bundle(bundle.clone(), "new", &["false"], vec![], NO_ENDOWMENT)?; session.call("flip", NO_ARGS, NO_ENDOWMENT)??; diff --git a/examples/cross-contract-call-tracing/lib.rs b/examples/cross-contract-call-tracing/lib.rs index 6ea7ae7..0620326 100644 --- a/examples/cross-contract-call-tracing/lib.rs +++ b/examples/cross-contract-call-tracing/lib.rs @@ -65,10 +65,7 @@ mod tests { use std::{cell::RefCell, error::Error}; use drink::{ - runtime::{ - pallet_contracts_debugging::{TracingExt, TracingExtT}, - MinimalRuntime, - }, + runtime::pallet_contracts_debugging::{TracingExt, TracingExtT}, session::{contract_transcode::Value, Session, NO_ARGS, NO_ENDOWMENT}, AccountId32, }; @@ -131,8 +128,7 @@ mod tests { } #[drink::test] - fn test() -> Result<(), Box> { - let mut session = Session::::new()?; + fn test(mut session: Session) -> Result<(), Box> { session.set_tracing_extension(TracingExt(Box::new(TestDebugger {}))); let outer_address = session.deploy_bundle( diff --git a/examples/flipper/lib.rs b/examples/flipper/lib.rs index 31683fd..75b51f6 100755 --- a/examples/flipper/lib.rs +++ b/examples/flipper/lib.rs @@ -34,18 +34,15 @@ mod flipper { mod tests { use std::error::Error; - use drink::{ - runtime::MinimalRuntime, - session::{Session, NO_ARGS, NO_ENDOWMENT, NO_SALT}, - }; + use drink::session::{Session, NO_ARGS, NO_ENDOWMENT, NO_SALT}; #[drink::contract_bundle_provider] enum BundleProvider {} #[drink::test] - fn initialization() -> Result<(), Box> { + fn initialization(mut session: Session) -> Result<(), Box> { let contract = BundleProvider::local()?; - let init_value: bool = Session::::new()? + let init_value: bool = session .deploy_bundle_and(contract, "new", &["true"], NO_SALT, NO_ENDOWMENT)? .call_and("get", NO_ARGS, NO_ENDOWMENT)? .record() @@ -58,9 +55,9 @@ mod tests { } #[drink::test] - fn flipping() -> Result<(), Box> { + fn flipping(mut session: Session) -> Result<(), Box> { let contract = BundleProvider::Flipper.bundle()?; - let init_value: bool = Session::::new()? + let init_value: bool = session .deploy_bundle_and(contract, "new", &["true"], NO_SALT, NO_ENDOWMENT)? .call_and("flip", NO_ARGS, NO_ENDOWMENT)? .call_and("flip", NO_ARGS, NO_ENDOWMENT)? diff --git a/examples/mocking/lib.rs b/examples/mocking/lib.rs index c7f261d..8425c52 100755 --- a/examples/mocking/lib.rs +++ b/examples/mocking/lib.rs @@ -41,7 +41,6 @@ mod tests { use drink::{ mock_message, - runtime::MinimalRuntime, session::{mocking_api::MockingApi, Session, NO_ARGS, NO_ENDOWMENT, NO_SALT}, ContractMock, }; @@ -52,9 +51,7 @@ mod tests { enum BundleProvider {} #[drink::test] - fn call_mocked_message() -> Result<(), Box> { - let mut session = Session::::new()?; - + fn call_mocked_message(mut session: Session) -> Result<(), Box> { // Firstly, we create the mocked contract. const RETURN_VALUE: (u8, u8) = (4, 1); let mocked_contract = diff --git a/examples/quick-start-with-drink/lib.rs b/examples/quick-start-with-drink/lib.rs index fca4eb4..b15e6b8 100644 --- a/examples/quick-start-with-drink/lib.rs +++ b/examples/quick-start-with-drink/lib.rs @@ -44,7 +44,6 @@ mod flipper { mod tests { use drink::{ contract_api::decode_debug_buffer, - runtime::MinimalRuntime, session::{Session, NO_ARGS, NO_ENDOWMENT, NO_SALT}, }; @@ -70,15 +69,12 @@ mod tests { /// have to run `cargo contract build` manually for every contract dependency. /// /// For convenience of using `?` operator, we mark the test function as returning a `Result`. + /// + /// `drink::test` will already provide us with a `Session` object. It is a wrapper around a runtime and it exposes + /// a broad API for interacting with it. Session is generic over the runtime type, but usually and by default, we + /// use `MinimalRuntime`, which is a minimalistic runtime that allows using smart contracts. #[drink::test] - fn deploy_and_call_a_contract() -> Result<(), Box> { - // Firstly, we create a `Session` object. It is a wrapper around a runtime and it exposes a - // broad API for interacting with it. - // - // It is generic over the runtime type, but usually, it is sufficient to use - // `MinimalRuntime`, which is a minimalistic runtime that allows using smart contracts. - let mut session = Session::::new()?; - + fn deploy_and_call_a_contract(mut session: Session) -> Result<(), Box> { // Now we get the contract bundle from the `BundleProvider` enum. Since the current crate // comes with a contract, we can use the `local` method to get the bundle for it. let contract_bundle = BundleProvider::local()?; @@ -122,9 +118,7 @@ mod tests { /// In this testcase we will see how to get and read debug logs from the contract. #[drink::test] - fn get_debug_logs() -> Result<(), Box> { - // We create a session object as usual and deploy the contract bundle. - let mut session = Session::::new()?; + fn get_debug_logs(mut session: Session) -> Result<(), Box> { session.deploy_bundle( BundleProvider::local()?, "new", @@ -150,8 +144,9 @@ mod tests { /// In this testcase we will see how to work with multiple contracts. #[drink::test] - fn work_with_multiple_contracts() -> Result<(), Box> { - let mut session = Session::::new()?; + fn work_with_multiple_contracts( + mut session: Session, + ) -> Result<(), Box> { let bundle = BundleProvider::local()?; // We can deploy the same contract multiple times. However, we have to ensure that the