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

feat: add event checks to motsu #13

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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 Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 19 additions & 37 deletions crates/motsu-proc/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, FnArg};

/// Defines a unit test that provides access to Stylus execution context.
/// Defines a unit test that provides access to Stylus' execution context.
///
/// For more information see [`crate::test`].
pub(crate) fn test(_attr: &TokenStream, input: TokenStream) -> TokenStream {
Expand All @@ -21,49 +21,31 @@ pub(crate) fn test(_attr: &TokenStream, input: TokenStream) -> TokenStream {
}

// Whether 1 or none contracts will be declared.
let arg_binding_and_ty = match fn_args
.into_iter()
.map(|arg| {
let FnArg::Typed(arg) = arg else {
error!(@arg, "unexpected receiver argument in test signature");
};
let contract_arg_binding = &arg.pat;
let contract_ty = &arg.ty;
Ok((contract_arg_binding, contract_ty))
})
.collect::<Result<Vec<_>, _>>()
{
Ok(res) => res,
Err(err) => return err.to_compile_error().into(),
};
let contract_declarations = fn_args.into_iter().map(|arg| {
let FnArg::Typed(arg) = arg else {
error!(arg, "unexpected receiver argument in test signature");
};
let contract_arg_binding = &arg.pat;
let contract_ty = &arg.ty;

let contract_arg_defs =
arg_binding_and_ty.iter().map(|(arg_binding, contract_ty)| {
// Test case assumes, that contract's variable has `&mut` reference
// to contract's type.
quote! {
#arg_binding: &mut #contract_ty
}
});

let contract_args =
arg_binding_and_ty.iter().map(|(_arg_binding, contract_ty)| {
// Pass mutable reference to the contract.
quote! {
&mut <#contract_ty>::default()
}
});
// Test case assumes, that contract's variable has `&mut` reference
// to contract's type.
quote! {
let mut #contract_arg_binding = <#contract_ty>::default();
let #contract_arg_binding = &mut #contract_arg_binding;
}
});

// Declare test case closure.
// Pass mut ref to the test closure and call it.
// Reset storage for the test context and return test's output.
// Output full testcase function.
// Declare contract.
// And in the end, reset storage for test context.
quote! {
#( #attrs )*
#[test]
fn #fn_name() #fn_return_type {
use ::motsu::prelude::DefaultStorage;
let test = | #( #contract_arg_defs ),* | #fn_block;
let res = test( #( #contract_args ),* );
#( #contract_declarations )*
let res = #fn_block;
::motsu::prelude::Context::current().reset_storage();
res
}
Expand Down
1 change: 1 addition & 0 deletions crates/motsu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ repository.workspace = true
version = "0.3.0"

[dependencies]
alloy-sol-types.workspace = true
const-hex.workspace = true
once_cell.workspace = true
tiny-keccak.workspace = true
Expand Down
109 changes: 0 additions & 109 deletions crates/motsu/src/context.rs

This file was deleted.

83 changes: 83 additions & 0 deletions crates/motsu/src/context/environment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//! Module with unit test EVM environment for Stylus contracts.

/// Block Timestamp - Epoch timestamp: 1st January 2025 `00::00::00`.
const BLOCK_TIMESTAMP: u64 = 1_735_689_600;
/// Arbitrum's CHAID ID.
const CHAIN_ID: u64 = 42161;

/// Dummy contract address set for tests.
const CONTRACT_ADDRESS: &[u8; 42] =
b"0xdCE82b5f92C98F27F116F70491a487EFFDb6a2a9";

/// Externally Owned Account (EOA) code hash.
const EOA_CODEHASH: &[u8; 66] =
b"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470";

/// Dummy msg sender set for tests.
const MSG_SENDER: &[u8; 42] = b"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF";

pub(crate) struct Environment {
account_codehash: [u8; 66],
block_timestamp: u64,
chain_id: u64,
contract_address: [u8; 42],
events: Vec<Vec<u8>>,
msg_sender: [u8; 42],
}

impl Default for Environment {
/// Creates default environment for a test case.
fn default() -> Environment {
Self {
account_codehash: *EOA_CODEHASH,
block_timestamp: BLOCK_TIMESTAMP,
chain_id: CHAIN_ID,
contract_address: *CONTRACT_ADDRESS,
events: Vec::new(),
msg_sender: *MSG_SENDER,
}
}
}

impl Environment {
/// Gets the code hash of the account at the given address.
pub(crate) fn account_codehash(&self) -> [u8; 66] {
self.account_codehash
}

/// Gets a bounded estimate of the Unix timestamp at which the Sequencer
/// sequenced the transaction.
pub(crate) fn block_timestamp(&self) -> u64 {
self.block_timestamp
}

/// Gets the chain ID of the current chain.
pub(crate) fn chain_id(&self) -> u64 {
self.chain_id
}

/// Gets the address of the current program.
pub(crate) fn contract_address(&self) -> [u8; 42] {
self.contract_address
}

/// Gets the address of the account that called the program.
pub(crate) fn msg_sender(&self) -> [u8; 42] {
self.msg_sender
}

/// Stores emitted event.
pub(crate) fn store_event(&mut self, event: &[u8]) {
self.events.push(Vec::from(event));
}

/// Removes all the stored events.
pub(crate) fn clear_events(&mut self) {
self.events.clear();
}

/// Gets all emitted events.
pub(crate) fn events(&self) -> Vec<Vec<u8>> {
self.events.clone()
}
}
Loading
Loading