From 807472a1f98dac94f291b41b84d6f8a4fbd80584 Mon Sep 17 00:00:00 2001 From: mertwole <33563701+mertwole@users.noreply.github.com> Date: Mon, 4 Sep 2023 16:43:45 +0300 Subject: [PATCH] refactor(gtest): introduce `state_args` macro for `read_state_using_wasm` --- Cargo.lock | 1 + examples/new-meta/tests/read_state.rs | 109 +++++++++++++++++++++-- gtest/Cargo.toml | 1 + gtest/src/lib.rs | 1 + gtest/src/manager.rs | 5 +- gtest/src/program.rs | 121 +++++++++++++++++++++++++- 6 files changed, 225 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1bc26f8c3f7..ca31e2e52ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4731,6 +4731,7 @@ dependencies = [ "colored", "demo-custom", "demo-futures-unordered", + "demo-meta-io", "demo-piggy-bank", "demo-ping", "derive_more", diff --git a/examples/new-meta/tests/read_state.rs b/examples/new-meta/tests/read_state.rs index 4e86296214c..d285ac014f8 100644 --- a/examples/new-meta/tests/read_state.rs +++ b/examples/new-meta/tests/read_state.rs @@ -2,7 +2,7 @@ use demo_new_meta::{ MessageInitIn, Person, Wallet, META_EXPORTS_V1, META_EXPORTS_V2, META_WASM_V1, META_WASM_V2, }; use gstd::Encode; -use gtest::{Program, System}; +use gtest::{state_args, state_args_encoded, Program, System}; #[test] fn read_state_bytes_returns_full_state() { @@ -22,11 +22,11 @@ fn read_state_bytes_returns_full_state() { fn read_state_bytes_with_wasm_func_returns_transformed_state() { let system = System::new(); let program = initialize_current_program(&system); - const FIRST_WALLET_FUNC_NAME: &str = "first_wallet"; - assert!(META_EXPORTS_V1.contains(&FIRST_WALLET_FUNC_NAME)); + const FUNC_NAME: &str = "first_wallet"; + assert!(META_EXPORTS_V1.contains(&FUNC_NAME)); let actual_state = program - .read_state_bytes_using_wasm(FIRST_WALLET_FUNC_NAME, META_WASM_V1.to_vec(), None) + .read_state_bytes_using_wasm(FUNC_NAME, META_WASM_V1.to_vec(), state_args_encoded!()) .expect("Unable to read program state"); let expected_state = Wallet::test_sequence().first().encode(); @@ -38,8 +38,8 @@ fn read_state_bytes_with_wasm_func_returns_transformed_state() { fn read_state_bytes_with_parameterized_wasm_func_returns_transformed_state() { let system = System::new(); let program = initialize_current_program(&system); - const WALLET_BY_PERSON_FUNC_NAME: &str = "wallet_by_person"; - assert!(META_EXPORTS_V2.contains(&WALLET_BY_PERSON_FUNC_NAME)); + const FUNC_NAME: &str = "wallet_by_person"; + assert!(META_EXPORTS_V2.contains(&FUNC_NAME)); let other_person = Person { surname: "OtherSurname".into(), name: "OtherName".into(), @@ -47,9 +47,9 @@ fn read_state_bytes_with_parameterized_wasm_func_returns_transformed_state() { let actual_state = program .read_state_bytes_using_wasm( - WALLET_BY_PERSON_FUNC_NAME, + FUNC_NAME, META_WASM_V2.to_vec(), - Some(other_person.encode()), + state_args_encoded!(&other_person), ) .expect("Unable to read program state"); @@ -61,6 +61,32 @@ fn read_state_bytes_with_parameterized_wasm_func_returns_transformed_state() { assert_eq!(expected_state, actual_state); } +#[test] +fn read_state_bytes_with_two_args_wasm_func_returns_transformed_state() { + let system = System::new(); + let program = initialize_current_program(&system); + const FUNC_NAME: &str = "wallet_by_name_and_surname"; + assert!(META_EXPORTS_V2.contains(&FUNC_NAME)); + + let name = "OtherName".to_string(); + let surname = "OtherSurname".to_string(); + + let actual_state = program + .read_state_bytes_using_wasm( + FUNC_NAME, + META_WASM_V2.to_vec(), + state_args_encoded!(name.clone(), surname.clone()), + ) + .expect("Unable to read program state"); + + let expected_state = Wallet::test_sequence() + .into_iter() + .find(|wallet| wallet.person.name == name && wallet.person.surname == surname) + .encode(); + + assert_eq!(expected_state, actual_state); +} + #[test] fn read_state_returns_full_state() { let system = System::new(); @@ -73,6 +99,73 @@ fn read_state_returns_full_state() { assert_eq!(actual_state, expected_state); } +#[test] +fn read_state_with_wasm_func_returns_transformed_state() { + let system = System::new(); + let program = initialize_current_program(&system); + const FUNC_NAME: &str = "first_wallet"; + assert!(META_EXPORTS_V1.contains(&FUNC_NAME)); + + let actual_state = program + .read_state_using_wasm(FUNC_NAME, META_WASM_V1.to_vec(), state_args!()) + .expect("Unable to read program state"); + + let expected_state = Wallet::test_sequence().first().cloned(); + + assert_eq!(expected_state, actual_state); +} + +#[test] +fn read_state_with_parameterized_wasm_func_returns_transformed_state() { + let system = System::new(); + let program = initialize_current_program(&system); + const FUNC_NAME: &str = "wallet_by_person"; + assert!(META_EXPORTS_V2.contains(&FUNC_NAME)); + let other_person = Person { + surname: "OtherSurname".into(), + name: "OtherName".into(), + }; + + let actual_state = program + .read_state_using_wasm( + FUNC_NAME, + META_WASM_V2.to_vec(), + state_args!(other_person.clone()), + ) + .expect("Unable to read program state"); + + let expected_state = Wallet::test_sequence() + .into_iter() + .find(|wallet| wallet.person == other_person); + + assert_eq!(expected_state, actual_state); +} + +#[test] +fn read_state_with_two_args_wasm_func_returns_transformed_state() { + let system = System::new(); + let program = initialize_current_program(&system); + const FUNC_NAME: &str = "wallet_by_name_and_surname"; + assert!(META_EXPORTS_V2.contains(&FUNC_NAME)); + + let name = "OtherName".to_string(); + let surname = "OtherSurname".to_string(); + + let actual_state = program + .read_state_using_wasm( + FUNC_NAME, + META_WASM_V2.to_vec(), + state_args!(name.clone(), surname.clone()), + ) + .expect("Unable to read program state"); + + let expected_state = Wallet::test_sequence() + .into_iter() + .find(|wallet| wallet.person.name == name && wallet.person.surname == surname); + + assert_eq!(expected_state, actual_state); +} + fn initialize_current_program(system: &System) -> Program { const SOME_USER_ID: u64 = 3; let program = Program::current(system); diff --git a/gtest/Cargo.toml b/gtest/Cargo.toml index efc813abc27..55886642194 100644 --- a/gtest/Cargo.toml +++ b/gtest/Cargo.toml @@ -30,3 +30,4 @@ demo-custom.workspace = true demo-piggy-bank.workspace = true demo-ping.workspace = true demo-futures-unordered.workspace = true +demo-meta-io.workspace = true diff --git a/gtest/src/lib.rs b/gtest/src/lib.rs index d1432ef14e2..0fe769f8b12 100644 --- a/gtest/src/lib.rs +++ b/gtest/src/lib.rs @@ -24,6 +24,7 @@ mod program; mod system; pub use crate::log::{CoreLog, Log, RunResult}; +pub use codec; pub use error::{Result, TestError}; pub use program::{calculate_program_id, Gas, Program, WasmProgram}; pub use system::System; diff --git a/gtest/src/manager.rs b/gtest/src/manager.rs index 36e71353afb..ca70cd4b075 100644 --- a/gtest/src/manager.rs +++ b/gtest/src/manager.rs @@ -471,7 +471,7 @@ impl ExtManager { program_id: &ProgramId, fn_name: &str, wasm: Vec, - argument: Option>, + args: Option>, ) -> Result> { let mapping_code = Code::new_raw(wasm, 1, None, true, false).map_err(|_| TestError::Instrumentation)?; @@ -480,8 +480,7 @@ impl ExtManager { .into_parts() .0; - // The `metawasm` macro knows how to decode this as a tuple - let mut mapping_code_payload = argument.unwrap_or_default(); + let mut mapping_code_payload = args.unwrap_or_default(); mapping_code_payload.append(&mut self.read_state_bytes(program_id)?); core_processor::informational::execute_for_reply::, _>( diff --git a/gtest/src/program.rs b/gtest/src/program.rs index a30d5497b7f..cc0c4e5b426 100644 --- a/gtest/src/program.rs +++ b/gtest/src/program.rs @@ -173,6 +173,38 @@ impl From<&str> for ProgramIdWrapper { } } +#[macro_export] +macro_rules! state_args { + () => { + Option::<()>::None + }; + ($single:expr) => { + Some($single) + }; + ($($multiple:expr),*) => { + Some(($($multiple,)*)) + }; +} + +#[macro_export] +macro_rules! state_args_encoded { + () => { + Option::>::None + }; + ($single:expr) => { + { + use $crate::codec::Encode; + Some(($single).encode()) + } + }; + ($($multiple:expr),*) => { + { + use $crate::codec::Encode; + Some((($($multiple,)*)).encode()) + } + }; +} + pub struct Program<'a> { pub(crate) manager: &'a RefCell, pub(crate) id: ProgramId, @@ -421,15 +453,58 @@ impl<'a> Program<'a> { /// Reads the program’s transformed state as a byte vector. The transformed /// state is a result of applying the `fn_name` function from the `wasm` /// binary with the optional `argument`. + /// + /// # Usage + /// You can pass arguments as `Option<(arg1, arg2, ...).encode()>` or by + /// using [`state_args_encoded`] macro. + /// + /// # Examples + /// + /// ``` + /// # use gtest::{state_args_encoded, Program, System, WasmProgram, Result}; + /// # use codec::Encode; + /// # fn doctest() -> Result<()> { + /// # #[derive(Debug)] + /// # struct MockWasm {} + /// # + /// # impl WasmProgram for MockWasm { + /// # fn init(&mut self, _payload: Vec) -> Result>, &'static str> { unimplemented!() } + /// # fn handle(&mut self, _payload: Vec) -> Result>, &'static str> { unimplemented!() } + /// # fn handle_reply(&mut self, _payload: Vec) -> Result<(), &'static str> {unimplemented!() } + /// # fn handle_signal(&mut self, _payload: Vec) -> Result<(), &'static str> { unimplemented!() } + /// # fn state(&mut self) -> Result, &'static str> { unimplemented!() } + /// # } + /// # let system = System::new(); + /// # let program = Program::mock(&system, MockWasm { }); + /// # let ARG_1 = 0u8; + /// # let ARG_2 = 0u8; + /// //Read state bytes with no arguments passed to wasm. + /// # let WASM = vec![]; + /// let _ = program.read_state_bytes_using_wasm("fn_name", WASM, Option::>::None)?; + /// # let WASM = vec![]; + /// let _ = program.read_state_bytes_using_wasm("fn_name", WASM, state_args_encoded!())?; + /// // Read state bytes with one argument passed to wasm. + /// # let WASM = vec![]; + /// let _ = program.read_state_bytes_using_wasm("fn_name", WASM, Some(ARG_1.encode()))?; + /// # let WASM = vec![]; + /// let _ = program.read_state_bytes_using_wasm("fn_name", WASM, state_args_encoded!(ARG_1))?; + /// // Read state bytes with multiple arguments passed to wasm. + /// # let WASM = vec![]; + /// let _ = program.read_state_bytes_using_wasm("fn_name", WASM, Some((ARG_1, ARG_2).encode()))?; + /// # let WASM = vec![]; + /// let _ = program.read_state_bytes_using_wasm("fn_name", WASM, state_args_encoded!(ARG_1, ARG_2))?; + /// # Ok(()) + /// # } + /// ``` pub fn read_state_bytes_using_wasm( &self, fn_name: &str, wasm: Vec, - argument: Option>, + args: Option>, ) -> Result> { self.manager .borrow_mut() - .read_state_bytes_using_wasm(&self.id, fn_name, wasm, argument) + .read_state_bytes_using_wasm(&self.id, fn_name, wasm, args) } /// Reads and decodes the program's state . @@ -441,6 +516,48 @@ impl<'a> Program<'a> { /// Reads and decodes the program’s transformed state. The transformed state /// is a result of applying the `fn_name` function from the `wasm` /// binary with the optional `argument`. + /// + /// # Usage + /// You can pass arguments as `Option<(arg1, arg2, ...)>` or by + /// using [`state_args`] macro. + /// + /// # Examples + /// + /// ``` + /// # use gtest::{state_args, Program, System, WasmProgram, Result}; + /// # fn doctest() -> Result<()> { + /// # #[derive(Debug)] + /// # struct MockWasm {} + /// # + /// # impl WasmProgram for MockWasm { + /// # fn init(&mut self, _payload: Vec) -> Result>, &'static str> { unimplemented!() } + /// # fn handle(&mut self, _payload: Vec) -> Result>, &'static str> { unimplemented!() } + /// # fn handle_reply(&mut self, _payload: Vec) -> Result<(), &'static str> {unimplemented!() } + /// # fn handle_signal(&mut self, _payload: Vec) -> Result<(), &'static str> { unimplemented!() } + /// # fn state(&mut self) -> Result, &'static str> { unimplemented!() } + /// # } + /// # let system = System::new(); + /// # let program = Program::mock(&system, MockWasm { }); + /// # let ARG_1 = 0u8; + /// # let ARG_2 = 0u8; + /// //Read state bytes with no arguments passed to wasm. + /// # let WASM = vec![]; + /// let _ = program.read_state_using_wasm("fn_name", WASM, Option::<()>::None)?; + /// # let WASM = vec![]; + /// let _ = program.read_state_using_wasm("fn_name", WASM, state_args!())?; + /// // Read state bytes with one argument passed to wasm. + /// # let WASM = vec![]; + /// let _ = program.read_state_using_wasm("fn_name", WASM, Some(ARG_1))?; + /// # let WASM = vec![]; + /// let _ = program.read_state_using_wasm("fn_name", WASM, state_args!(ARG_1))?; + /// // Read state bytes with multiple arguments passed to wasm. + /// # let WASM = vec![]; + /// let _ = program.read_state_using_wasm("fn_name", WASM, Some((ARG_1, ARG_2)))?; + /// # let WASM = vec![]; + /// let _ = program.read_state_using_wasm("fn_name", WASM, state_args!(ARG_1, ARG_2))?; + /// # Ok(()) + /// # } + /// ``` pub fn read_state_using_wasm( &self, fn_name: &str,