#![allow(clippy::ptr_arg)] // Usage of `&Vec<_>` is needed for type inference // cspell:ignore oneof //! Property testing for list index as a rust collection. use modifier::Modifier; use proptest::{ collection::vec, num, prop_assert, prop_oneof, proptest, strategy, strategy::Strategy, test_runner::TestCaseResult, }; use std::rc::Rc; use matterdb::{access::AccessExt, BinaryValue, Fork, ListIndex, TemporaryDB}; mod common; use crate::common::{compare_collections, AsForkAction, ForkAction, FromFork, ACTIONS_MAX_LEN}; #[derive(Debug, Clone)] enum ListAction { Push(V), Pop, Extend(Vec), // Applied with argument modulo `collection.len()`. Truncate(u64), // Applied to index modulo `collection.len()`. Set(u64, V), Clear, MergeFork, } impl AsForkAction for ListAction { fn as_fork_action(&self) -> Option { match self { ListAction::MergeFork => Some(ForkAction::Merge), _ => None, } } } impl Modifier> for ListAction { fn modify(self, list: &mut Vec) { match self { ListAction::Push(val) => { list.push(val); } ListAction::Pop => { list.pop(); } ListAction::Extend(vec) => { list.extend(vec); } ListAction::Truncate(size) => { let len = list.len(); if len > 0 { list.truncate(size as usize % len); } } ListAction::Set(idx, val) => { let len = list.len(); if len > 0 { list[idx as usize % len] = val; } } ListAction::Clear => { list.clear(); } _ => unreachable!(), } } } impl Modifier, V>> for ListAction { fn modify(self, list: &mut ListIndex, V>) { match self { ListAction::Push(val) => { list.push(val); } ListAction::Pop => { list.pop(); } ListAction::Extend(vec) => { list.extend(vec); } ListAction::Truncate(size) => { let len = list.len(); if len > 0 { list.truncate(size % len); } } ListAction::Set(idx, val) => { let len = list.len(); if len > 0 { list.set(idx % len, val); } } ListAction::Clear => { list.clear(); } _ => unreachable!(), } } } impl FromFork for ListIndex, V> { fn from_fork(fork: Rc) -> Self { fork.get_list("test") } fn clear(&mut self) { self.clear(); } } fn generate_action() -> impl Strategy> { prop_oneof![ num::i32::ANY.prop_map(ListAction::Push), strategy::Just(ListAction::Pop), vec(num::i32::ANY, 1..5).prop_map(ListAction::Extend), num::u64::ANY.prop_map(ListAction::Truncate), (num::u64::ANY, num::i32::ANY).prop_map(|(i, v)| ListAction::Set(i, v)), strategy::Just(ListAction::Clear), strategy::Just(ListAction::MergeFork), ] } fn compare_list(list: &ListIndex, i32>, ref_list: &Vec) -> TestCaseResult { prop_assert!(ref_list.iter().copied().eq(list)); Ok(()) } #[test] fn compare_list_to_vec() { let db = TemporaryDB::new(); proptest!(|(ref actions in vec(generate_action(), 1..ACTIONS_MAX_LEN))| { compare_collections(&db, actions, compare_list)?; }); }