Skip to content

Commit

Permalink
refactoring(gtest): Introduce gear_commons mailbox to gtest (#4010)
Browse files Browse the repository at this point in the history
  • Loading branch information
techraed authored Jul 16, 2024
1 parent fac54c1 commit 5cfb148
Show file tree
Hide file tree
Showing 14 changed files with 600 additions and 486 deletions.
41 changes: 40 additions & 1 deletion common/src/auxiliary/mailbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@

use crate::{
auxiliary::DoubleBTreeMap,
storage::{DoubleMapStorage, Interval, MailboxError, MailboxImpl, MailboxKeyGen},
storage::{
CountedByKey, DoubleMapStorage, GetSecondPos, Interval, IterableByKeyMap, IteratorWrap,
MailboxError, MailboxImpl, MailboxKeyGen,
},
};
use alloc::collections::btree_map::IntoIter;
use core::cell::RefCell;
use gear_core::{
ids::{MessageId, ProgramId},
Expand Down Expand Up @@ -97,6 +101,41 @@ impl DoubleMapStorage for MailboxStorageWrap {
}
}

impl CountedByKey for MailboxStorageWrap {
type Key = ProgramId;
type Length = usize;

fn len(key: &Self::Key) -> Self::Length {
MAILBOX_STORAGE.with_borrow(|map| map.count_key(key))
}
}

impl IterableByKeyMap<(MailboxedMessage, Interval<BlockNumber>)> for MailboxStorageWrap {
type Key = ProgramId;

type DrainIter = IteratorWrap<
IntoIter<MessageId, (MailboxedMessage, Interval<BlockNumber>)>,
(MailboxedMessage, Interval<BlockNumber>),
GetSecondPos,
>;

type Iter = IteratorWrap<
IntoIter<MessageId, (MailboxedMessage, Interval<BlockNumber>)>,
(MailboxedMessage, Interval<BlockNumber>),
GetSecondPos,
>;

fn drain_key(key: Self::Key) -> Self::DrainIter {
MAILBOX_STORAGE
.with_borrow_mut(|map| map.drain_key(&key))
.into()
}

fn iter_key(key: Self::Key) -> Self::Iter {
MAILBOX_STORAGE.with_borrow(|map| map.iter_key(&key)).into()
}
}

/// An implementor of the error returned from calling `Mailbox` trait functions.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MailboxErrorImpl {
Expand Down
38 changes: 37 additions & 1 deletion common/src/auxiliary/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
pub mod gas_provider;
pub mod mailbox;

use alloc::collections::btree_map::{BTreeMap, Entry};
use alloc::collections::btree_map::{BTreeMap, Entry, IntoIter};

/// Double key `BTreeMap`.
///
Expand Down Expand Up @@ -52,6 +52,16 @@ impl<K1, K2, V> DoubleBTreeMap<K1, K2, V> {
.unwrap_or_default()
}

pub fn count_key(&self, key1: &K1) -> usize
where
K1: Ord,
{
self.inner
.get(key1)
.map(|key2_map| key2_map.len())
.unwrap_or_default()
}

/// Returns a reference to the value corresponding to the keys.
pub fn get(&self, key1: &K1, key2: &K2) -> Option<&V>
where
Expand Down Expand Up @@ -95,6 +105,32 @@ impl<K1, K2, V> DoubleBTreeMap<K1, K2, V> {
}
}

// Iterator related impl
impl<K1, K2, V> DoubleBTreeMap<K1, K2, V> {
pub fn iter_key(&self, key1: &K1) -> IntoIter<K2, V>
where
K1: Ord,
K2: Clone,
V: Clone,
{
self.inner
.get(key1)
.cloned()
.map(|key2_map| key2_map.into_iter())
.unwrap_or_default()
}

pub fn drain_key(&mut self, key1: &K1) -> IntoIter<K2, V>
where
K1: Ord,
{
self.inner
.remove(key1)
.map(|key2_map| key2_map.into_iter())
.unwrap_or_default()
}
}

impl<K1, K2, V> Default for DoubleBTreeMap<K1, K2, V> {
fn default() -> Self {
Self::new()
Expand Down
7 changes: 3 additions & 4 deletions common/src/gas_provider/property_tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,15 @@
//!
//! 14. Value catch can be performed only on consumed nodes (not tested).

use super::{auxiliary::gas_provider::*, *};
use crate::storage::MapStorage;
use alloc::collections::BTreeSet;
use super::*;
use crate::{auxiliary::gas_provider::*, storage::MapStorage};
use core::iter::FromIterator;
use enum_iterator::all;
use frame_support::{assert_err, assert_ok};
use gear_utils::{NonEmpty, RingGet};
use primitive_types::H256;
use proptest::prelude::*;
use std::collections::HashMap;
use std::collections::{BTreeSet, HashMap};
use strategies::GasTreeAction;

mod assertions;
Expand Down
2 changes: 1 addition & 1 deletion examples/piggy-bank/src/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ extern "C" fn handle() {

if msg.expect("Failed to load payload bytes") == b"smash" {
debug!("smashing, total: {available_value}");
msg::reply_bytes(b"send", available_value).unwrap();
msg::send(msg::source(), b"send", available_value).unwrap();
}
});
}
93 changes: 75 additions & 18 deletions gtest/src/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,42 +21,53 @@
use core_processor::configs::BlockInfo;
use std::{
cell::RefCell,
rc::Rc,
time::{SystemTime, UNIX_EPOCH},
};

use crate::BLOCK_DURATION_IN_MSECS;

type BlockInfoStorageInner = Rc<RefCell<Option<BlockInfo>>>;

thread_local! {
/// Definition of the storage value storing block info (timestamp and height).
static BLOCK_INFO_STORAGE: RefCell<Option<BlockInfo>> = const { RefCell::new(None) };
static BLOCK_INFO_STORAGE: BlockInfoStorageInner = Rc::new(RefCell::new(None));
}

/// Block info storage manager.
#[derive(Debug, Default)]
pub(crate) struct BlocksManager(());
#[derive(Debug)]
pub(crate) struct BlocksManager {
_unused: BlockInfoStorageInner,
}

impl BlocksManager {
/// Create block info storage manager with a further initialization of the
/// storage.
pub(crate) fn new() -> Self {
BLOCK_INFO_STORAGE.with_borrow_mut(|block_info| {
let info = BlockInfo {
height: 0,
timestamp: now(),
};

block_info.replace(info);
let unused = BLOCK_INFO_STORAGE.with(|bi_rc| {
let mut ref_mut = bi_rc.borrow_mut();
if ref_mut.is_none() {
let info = BlockInfo {
height: 0,
timestamp: now(),
};

*ref_mut = Some(info);
}

Rc::clone(bi_rc)
});

Self(())
Self { _unused: unused }
}

/// Get current block info.
pub(crate) fn get(&self) -> BlockInfo {
BLOCK_INFO_STORAGE.with_borrow(|cell| {
cell.as_ref()
BLOCK_INFO_STORAGE.with(|bi_rc| {
bi_rc
.borrow()
.as_ref()
.copied()
.expect("must be initialized in a `BlocksManager::new`")
.expect("instance always initialized")
})
}

Expand All @@ -67,9 +78,10 @@ impl BlocksManager {

/// Adjusts blocks info by moving blocks by `amount`.
pub(crate) fn move_blocks_by(&self, amount: u32) -> BlockInfo {
BLOCK_INFO_STORAGE.with_borrow_mut(|block_info| {
let Some(block_info) = block_info.as_mut() else {
panic!("must initialized in a `BlocksManager::new`");
BLOCK_INFO_STORAGE.with(|bi_rc| {
let mut bi_ref_mut = bi_rc.borrow_mut();
let Some(block_info) = bi_ref_mut.as_mut() else {
panic!("instance always initialized");
};
block_info.height += amount;
let duration = BLOCK_DURATION_IN_MSECS.saturating_mul(amount as u64);
Expand All @@ -80,9 +92,54 @@ impl BlocksManager {
}
}

impl Default for BlocksManager {
fn default() -> Self {
Self::new()
}
}

impl Drop for BlocksManager {
fn drop(&mut self) {
BLOCK_INFO_STORAGE.with(|bi_rc| {
if Rc::strong_count(bi_rc) == 2 {
*bi_rc.borrow_mut() = None;
}
});
}
}

fn now() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_millis() as u64
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_data_nullified_on_drop() {
let first_instance = BlocksManager::new();
let second_instance = BlocksManager::new();

first_instance.next_block();
first_instance.next_block();

// Assert all instance use same data;
assert_eq!(second_instance.get().height, 2);
BLOCK_INFO_STORAGE.with(|bi_rc| bi_rc.borrow().is_some());

// Drop first instance and check whether data is removed.
drop(first_instance);
assert_eq!(second_instance.get().height, 2);

second_instance.next_block();
assert_eq!(second_instance.get().height, 3);
BLOCK_INFO_STORAGE.with(|bi_rc| bi_rc.borrow().is_some());

drop(second_instance);
BLOCK_INFO_STORAGE.with(|bi_rc| bi_rc.borrow().is_none());
}
}
2 changes: 1 addition & 1 deletion gtest/src/gas_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub(crate) type PositiveImbalance = <GasTree as Tree>::PositiveImbalance;
pub(crate) type NegativeImbalance = <GasTree as Tree>::NegativeImbalance;
type GasTree = <AuxiliaryGasProvider as Provider>::GasTree;

/// Gas tree manager which uses operates under the hood over
/// Gas tree manager which operates under the hood over
/// [`gear_common::AuxiliaryGasProvider`].
///
/// Manager is needed mainly to adapt arguments of the gas tree methods to the
Expand Down
1 change: 1 addition & 0 deletions gtest/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,7 @@ mod system;
pub use crate::log::{CoreLog, Log, RunResult};
pub use codec;
pub use error::{Result, TestError};
pub use mailbox::ActorMailbox;
pub use program::{
calculate_program_id, gbuild::ensure_gbuild, Gas, Program, ProgramBuilder, ProgramIdWrapper,
WasmProgram,
Expand Down
36 changes: 23 additions & 13 deletions gtest/src/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::program::{Gas, ProgramIdWrapper};
use codec::{Codec, Encode};
use gear_core::{
ids::{MessageId, ProgramId},
message::{Payload, StoredMessage},
message::{Payload, StoredMessage, UserStoredMessage},
};
use gear_core_errors::{ErrorReplyReason, ReplyCode, SimpleExecutionError, SuccessReplyReason};
use std::{collections::BTreeMap, convert::TryInto, fmt::Debug};
Expand Down Expand Up @@ -151,11 +151,11 @@ impl<T: Codec + Debug> DecodedCoreLog<T> {
/// ```
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct Log {
source: Option<ProgramId>,
destination: Option<ProgramId>,
payload: Option<Payload>,
reply_code: Option<ReplyCode>,
reply_to: Option<MessageId>,
pub(crate) source: Option<ProgramId>,
pub(crate) destination: Option<ProgramId>,
pub(crate) payload: Option<Payload>,
pub(crate) reply_code: Option<ReplyCode>,
pub(crate) reply_to: Option<MessageId>,
}

impl<ID, T> From<(ID, T)> for Log
Expand Down Expand Up @@ -283,22 +283,32 @@ impl Log {
}
}

impl PartialEq<StoredMessage> for Log {
fn eq(&self, other: &StoredMessage) -> bool {
if matches!(other.reply_details(), Some(reply) if Some(reply.to_reply_code()) != self.reply_code)
{
return false;
}
impl PartialEq<UserStoredMessage> for Log {
fn eq(&self, other: &UserStoredMessage) -> bool {
// Any log field is set.
let has_any = self.source.is_some()
|| self.destination.is_some()
|| self.payload.is_some()
|| self.reply_to.is_some();

// If any of log field doesn't match, then there's no equality.
if matches!(self.source, Some(source) if source != other.source()) {
return false;
}

if matches!(self.destination, Some(dest) if dest != other.destination()) {
return false;
}

if matches!(&self.payload, Some(payload) if payload.inner() != other.payload_bytes()) {
return false;
}
true

if matches!(self.reply_to, Some(reply_to) if reply_to != other.id()) {
return false;
}

has_any
}
}

Expand Down
Loading

0 comments on commit 5cfb148

Please sign in to comment.