reth_codecs/
test_utils.rs

1//! Test utilities for `Compact` derive macro
2
3/// Macro to ensure that derived `Compact` types can be extended with new fields while maintaining
4/// backwards compatibility.
5///
6/// Verifies that the unused bits in the bitflag struct remain as expected: `Zero` or `NotZero`. For
7/// more on bitflag struct: [`reth_codecs_derive::Compact`].
8///
9/// Possible failures:
10/// ### 1. `NotZero` -> `Zero`
11///    This wouldn't allow new fields to be added in the future. Instead, the new field of `T`
12/// should be `Option<TExtension>`  to allow for new fields. The new user field should be included
13/// in `TExtension` type. **Only then, update the test to expect `Zero` for `T` and
14/// add a new test for `TExtension`.**
15///
16/// **Goal:**
17///
18///    ```rust,ignore
19/// {
20///    struct T {
21///        // ... other fields
22///        ext: Option<TExtension>
23///    }
24///    
25///    // Use an extension type for new fields:
26///    struct TExtension {
27///        new_field_b: Option<u8>,
28///    }
29///    
30///    // Change tests
31///    validate_bitflag_backwards_compat!(T, UnusedBits::Zero);
32///    validate_bitflag_backwards_compat!(TExtension, UnusedBits::NotZero);
33/// }   
34/// ```
35/// 
36/// ### 2. `Zero` -> `NotZero`
37/// If it becomes `NotZero`, it would break backwards compatibility, so there is not an action item,
38/// and should be handled with care in a case by case scenario.
39#[macro_export]
40macro_rules! validate_bitflag_backwards_compat {
41    ($type:ty, $expected_unused_bits:expr) => {
42        let actual_unused_bits = <$type>::bitflag_unused_bits();
43
44        match $expected_unused_bits {
45            UnusedBits::NotZero => {
46                assert_ne!(
47                    actual_unused_bits,
48                    0,
49                    "Assertion failed: `bitflag_unused_bits` for type `{}` unexpectedly went from non-zero to zero!",
50                    stringify!($type)
51                );
52            }
53            UnusedBits::Zero => {
54                assert_eq!(
55                    actual_unused_bits,
56                    0,
57                    "Assertion failed: `bitflag_unused_bits` for type `{}` unexpectedly went from zero to non-zero!",
58                    stringify!($type)
59                );
60            }
61        }
62    };
63}
64
65/// Whether there are zero or more unused bits on `Compact` bitflag struct.
66///
67/// To be used with [`validate_bitflag_backwards_compat`].
68#[derive(Debug)]
69pub enum UnusedBits {
70    /// Zero bits available for a new field.
71    Zero,
72    /// Bits available for a new field.
73    NotZero,
74}
75
76impl UnusedBits {
77    /// Returns true if the variant is [`Self::NotZero`].
78    pub const fn not_zero(&self) -> bool {
79        matches!(self, Self::NotZero)
80    }
81}
82
83/// Tests decoding and re-encoding to ensure correctness.
84pub fn test_decode<T: crate::Compact>(buf: &[u8]) {
85    let (decoded, _) = T::from_compact(buf, buf.len());
86    let mut encoded = Vec::with_capacity(buf.len());
87
88    decoded.to_compact(&mut encoded);
89    assert_eq!(buf, &encoded[..]);
90}