From f3e9984e5877978ebe3cf9be204f6030a59bfc28 Mon Sep 17 00:00:00 2001 From: brentstone Date: Wed, 14 Sep 2022 20:07:58 +0200 Subject: [PATCH] WIP: StateMachine tests for lazy_vec validation --- shared/Cargo.toml | 1 + .../storage_api/collections/lazy_vec.rs | 68 ++++++++++++++++--- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 89b20e59bc8..fe5f4b4eb00 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -109,6 +109,7 @@ pretty_assertions = "0.7.2" proptest = {git = "https://github.com/heliaxdev/proptest", branch = "tomas/sm"} test-log = {version = "0.2.7", default-features = false, features = ["trace"]} tracing-subscriber = {version = "0.3.7", default-features = false, features = ["env-filter", "fmt"]} +namada_tests = {path = "../tests/" } [build-dependencies] tonic-build = "0.6.0" diff --git a/shared/src/ledger/storage_api/collections/lazy_vec.rs b/shared/src/ledger/storage_api/collections/lazy_vec.rs index 79967cb9c36..4be7921508d 100644 --- a/shared/src/ledger/storage_api/collections/lazy_vec.rs +++ b/shared/src/ledger/storage_api/collections/lazy_vec.rs @@ -520,6 +520,7 @@ mod test { use super::*; use crate::ledger::storage::testing::TestStorage; + use namada_tests::tx::{tx_host_env, TestTxEnv}; #[test] fn test_lazy_vec_basics() -> storage_api::Result<()> { @@ -602,17 +603,23 @@ mod test { // `fn apply_transition_on_eager_vec`) eager_vec: Vec, lazy_vec: LazyVec, - storage: TestStorage, } #[derive(Clone, Debug)] - struct AbstractLazyVecState(Vec); + struct AbstractLazyVecState { + /// Valid LazyVec changes in the current transaction + valid_transitions: Vec>, + /// Valid LazyVec changes committed to storage + committed_transitions: Vec>, + } /// Possible transitions that can modify a [`LazyVec`]. This roughly /// corresponds to the methods that have `StorageWrite` access and is very /// similar to [`Action`] #[derive(Clone, Debug)] pub enum Transition { + /// Commit all valid transition in the current transaction + CommitTx, /// Push a value `T` into a [`LazyVec`] Push(T), /// Pop a value from a [`LazyVec`] @@ -625,6 +632,7 @@ mod test { /// value to update the element to value: T, }, + } impl AbstractStateMachine for AbstractLazyVecState { @@ -632,19 +640,27 @@ mod test { type Transition = Transition; fn init_state() -> BoxedStrategy { - Just(Self(vec![])).boxed() + Just(Self { + valid_transitions: vec![], + committed_transitions: vec![], + }) + .boxed() } + // + // Apply a random transition to the state fn transitions(state: &Self::State) -> BoxedStrategy { - if state.0.is_empty() { - prop_oneof![arb_test_vec_item().prop_map(Transition::Push)] + let length = state.len(); + if length == 0 { + prop_oneof![Just(Transition::CommitTx), arb_test_vec_item().prop_map(Transition::Push)] .boxed() } else { let indices: Vec = - (0_usize..state.0.len()).map(|ix| ix as Index).collect(); + (0..length).collect(); let arb_index = proptest::sample::select(indices); prop_oneof![ + Just(Transition::CommitTx), Just(Transition::Pop), arb_test_vec_item().prop_map(Transition::Push), (arb_index, arb_test_vec_item()).prop_map( @@ -659,7 +675,13 @@ mod test { mut state: Self::State, transition: &Self::Transition, ) -> Self::State { - apply_transition_on_eager_vec(&mut state.0, transition); + match transition { + Transition::CommitTx => { + let valid_actions_to_commit = std::mem::take(&mut state.valid_transitions); + state.committed_transitions.extend(valid_actions_to_commit.into_iter()); + }, + _ => state.valid_transitions.push(transition.clone()) + } state } @@ -667,7 +689,8 @@ mod test { state: &Self::State, transition: &Self::Transition, ) -> bool { - if state.0.is_empty() { + let length = state.len(); + if length == 0 { // Ensure that the pop or update transitions are not applied to // an empty state !matches!( @@ -676,7 +699,7 @@ mod test { ) } else if let Transition::Update { index, .. } = transition { // Ensure that the update index is a valid one - *index < (state.0.len() - 1) as Index + *index < (length - 1) } else { true } @@ -690,12 +713,12 @@ mod test { fn init_test( _initial_state: ::State, ) -> Self::ConcreteState { + tx_host_env::init(); Self { eager_vec: vec![], lazy_vec: LazyVec::open( storage::Key::parse("key_path/arbitrary").unwrap(), ), - storage: TestStorage::default(), } } @@ -705,8 +728,12 @@ mod test { ) -> Self::ConcreteState { // Transition application on lazy vec and post-conditions: match dbg!(&transition) { + Transition::CommitTx => { + // commit the tx without committing the block + tx_host_env::with(|env| env.write_log.commit_tx()); + } Transition::Push(value) => { - let old_len = state.lazy_vec.len(&state.storage).unwrap(); + let old_len = state.lazy_vec.len(&tx_host_env::Ctx::new()).unwrap(); state .lazy_vec @@ -811,6 +838,24 @@ mod test { } } + /// impl + impl AbstractLazyVecState { + fn len(&self) -> u64 { + let all_transitions = [self.committed_transitions.clone(), self.valid_transitions.clone()].concat(); + let mut push_count = 0; + let mut pop_count = 0; + + for trans in all_transitions { + match trans { + Transition::CommitTx | Transition::Update{..} => {}, + Transition::Push(_) => push_count += 1, + Transition::Pop => pop_count += 1, + } + } + push_count - pop_count + } + } + /// Generate an arbitrary `TestVecItem` fn arb_test_vec_item() -> impl Strategy { (any::(), any::()).prop_map(|(x, y)| TestVecItem { x, y }) @@ -822,6 +867,7 @@ mod test { transition: &Transition, ) { match transition { + Transition::CommitTx => {} Transition::Push(value) => vec.push(value.clone()), Transition::Pop => { let _popped = vec.pop();