tempo_precompiles_macros/
storable_tests.rs

1//! Code generation for storage trait property tests.
2//!
3//! This module generates comprehensive property tests for all supported storage types,
4//! including complete test function implementations.
5
6use proc_macro2::TokenStream;
7use quote::quote;
8
9use crate::storable_primitives::{ALLOY_INT_SIZES, RUST_INT_SIZES};
10const FIXED_BYTES_SIZES: &[usize] = &[
11    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
12    27, 28, 29, 30, 31, 32,
13];
14
15/// Generate all storage type tests.
16///
17/// This function generates:
18/// 1. Arbitrary function generators for all types
19/// 2. Complete proptest! blocks with test function implementations
20pub(crate) fn gen_storable_tests() -> TokenStream {
21    let rust_unsigned_arb = gen_rust_unsigned_arbitrary();
22    let rust_signed_arb = gen_rust_signed_arbitrary();
23    let alloy_unsigned_arb = gen_alloy_unsigned_arbitrary();
24    let alloy_signed_arb = gen_alloy_signed_arbitrary();
25    let fixed_bytes_arb = gen_fixed_bytes_arbitrary();
26
27    let rust_unsigned_tests = gen_rust_unsigned_tests();
28    let rust_signed_tests = gen_rust_signed_tests();
29    let alloy_unsigned_tests = gen_alloy_unsigned_tests();
30    let alloy_signed_tests = gen_alloy_signed_tests();
31    let fixed_bytes_tests = gen_fixed_bytes_tests();
32
33    quote! {
34        // -- ARBITRARY FUNCTION GENERATORS ----------------------------------------
35
36        #rust_unsigned_arb
37        #rust_signed_arb
38        #alloy_unsigned_arb
39        #alloy_signed_arb
40        #fixed_bytes_arb
41
42        // -- GENERATED TESTS ------------------------------------------------------
43
44        #rust_unsigned_tests
45        #rust_signed_tests
46        #alloy_unsigned_tests
47        #alloy_signed_tests
48        #fixed_bytes_tests
49    }
50}
51
52/// Generate arbitrary functions for Rust unsigned integers
53fn gen_rust_unsigned_arbitrary() -> TokenStream {
54    quote! {}
55}
56
57/// Generate arbitrary functions for Rust signed integers
58fn gen_rust_signed_arbitrary() -> TokenStream {
59    quote! {}
60}
61
62/// Generate arbitrary functions for Alloy unsigned integers
63fn gen_alloy_unsigned_arbitrary() -> TokenStream {
64    let funcs: Vec<_> = ALLOY_INT_SIZES
65        .iter()
66        .map(|&size| {
67            let type_name = quote::format_ident!("U{size}");
68            let fn_name = quote::format_ident!("arb_u{size}_alloy");
69
70            quote! {
71                fn #fn_name() -> impl Strategy<Value = ::alloy::primitives::#type_name> {
72                    Just(()).prop_perturb(|_, _| ::alloy::primitives::#type_name::random())
73                }
74            }
75        })
76        .collect();
77
78    quote! { #(#funcs)* }
79}
80
81/// Generate arbitrary functions for Alloy signed integers
82fn gen_alloy_signed_arbitrary() -> TokenStream {
83    let funcs: Vec<_> = ALLOY_INT_SIZES
84        .iter()
85        .flat_map(|&size| {
86            let signed_type = quote::format_ident!("I{size}");
87            let unsigned_type = quote::format_ident!("U{size}");
88            let arb_any_fn = quote::format_ident!("arb_i{size}_alloy");
89            let arb_pos_fn = quote::format_ident!("arb_positive_i{size}_alloy");
90            let arb_neg_fn = quote::format_ident!("arb_negative_i{size}_alloy");
91            let arb_unsigned_fn = quote::format_ident!("arb_u{size}_alloy");
92
93            vec![
94                // Any signed value
95                quote! {
96                    fn #arb_any_fn() -> impl Strategy<Value = ::alloy::primitives::#signed_type> {
97                        #arb_unsigned_fn().prop_map(|u| ::alloy::primitives::#signed_type::from_raw(u))
98                    }
99                },
100                // Positive values only
101                quote! {
102                    fn #arb_pos_fn() -> impl Strategy<Value = ::alloy::primitives::#signed_type> {
103                        #arb_unsigned_fn().prop_map(|u| {
104                            ::alloy::primitives::#signed_type::from_raw(
105                                u & (::alloy::primitives::#unsigned_type::MAX >> 1)
106                            )
107                        })
108                    }
109                },
110                // Negative values only
111                quote! {
112                    fn #arb_neg_fn() -> impl Strategy<Value = ::alloy::primitives::#signed_type> {
113                        #arb_pos_fn().prop_map(|i| -i)
114                    }
115                },
116            ]
117        })
118        .collect();
119
120    quote! { #(#funcs)* }
121}
122
123/// Generate arbitrary functions for FixedBytes
124fn gen_fixed_bytes_arbitrary() -> TokenStream {
125    let funcs: Vec<_> = FIXED_BYTES_SIZES
126        .iter()
127        .map(|&size| {
128            let fn_name = quote::format_ident!("arb_fixed_bytes_{size}");
129
130            quote! {
131                fn #fn_name() -> impl Strategy<Value = ::alloy::primitives::FixedBytes<#size>> {
132                    Just(()).prop_perturb(|_, _| ::alloy::primitives::FixedBytes::<#size>::random())
133                }
134            }
135        })
136        .collect();
137
138    quote! { #(#funcs)* }
139}
140
141/// Generate complete proptest! block for Rust unsigned integers
142fn gen_rust_unsigned_tests() -> TokenStream {
143    let tests: Vec<_> = RUST_INT_SIZES
144        .iter()
145        .map(|&size| {
146            let type_name = quote::format_ident!("u{size}");
147            let test_name = quote::format_ident!("test_u{size}_storage_roundtrip");
148            let label = format!("u{size}");
149
150            quote! {
151                #[test]
152                fn #test_name(value in any::<#type_name>(), base_slot in arb_safe_slot()) {
153                    let (mut storage, address) = setup_storage();
154                    StorageCtx::enter(&mut storage, || {
155                        let mut slot = Slot::<#type_name>::new(base_slot, address);
156
157                        // Verify store → load roundtrip
158                        slot.write(value).unwrap();
159                        let loaded = slot.read().unwrap();
160                        assert_eq!(value, loaded, concat!(#label, " roundtrip failed"));
161
162                        // Verify delete works
163                        slot.delete().unwrap();
164                        let after_delete = slot.read().unwrap();
165                        assert_eq!(after_delete, 0, concat!(#label, " not zero after delete"));
166
167                        // EVM word roundtrip
168                        let word = value.to_word();
169                        let recovered = #type_name::from_word(word).unwrap();
170                        assert_eq!(value, recovered, concat!(#label, " EVM word roundtrip failed"));
171
172                    });
173                }
174            }
175        })
176        .collect();
177
178    quote! {
179        proptest! {
180            #![proptest_config(ProptestConfig::with_cases(500))]
181
182            #(#tests)*
183        }
184    }
185}
186
187/// Generate complete proptest! block for Rust signed integers
188fn gen_rust_signed_tests() -> TokenStream {
189    let tests: Vec<_> = RUST_INT_SIZES
190        .iter()
191        .flat_map(|&size| {
192            let type_name = quote::format_ident!("i{size}");
193            let pos_test_name = quote::format_ident!("test_i{size}_positive_storage_roundtrip");
194            let neg_test_name = quote::format_ident!("test_i{size}_negative_storage_roundtrip");
195            let label = format!("i{size}");
196
197            vec![
198                // Positive test
199                quote! {
200                    #[test]
201                    fn #pos_test_name(value in 0 as #type_name..=#type_name::MAX, base_slot in arb_safe_slot()) {
202                        let (mut storage, address) = setup_storage();
203                        StorageCtx::enter(&mut storage, || {
204                            let mut slot = Slot::<#type_name>::new(base_slot, address);
205
206                            // Verify store → load roundtrip
207                            slot.write(value).unwrap();
208                            let loaded = slot.read().unwrap();
209                            assert_eq!(value, loaded, concat!(#label, " positive roundtrip failed"));
210
211                            // Verify delete works
212                            slot.delete().unwrap();
213                            let after_delete = slot.read().unwrap();
214                            assert_eq!(after_delete, 0, concat!(#label, " not zero after delete"));
215
216                            // EVM word roundtrip
217                            let word = value.to_word();
218                            let recovered = #type_name::from_word(word).unwrap();
219                            assert_eq!(value, recovered, concat!(#label, " positive EVM word roundtrip failed"));
220                        });
221                    }
222                },
223                // Negative test
224                quote! {
225                    #[test]
226                    fn #neg_test_name(value in #type_name::MIN..0 as #type_name, base_slot in arb_safe_slot()) {
227                        let (mut storage, address) = setup_storage();
228                        StorageCtx::enter(&mut storage, || {
229                            let mut slot = Slot::<#type_name>::new(base_slot, address);
230
231                            // Verify store → load roundtrip
232                            slot.write(value).unwrap();
233                            let loaded = slot.read().unwrap();
234                            assert_eq!(value, loaded, concat!(#label, " negative roundtrip failed"));
235
236                            // Verify delete works
237                            slot.delete().unwrap();
238                            let after_delete = slot.read().unwrap();
239                            assert_eq!(after_delete, 0, concat!(#label, " not zero after delete"));
240
241                            // EVM word roundtrip
242                            let word = value.to_word();
243                            let recovered = #type_name::from_word(word).unwrap();
244                            assert_eq!(value, recovered, concat!(#label, " negative EVM word roundtrip failed"));
245                        });
246                    }
247                },
248            ]
249        })
250        .collect();
251
252    quote! {
253        proptest! {
254            #![proptest_config(ProptestConfig::with_cases(500))]
255
256            #(#tests)*
257        }
258    }
259}
260
261/// Generate complete proptest! block for Alloy unsigned integers
262fn gen_alloy_unsigned_tests() -> TokenStream {
263    let tests: Vec<_> = ALLOY_INT_SIZES
264        .iter()
265        .map(|&size| {
266            let type_name = quote::format_ident!("U{size}");
267            let test_name = quote::format_ident!("test_u{size}_alloy_storage_roundtrip");
268            let arb_fn = quote::format_ident!("arb_u{size}_alloy");
269            let label = format!("U{size}");
270
271            quote! {
272                #[test]
273                fn #test_name(value in #arb_fn(), base_slot in arb_safe_slot()) {
274                    let (mut storage, address) = setup_storage();
275                    StorageCtx::enter(&mut storage, || {
276                        let mut slot = Slot::<::alloy::primitives::#type_name>::new(base_slot, address);
277
278                        // Verify store → load roundtrip
279                        slot.write(value).unwrap();
280                        let loaded = slot.read().unwrap();
281                        assert_eq!(value, loaded, concat!(#label, " roundtrip failed"));
282
283                        // Verify delete works
284                        slot.delete().unwrap();
285                        let after_delete = slot.read().unwrap();
286                        assert_eq!(
287                            after_delete,
288                            ::alloy::primitives::#type_name::ZERO,
289                            concat!(#label, " not zero after delete")
290                        );
291
292                        // EVM word roundtrip
293                        let word = value.to_word();
294                        let recovered = ::alloy::primitives::#type_name::from_word(word).unwrap();
295                        assert_eq!(value, recovered, concat!(#label, " EVM word roundtrip failed"));
296
297                    });
298                }
299            }
300        })
301        .collect();
302
303    quote! {
304        proptest! {
305            #![proptest_config(ProptestConfig::with_cases(500))]
306
307            #(#tests)*
308        }
309    }
310}
311
312/// Generate complete proptest! block for Alloy signed integers
313fn gen_alloy_signed_tests() -> TokenStream {
314    let tests: Vec<_> = ALLOY_INT_SIZES
315        .iter()
316        .flat_map(|&size| {
317            let type_name = quote::format_ident!("I{size}");
318            let pos_test_name = quote::format_ident!("test_i{size}_alloy_positive_storage_roundtrip");
319            let neg_test_name = quote::format_ident!("test_i{size}_alloy_negative_storage_roundtrip");
320            let arb_pos_fn = quote::format_ident!("arb_positive_i{size}_alloy");
321            let arb_neg_fn = quote::format_ident!("arb_negative_i{size}_alloy");
322            let label = format!("I{size}");
323
324            vec![
325                // Positive test
326                quote! {
327                    #[test]
328                    fn #pos_test_name(value in #arb_pos_fn(), base_slot in arb_safe_slot()) {
329                        let (mut storage, address) = setup_storage();
330                        StorageCtx::enter(&mut storage, || {
331                            let mut slot = Slot::<::alloy::primitives::#type_name>::new(base_slot, address);
332
333                            // Verify store → load roundtrip
334                            slot.write(value).unwrap();
335                            let loaded = slot.read().unwrap();
336                            assert_eq!(value, loaded, concat!(#label, " positive roundtrip failed"));
337
338                            // Verify delete works
339                            slot.delete().unwrap();
340                            let after_delete = slot.read().unwrap();
341                            assert_eq!(
342                                after_delete,
343                                ::alloy::primitives::#type_name::ZERO,
344                                concat!(#label, " not zero after delete")
345                            );
346
347                            // EVM word roundtrip
348                            let word = value.to_word();
349                            let recovered = ::alloy::primitives::#type_name::from_word(word).unwrap();
350                            assert_eq!(value, recovered, concat!(#label, " positive EVM word roundtrip failed"));
351                        });
352                    }
353                },
354                // Negative test
355                quote! {
356                    #[test]
357                    fn #neg_test_name(value in #arb_neg_fn(), base_slot in arb_safe_slot()) {
358                        let (mut storage, address) = setup_storage();
359                        StorageCtx::enter(&mut storage, || {
360                            let mut slot = Slot::<::alloy::primitives::#type_name>::new(base_slot, address);
361
362                            // Verify store → load roundtrip
363                            slot.write(value).unwrap();
364                            let loaded = slot.read().unwrap();
365                            assert_eq!(value, loaded, concat!(#label, " negative roundtrip failed"));
366
367                            // Verify delete works
368                            slot.delete().unwrap();
369                            let after_delete = slot.read().unwrap();
370                            assert_eq!(
371                                after_delete,
372                                ::alloy::primitives::#type_name::ZERO,
373                                concat!(#label, " not zero after delete")
374                            );
375
376                            // EVM word roundtrip
377                            let word = value.to_word();
378                            let recovered = ::alloy::primitives::#type_name::from_word(word).unwrap();
379                            assert_eq!(value, recovered, concat!(#label, " negative EVM word roundtrip failed"));
380                        });
381                    }
382                },
383            ]
384        })
385        .collect();
386
387    quote! {
388        proptest! {
389            #![proptest_config(ProptestConfig::with_cases(500))]
390
391            #(#tests)*
392        }
393    }
394}
395
396/// Generate complete proptest! block for FixedBytes
397fn gen_fixed_bytes_tests() -> TokenStream {
398    let tests: Vec<_> = FIXED_BYTES_SIZES
399        .iter()
400        .map(|&size| {
401            let test_name = quote::format_ident!("test_fixed_bytes_{size}_storage_roundtrip");
402            let arb_fn = quote::format_ident!("arb_fixed_bytes_{size}");
403
404            quote! {
405                #[test]
406                fn #test_name(value in #arb_fn(), base_slot in arb_safe_slot()) {
407                    let (mut storage, address) = setup_storage();
408                    StorageCtx::enter(&mut storage, || {
409                        let mut slot = Slot::<::alloy::primitives::FixedBytes<#size>>::new(base_slot, address);
410
411                        // Verify store → load roundtrip
412                        slot.write(value).unwrap();
413                        let loaded = slot.read().unwrap();
414                        assert_eq!(
415                            value, loaded,
416                            concat!("FixedBytes<", stringify!(#size), "> roundtrip failed")
417                        );
418
419                        // Verify delete works
420                        slot.delete().unwrap();
421                        let after_delete = slot.read().unwrap();
422                        assert_eq!(
423                            after_delete,
424                            ::alloy::primitives::FixedBytes::<#size>::ZERO,
425                            concat!("FixedBytes<", stringify!(#size), "> not zero after delete")
426                        );
427
428                        // EVM word roundtrip
429                        let word = value.to_word();
430                        let recovered = ::alloy::primitives::FixedBytes::<#size>::from_word(word).unwrap();
431                        assert_eq!(
432                            value, recovered,
433                            concat!("FixedBytes<", stringify!(#size), "> EVM word roundtrip failed")
434                        );
435
436                    });
437                }
438            }
439        })
440        .collect();
441
442    quote! {
443        proptest! {
444            #![proptest_config(ProptestConfig::with_cases(500))]
445
446            #(#tests)*
447        }
448    }
449}