From 73a1249e59f286698abc205cf56eafe65955f096 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Dec 2025 07:06:31 +0000 Subject: [PATCH 1/3] Initial plan From 64b80f84593a23526e4ea020f48e05e0de3fda61 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Dec 2025 07:15:54 +0000 Subject: [PATCH 2/3] Add ProgressiveList to proptest suite Co-authored-by: michaelsproul <4452260+michaelsproul@users.noreply.github.com> --- src/tests/proptest/mod.rs | 11 ++ src/tests/proptest/operations.rs | 176 ++++++++++++++++++++++++++++++- 2 files changed, 185 insertions(+), 2 deletions(-) diff --git a/src/tests/proptest/mod.rs b/src/tests/proptest/mod.rs index eaa9385..46d1116 100644 --- a/src/tests/proptest/mod.rs +++ b/src/tests/proptest/mod.rs @@ -35,6 +35,17 @@ pub fn arb_hash256() -> impl Strategy { proptest::array::uniform32(any::()).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(strategy: S, max_len: usize) -> impl Strategy> +where + S: Strategy, + 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 { diff --git a/src/tests/proptest/operations.rs b/src/tests/proptest/operations.rs index bb3aaa6..d156c1c 100644 --- a/src/tests/proptest/operations.rs +++ b/src/tests/proptest/operations.rs @@ -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; @@ -91,6 +91,30 @@ impl Spec { } } +/// Simple specification for `ProgressiveList` behaviour without a length limit. +#[derive(Debug, Clone)] +pub struct ProgressiveSpec { + values: Vec, +} + +impl ProgressiveSpec { + pub fn new(values: Vec) -> Self { + Self { values } + } + + pub fn len(&self) -> usize { + self.values.len() + } + + pub fn iter(&self) -> impl Iterator { + self.values.iter() + } + + pub fn push(&mut self, value: T) { + self.values.push(value); + } +} + #[derive(Debug, Clone)] pub enum Op { /// Check that `len` returns the correct length. @@ -173,6 +197,121 @@ where proptest::collection::vec(arb_op(strategy, n), 1..limit) } +/// Strategy for generating operations for ProgressiveList. +/// Since ProgressiveList has no length limit, we use a fixed index range. +fn arb_op_progressive<'a, T, S>(strategy: &'a S, n: usize) -> impl Strategy> + 'a +where + T: Debug + Clone + 'a, + S: Strategy + 'a, +{ + // For ProgressiveList, we mainly want to test push, len, iter, and tree_hash + // since get, set, iter_from, pop_front, etc. are not implemented yet. + let a_block = prop_oneof![ + Just(Op::Len), + arb_index(n).prop_map(Op::Get), + (arb_index(n), strategy).prop_map(|(index, value)| Op::Set(index, value)), + (arb_index(n), strategy).prop_map(|(index, value)| Op::SetCowWithIntoMut(index, value)), + (arb_index(n), strategy).prop_map(|(index, value)| Op::SetCowWithMakeMut(index, value)), + strategy.prop_map(Op::Push), + Just(Op::Iter), + arb_index(n).prop_map(Op::IterFrom), + arb_index(n).prop_map(Op::PopFront), + Just(Op::ApplyUpdates), + ]; + let b_block = prop_oneof![ + Just(Op::TreeHash), + Just(Op::Checkpoint), + Just(Op::Rebase), + Just(Op::Debase), + Just(Op::FromIntoRoundtrip), + Just(Op::IntraRebase), + ]; + prop_oneof![ + 10 => a_block, + 6 => b_block + ] +} + +fn arb_ops_progressive<'a, T, S>( + strategy: &'a S, + n: usize, + limit: usize, +) -> impl Strategy>> + 'a +where + T: Debug + Clone + 'a, + S: Strategy + 'a, +{ + proptest::collection::vec(arb_op_progressive(strategy, n), 1..limit) +} + +fn apply_ops_progressive_list( + list: &mut ProgressiveList, + spec: &mut ProgressiveSpec, + ops: Vec>, +) where + T: Value + Debug + Send + Sync, +{ + let mut checkpoint = list.clone(); + + for op in ops { + match op { + Op::Len => { + assert_eq!(list.len(), spec.len()); + } + Op::Get(_) => { + // No-op: ProgressiveList doesn't implement get yet + } + Op::Set(_, _) => { + // No-op: ProgressiveList doesn't implement set yet + } + Op::SetCowWithIntoMut(_, _) => { + // No-op: ProgressiveList doesn't implement set yet + } + Op::SetCowWithMakeMut(_, _) => { + // No-op: ProgressiveList doesn't implement set yet + } + Op::Push(value) => { + list.push(value.clone()).expect("push should succeed"); + spec.push(value); + } + Op::Iter => { + assert!(list.iter().eq(spec.iter())); + } + Op::IterFrom(_) => { + // No-op: ProgressiveList doesn't implement iter_from yet + } + Op::PopFront(_) => { + // No-op: ProgressiveList doesn't implement pop_front yet + } + Op::ApplyUpdates => { + // No-op: ProgressiveList doesn't have apply_updates + } + Op::Checkpoint => { + checkpoint = list.clone(); + } + Op::TreeHash => { + list.tree_hash_root(); + } + Op::Rebase => { + // No-op: ProgressiveList doesn't implement rebase yet + let _ = &checkpoint; + } + 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; + } + Op::FromIntoRoundtrip => { + // No-op: No corresponding Vector type for ProgressiveList + } + Op::IntraRebase => { + // No-op: ProgressiveList doesn't implement intra_rebase yet + } + } + } +} + fn apply_ops_list(list: &mut List, spec: &mut Spec, ops: Vec>) where T: Value + Debug + Send + Sync, @@ -492,3 +631,36 @@ mod vect { vect_test!(large_33, Large, U33, arb_large()); vect_test!(large_1024, Large, U1024, arb_large()); } + +/// Maximum length for progressive list tests. +/// We use smaller sizes than the regular list tests since progressive lists can grow unbounded. +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, PROGRESSIVE_LIST_MAX_LEN, 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()); +} From e32d55890621c95cb956b0e4fc43f511072df41c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Dec 2025 07:18:41 +0000 Subject: [PATCH 3/3] Refactor progressive list tests to only include supported operations Co-authored-by: michaelsproul <4452260+michaelsproul@users.noreply.github.com> --- src/tests/proptest/operations.rs | 86 +++++++++----------------------- 1 file changed, 23 insertions(+), 63 deletions(-) diff --git a/src/tests/proptest/operations.rs b/src/tests/proptest/operations.rs index d156c1c..6286eed 100644 --- a/src/tests/proptest/operations.rs +++ b/src/tests/proptest/operations.rs @@ -198,50 +198,32 @@ where } /// Strategy for generating operations for ProgressiveList. -/// Since ProgressiveList has no length limit, we use a fixed index range. -fn arb_op_progressive<'a, T, S>(strategy: &'a S, n: usize) -> impl Strategy> + 'a +/// Only includes operations that are currently implemented for ProgressiveList. +fn arb_op_progressive<'a, T, S>(strategy: &'a S) -> impl Strategy> + 'a where T: Debug + Clone + 'a, S: Strategy + 'a, { - // For ProgressiveList, we mainly want to test push, len, iter, and tree_hash - // since get, set, iter_from, pop_front, etc. are not implemented yet. - let a_block = prop_oneof![ + // Only include operations that are currently implemented for ProgressiveList: + // Len, Push, Iter, TreeHash, and Debase (SSZ roundtrip) + prop_oneof![ Just(Op::Len), - arb_index(n).prop_map(Op::Get), - (arb_index(n), strategy).prop_map(|(index, value)| Op::Set(index, value)), - (arb_index(n), strategy).prop_map(|(index, value)| Op::SetCowWithIntoMut(index, value)), - (arb_index(n), strategy).prop_map(|(index, value)| Op::SetCowWithMakeMut(index, value)), strategy.prop_map(Op::Push), Just(Op::Iter), - arb_index(n).prop_map(Op::IterFrom), - arb_index(n).prop_map(Op::PopFront), - Just(Op::ApplyUpdates), - ]; - let b_block = prop_oneof![ Just(Op::TreeHash), - Just(Op::Checkpoint), - Just(Op::Rebase), Just(Op::Debase), - Just(Op::FromIntoRoundtrip), - Just(Op::IntraRebase), - ]; - prop_oneof![ - 10 => a_block, - 6 => b_block ] } fn arb_ops_progressive<'a, T, S>( strategy: &'a S, - n: usize, limit: usize, ) -> impl Strategy>> + 'a where T: Debug + Clone + 'a, S: Strategy + 'a, { - proptest::collection::vec(arb_op_progressive(strategy, n), 1..limit) + proptest::collection::vec(arb_op_progressive(strategy), 1..limit) } fn apply_ops_progressive_list( @@ -251,25 +233,11 @@ fn apply_ops_progressive_list( ) where T: Value + Debug + Send + Sync, { - let mut checkpoint = list.clone(); - for op in ops { match op { Op::Len => { assert_eq!(list.len(), spec.len()); } - Op::Get(_) => { - // No-op: ProgressiveList doesn't implement get yet - } - Op::Set(_, _) => { - // No-op: ProgressiveList doesn't implement set yet - } - Op::SetCowWithIntoMut(_, _) => { - // No-op: ProgressiveList doesn't implement set yet - } - Op::SetCowWithMakeMut(_, _) => { - // No-op: ProgressiveList doesn't implement set yet - } Op::Push(value) => { list.push(value.clone()).expect("push should succeed"); spec.push(value); @@ -277,37 +245,28 @@ fn apply_ops_progressive_list( Op::Iter => { assert!(list.iter().eq(spec.iter())); } - Op::IterFrom(_) => { - // No-op: ProgressiveList doesn't implement iter_from yet - } - Op::PopFront(_) => { - // No-op: ProgressiveList doesn't implement pop_front yet - } - Op::ApplyUpdates => { - // No-op: ProgressiveList doesn't have apply_updates - } - Op::Checkpoint => { - checkpoint = list.clone(); - } Op::TreeHash => { list.tree_hash_root(); } - Op::Rebase => { - // No-op: ProgressiveList doesn't implement rebase yet - let _ = &checkpoint; - } 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; } - Op::FromIntoRoundtrip => { - // No-op: No corresponding Vector type for ProgressiveList - } - Op::IntraRebase => { - // No-op: ProgressiveList doesn't implement intra_rebase yet - } + // 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 => {} } } } @@ -632,8 +591,9 @@ mod vect { vect_test!(large_1024, Large, U1024, arb_large()); } -/// Maximum length for progressive list tests. -/// We use smaller sizes than the regular list tests since progressive lists can grow unbounded. +/// 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 { @@ -646,7 +606,7 @@ macro_rules! progressive_list_test { #[test] fn $name( init in arb_progressive_list::<$T, _>(&$strat, PROGRESSIVE_LIST_MAX_LEN), - ops in arb_ops_progressive::<$T, _>(&$strat, PROGRESSIVE_LIST_MAX_LEN, OP_LIMIT) + 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);