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}