Skip to content
Merged
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
11 changes: 11 additions & 0 deletions src/tests/proptest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ pub fn arb_hash256() -> impl Strategy<Value = Hash256> {
proptest::array::uniform32(any::<u8>()).prop_map(Hash256::from)
}

/// Strategy for generating initial values for a progressive list.
/// Unlike `arb_list`, this has no length limit, but we cap it at a reasonable
/// size for testing purposes.
pub fn arb_progressive_list<T, S>(strategy: S, max_len: usize) -> impl Strategy<Value = Vec<T>>
where
S: Strategy<Value = T>,
T: std::fmt::Debug,
{
proptest::collection::vec(strategy, 0..=max_len)
}

/// Struct with multiple fields shared by multiple proptests.
#[derive(Debug, Clone, PartialEq, Encode, Decode, TreeHash)]
pub struct Large {
Expand Down
136 changes: 134 additions & 2 deletions src/tests/proptest/operations.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::{Large, arb_hash256, arb_index, arb_large, arb_list, arb_vect};
use crate::{Error, List, Value, Vector};
use super::{Large, arb_hash256, arb_index, arb_large, arb_list, arb_progressive_list, arb_vect};
use crate::{Error, List, ProgressiveList, Value, Vector};
use proptest::prelude::*;
use ssz::{Decode, Encode};
use std::fmt::Debug;
Expand Down Expand Up @@ -91,6 +91,30 @@ impl<T: Value, N: Unsigned> Spec<T, N> {
}
}

/// Simple specification for `ProgressiveList` behaviour without a length limit.
#[derive(Debug, Clone)]
pub struct ProgressiveSpec<T> {
values: Vec<T>,
}

impl<T: Value> ProgressiveSpec<T> {
pub fn new(values: Vec<T>) -> Self {
Self { values }
}

pub fn len(&self) -> usize {
self.values.len()
}

pub fn iter(&self) -> impl Iterator<Item = &T> {
self.values.iter()
}

pub fn push(&mut self, value: T) {
self.values.push(value);
}
}

#[derive(Debug, Clone)]
pub enum Op<T> {
/// Check that `len` returns the correct length.
Expand Down Expand Up @@ -173,6 +197,80 @@ where
proptest::collection::vec(arb_op(strategy, n), 1..limit)
}

/// Strategy for generating operations for ProgressiveList.
/// Only includes operations that are currently implemented for ProgressiveList.
fn arb_op_progressive<'a, T, S>(strategy: &'a S) -> impl Strategy<Value = Op<T>> + 'a
where
T: Debug + Clone + 'a,
S: Strategy<Value = T> + 'a,
{
// Only include operations that are currently implemented for ProgressiveList:
// Len, Push, Iter, TreeHash, and Debase (SSZ roundtrip)
prop_oneof![
Just(Op::Len),
strategy.prop_map(Op::Push),
Just(Op::Iter),
Just(Op::TreeHash),
Just(Op::Debase),
]
}

fn arb_ops_progressive<'a, T, S>(
strategy: &'a S,
limit: usize,
) -> impl Strategy<Value = Vec<Op<T>>> + 'a
where
T: Debug + Clone + 'a,
S: Strategy<Value = T> + 'a,
{
proptest::collection::vec(arb_op_progressive(strategy), 1..limit)
}

fn apply_ops_progressive_list<T>(
list: &mut ProgressiveList<T>,
spec: &mut ProgressiveSpec<T>,
ops: Vec<Op<T>>,
) where
T: Value + Debug + Send + Sync,
{
for op in ops {
match op {
Op::Len => {
assert_eq!(list.len(), spec.len());
}
Op::Push(value) => {
list.push(value.clone()).expect("push should succeed");
spec.push(value);
}
Op::Iter => {
assert!(list.iter().eq(spec.iter()));
}
Op::TreeHash => {
list.tree_hash_root();
}
Op::Debase => {
let ssz_bytes = list.as_ssz_bytes();
let new_list = ProgressiveList::from_ssz_bytes(&ssz_bytes).expect("SSZ decode");
assert_eq!(new_list, *list);
*list = new_list;
}
// These operations are not implemented for ProgressiveList yet and
// are not generated by arb_op_progressive, but we handle them for completeness.
Op::Get(_)
| Op::Set(_, _)
| Op::SetCowWithIntoMut(_, _)
| Op::SetCowWithMakeMut(_, _)
| Op::IterFrom(_)
| Op::PopFront(_)
| Op::ApplyUpdates
| Op::Checkpoint
| Op::Rebase
| Op::FromIntoRoundtrip
| Op::IntraRebase => {}
}
}
}

fn apply_ops_list<T, N>(list: &mut List<T, N>, spec: &mut Spec<T, N>, ops: Vec<Op<T>>)
where
T: Value + Debug + Send + Sync,
Expand Down Expand Up @@ -492,3 +590,37 @@ mod vect {
vect_test!(large_33, Large, U33, arb_large());
vect_test!(large_1024, Large, U1024, arb_large());
}

/// Maximum initial length for progressive list tests.
/// This is used to cap the initial list size for reasonable test execution time.
/// Compare to list tests which can use up to 1024 elements (U1024).
const PROGRESSIVE_LIST_MAX_LEN: usize = 128;

macro_rules! progressive_list_test {
($name:ident, $T:ty) => {
// Use default strategy (assumes existence of an `Arbitrary` impl).
progressive_list_test!($name, $T, any::<$T>());
};
($name:ident, $T:ty, $strat:expr) => {
proptest! {
#[test]
fn $name(
init in arb_progressive_list::<$T, _>(&$strat, PROGRESSIVE_LIST_MAX_LEN),
ops in arb_ops_progressive::<$T, _>(&$strat, OP_LIMIT)
) {
let mut list = ProgressiveList::<$T>::try_from_iter(init.clone()).unwrap();
let mut spec = ProgressiveSpec::<$T>::new(init);
apply_ops_progressive_list(&mut list, &mut spec, ops);
}
}
};
}

mod progressive_list {
use super::*;

progressive_list_test!(u8, u8);
progressive_list_test!(u64, u64);
progressive_list_test!(hash256, Hash256, arb_hash256());
progressive_list_test!(large, Large, arb_large());
}
Loading