|
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}; |
3 | 3 | use proptest::prelude::*; |
4 | 4 | use ssz::{Decode, Encode}; |
5 | 5 | use std::fmt::Debug; |
@@ -91,6 +91,30 @@ impl<T: Value, N: Unsigned> Spec<T, N> { |
91 | 91 | } |
92 | 92 | } |
93 | 93 |
|
| 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 | + |
94 | 118 | #[derive(Debug, Clone)] |
95 | 119 | pub enum Op<T> { |
96 | 120 | /// Check that `len` returns the correct length. |
@@ -173,6 +197,121 @@ where |
173 | 197 | proptest::collection::vec(arb_op(strategy, n), 1..limit) |
174 | 198 | } |
175 | 199 |
|
| 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 | + |
176 | 315 | fn apply_ops_list<T, N>(list: &mut List<T, N>, spec: &mut Spec<T, N>, ops: Vec<Op<T>>) |
177 | 316 | where |
178 | 317 | T: Value + Debug + Send + Sync, |
@@ -492,3 +631,36 @@ mod vect { |
492 | 631 | vect_test!(large_33, Large, U33, arb_large()); |
493 | 632 | vect_test!(large_1024, Large, U1024, arb_large()); |
494 | 633 | } |
| 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