Skip to content

Commit 64b80f8

Browse files
Add ProgressiveList to proptest suite
Co-authored-by: michaelsproul <4452260+michaelsproul@users.noreply.github.com>
1 parent 73a1249 commit 64b80f8

File tree

2 files changed

+185
-2
lines changed

2 files changed

+185
-2
lines changed

src/tests/proptest/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ pub fn arb_hash256() -> impl Strategy<Value = Hash256> {
3535
proptest::array::uniform32(any::<u8>()).prop_map(Hash256::from)
3636
}
3737

38+
/// Strategy for generating initial values for a progressive list.
39+
/// Unlike `arb_list`, this has no length limit, but we cap it at a reasonable
40+
/// size for testing purposes.
41+
pub fn arb_progressive_list<T, S>(strategy: S, max_len: usize) -> impl Strategy<Value = Vec<T>>
42+
where
43+
S: Strategy<Value = T>,
44+
T: std::fmt::Debug,
45+
{
46+
proptest::collection::vec(strategy, 0..=max_len)
47+
}
48+
3849
/// Struct with multiple fields shared by multiple proptests.
3950
#[derive(Debug, Clone, PartialEq, Encode, Decode, TreeHash)]
4051
pub struct Large {

src/tests/proptest/operations.rs

Lines changed: 174 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use super::{Large, arb_hash256, arb_index, arb_large, arb_list, arb_vect};
2-
use crate::{Error, List, Value, Vector};
1+
use super::{Large, arb_hash256, arb_index, arb_large, arb_list, arb_progressive_list, arb_vect};
2+
use crate::{Error, List, ProgressiveList, Value, Vector};
33
use proptest::prelude::*;
44
use ssz::{Decode, Encode};
55
use std::fmt::Debug;
@@ -91,6 +91,30 @@ impl<T: Value, N: Unsigned> Spec<T, N> {
9191
}
9292
}
9393

94+
/// Simple specification for `ProgressiveList` behaviour without a length limit.
95+
#[derive(Debug, Clone)]
96+
pub struct ProgressiveSpec<T> {
97+
values: Vec<T>,
98+
}
99+
100+
impl<T: Value> ProgressiveSpec<T> {
101+
pub fn new(values: Vec<T>) -> Self {
102+
Self { values }
103+
}
104+
105+
pub fn len(&self) -> usize {
106+
self.values.len()
107+
}
108+
109+
pub fn iter(&self) -> impl Iterator<Item = &T> {
110+
self.values.iter()
111+
}
112+
113+
pub fn push(&mut self, value: T) {
114+
self.values.push(value);
115+
}
116+
}
117+
94118
#[derive(Debug, Clone)]
95119
pub enum Op<T> {
96120
/// Check that `len` returns the correct length.
@@ -173,6 +197,121 @@ where
173197
proptest::collection::vec(arb_op(strategy, n), 1..limit)
174198
}
175199

200+
/// Strategy for generating operations for ProgressiveList.
201+
/// Since ProgressiveList has no length limit, we use a fixed index range.
202+
fn arb_op_progressive<'a, T, S>(strategy: &'a S, n: usize) -> impl Strategy<Value = Op<T>> + 'a
203+
where
204+
T: Debug + Clone + 'a,
205+
S: Strategy<Value = T> + 'a,
206+
{
207+
// For ProgressiveList, we mainly want to test push, len, iter, and tree_hash
208+
// since get, set, iter_from, pop_front, etc. are not implemented yet.
209+
let a_block = prop_oneof![
210+
Just(Op::Len),
211+
arb_index(n).prop_map(Op::Get),
212+
(arb_index(n), strategy).prop_map(|(index, value)| Op::Set(index, value)),
213+
(arb_index(n), strategy).prop_map(|(index, value)| Op::SetCowWithIntoMut(index, value)),
214+
(arb_index(n), strategy).prop_map(|(index, value)| Op::SetCowWithMakeMut(index, value)),
215+
strategy.prop_map(Op::Push),
216+
Just(Op::Iter),
217+
arb_index(n).prop_map(Op::IterFrom),
218+
arb_index(n).prop_map(Op::PopFront),
219+
Just(Op::ApplyUpdates),
220+
];
221+
let b_block = prop_oneof![
222+
Just(Op::TreeHash),
223+
Just(Op::Checkpoint),
224+
Just(Op::Rebase),
225+
Just(Op::Debase),
226+
Just(Op::FromIntoRoundtrip),
227+
Just(Op::IntraRebase),
228+
];
229+
prop_oneof![
230+
10 => a_block,
231+
6 => b_block
232+
]
233+
}
234+
235+
fn arb_ops_progressive<'a, T, S>(
236+
strategy: &'a S,
237+
n: usize,
238+
limit: usize,
239+
) -> impl Strategy<Value = Vec<Op<T>>> + 'a
240+
where
241+
T: Debug + Clone + 'a,
242+
S: Strategy<Value = T> + 'a,
243+
{
244+
proptest::collection::vec(arb_op_progressive(strategy, n), 1..limit)
245+
}
246+
247+
fn apply_ops_progressive_list<T>(
248+
list: &mut ProgressiveList<T>,
249+
spec: &mut ProgressiveSpec<T>,
250+
ops: Vec<Op<T>>,
251+
) where
252+
T: Value + Debug + Send + Sync,
253+
{
254+
let mut checkpoint = list.clone();
255+
256+
for op in ops {
257+
match op {
258+
Op::Len => {
259+
assert_eq!(list.len(), spec.len());
260+
}
261+
Op::Get(_) => {
262+
// No-op: ProgressiveList doesn't implement get yet
263+
}
264+
Op::Set(_, _) => {
265+
// No-op: ProgressiveList doesn't implement set yet
266+
}
267+
Op::SetCowWithIntoMut(_, _) => {
268+
// No-op: ProgressiveList doesn't implement set yet
269+
}
270+
Op::SetCowWithMakeMut(_, _) => {
271+
// No-op: ProgressiveList doesn't implement set yet
272+
}
273+
Op::Push(value) => {
274+
list.push(value.clone()).expect("push should succeed");
275+
spec.push(value);
276+
}
277+
Op::Iter => {
278+
assert!(list.iter().eq(spec.iter()));
279+
}
280+
Op::IterFrom(_) => {
281+
// No-op: ProgressiveList doesn't implement iter_from yet
282+
}
283+
Op::PopFront(_) => {
284+
// No-op: ProgressiveList doesn't implement pop_front yet
285+
}
286+
Op::ApplyUpdates => {
287+
// No-op: ProgressiveList doesn't have apply_updates
288+
}
289+
Op::Checkpoint => {
290+
checkpoint = list.clone();
291+
}
292+
Op::TreeHash => {
293+
list.tree_hash_root();
294+
}
295+
Op::Rebase => {
296+
// No-op: ProgressiveList doesn't implement rebase yet
297+
let _ = &checkpoint;
298+
}
299+
Op::Debase => {
300+
let ssz_bytes = list.as_ssz_bytes();
301+
let new_list = ProgressiveList::from_ssz_bytes(&ssz_bytes).expect("SSZ decode");
302+
assert_eq!(new_list, *list);
303+
*list = new_list;
304+
}
305+
Op::FromIntoRoundtrip => {
306+
// No-op: No corresponding Vector type for ProgressiveList
307+
}
308+
Op::IntraRebase => {
309+
// No-op: ProgressiveList doesn't implement intra_rebase yet
310+
}
311+
}
312+
}
313+
}
314+
176315
fn apply_ops_list<T, N>(list: &mut List<T, N>, spec: &mut Spec<T, N>, ops: Vec<Op<T>>)
177316
where
178317
T: Value + Debug + Send + Sync,
@@ -492,3 +631,36 @@ mod vect {
492631
vect_test!(large_33, Large, U33, arb_large());
493632
vect_test!(large_1024, Large, U1024, arb_large());
494633
}
634+
635+
/// Maximum length for progressive list tests.
636+
/// We use smaller sizes than the regular list tests since progressive lists can grow unbounded.
637+
const PROGRESSIVE_LIST_MAX_LEN: usize = 128;
638+
639+
macro_rules! progressive_list_test {
640+
($name:ident, $T:ty) => {
641+
// Use default strategy (assumes existence of an `Arbitrary` impl).
642+
progressive_list_test!($name, $T, any::<$T>());
643+
};
644+
($name:ident, $T:ty, $strat:expr) => {
645+
proptest! {
646+
#[test]
647+
fn $name(
648+
init in arb_progressive_list::<$T, _>(&$strat, PROGRESSIVE_LIST_MAX_LEN),
649+
ops in arb_ops_progressive::<$T, _>(&$strat, PROGRESSIVE_LIST_MAX_LEN, OP_LIMIT)
650+
) {
651+
let mut list = ProgressiveList::<$T>::try_from_iter(init.clone()).unwrap();
652+
let mut spec = ProgressiveSpec::<$T>::new(init);
653+
apply_ops_progressive_list(&mut list, &mut spec, ops);
654+
}
655+
}
656+
};
657+
}
658+
659+
mod progressive_list {
660+
use super::*;
661+
662+
progressive_list_test!(u8, u8);
663+
progressive_list_test!(u64, u64);
664+
progressive_list_test!(hash256, Hash256, arb_hash256());
665+
progressive_list_test!(large, Large, arb_large());
666+
}

0 commit comments

Comments
 (0)