foundry_common/
traits.rs

1//! Commonly used traits.
2
3use alloy_json_abi::Function;
4use alloy_primitives::Bytes;
5use alloy_sol_types::SolError;
6use std::{fmt, path::Path};
7
8/// Test filter.
9pub trait TestFilter: Send + Sync {
10    /// Returns whether the test should be included.
11    fn matches_test(&self, test_name: &str) -> bool;
12
13    /// Returns whether the contract should be included.
14    fn matches_contract(&self, contract_name: &str) -> bool;
15
16    /// Returns a contract with the given path should be included.
17    fn matches_path(&self, path: &Path) -> bool;
18}
19
20/// Extension trait for `Function`.
21pub trait TestFunctionExt {
22    /// Returns the kind of test function.
23    fn test_function_kind(&self) -> TestFunctionKind {
24        TestFunctionKind::classify(self.tfe_as_str(), self.tfe_has_inputs())
25    }
26
27    /// Returns `true` if this function is a `setUp` function.
28    fn is_setup(&self) -> bool {
29        self.test_function_kind().is_setup()
30    }
31
32    /// Returns `true` if this function is a unit, fuzz, or invariant test.
33    fn is_any_test(&self) -> bool {
34        self.test_function_kind().is_any_test()
35    }
36
37    /// Returns `true` if this function is a test that should fail.
38    fn is_any_test_fail(&self) -> bool {
39        self.test_function_kind().is_any_test_fail()
40    }
41
42    /// Returns `true` if this function is a unit test.
43    fn is_unit_test(&self) -> bool {
44        matches!(self.test_function_kind(), TestFunctionKind::UnitTest { .. })
45    }
46
47    /// Returns `true` if this function is a `beforeTestSetup` function.
48    fn is_before_test_setup(&self) -> bool {
49        self.tfe_as_str().eq_ignore_ascii_case("beforetestsetup")
50    }
51
52    /// Returns `true` if this function is a fuzz test.
53    fn is_fuzz_test(&self) -> bool {
54        self.test_function_kind().is_fuzz_test()
55    }
56
57    /// Returns `true` if this function is an invariant test.
58    fn is_invariant_test(&self) -> bool {
59        self.test_function_kind().is_invariant_test()
60    }
61
62    /// Returns `true` if this function is an `afterInvariant` function.
63    fn is_after_invariant(&self) -> bool {
64        self.test_function_kind().is_after_invariant()
65    }
66
67    /// Returns `true` if this function is a `fixture` function.
68    fn is_fixture(&self) -> bool {
69        self.test_function_kind().is_fixture()
70    }
71
72    /// Returns `true` if this function is test reserved function.
73    fn is_reserved(&self) -> bool {
74        self.is_any_test()
75            || self.is_setup()
76            || self.is_before_test_setup()
77            || self.is_after_invariant()
78            || self.is_fixture()
79    }
80
81    #[doc(hidden)]
82    fn tfe_as_str(&self) -> &str;
83    #[doc(hidden)]
84    fn tfe_has_inputs(&self) -> bool;
85}
86
87impl TestFunctionExt for Function {
88    fn tfe_as_str(&self) -> &str {
89        self.name.as_str()
90    }
91
92    fn tfe_has_inputs(&self) -> bool {
93        !self.inputs.is_empty()
94    }
95}
96
97impl TestFunctionExt for String {
98    fn tfe_as_str(&self) -> &str {
99        self
100    }
101
102    fn tfe_has_inputs(&self) -> bool {
103        false
104    }
105}
106
107impl TestFunctionExt for str {
108    fn tfe_as_str(&self) -> &str {
109        self
110    }
111
112    fn tfe_has_inputs(&self) -> bool {
113        false
114    }
115}
116
117/// Test function kind.
118#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
119pub enum TestFunctionKind {
120    /// `setUp`.
121    Setup,
122    /// `test*`. `should_fail` is `true` for `testFail*`.
123    UnitTest { should_fail: bool },
124    /// `test*`, with arguments. `should_fail` is `true` for `testFail*`.
125    FuzzTest { should_fail: bool },
126    /// `invariant*` or `statefulFuzz*`.
127    InvariantTest,
128    /// `table*`, with arguments.
129    TableTest,
130    /// `afterInvariant`.
131    AfterInvariant,
132    /// `fixture*`.
133    Fixture,
134    /// Unknown kind.
135    Unknown,
136}
137
138impl TestFunctionKind {
139    /// Classify a function.
140    #[inline]
141    pub fn classify(name: &str, has_inputs: bool) -> Self {
142        match () {
143            _ if name.starts_with("test") => {
144                let should_fail = name.starts_with("testFail");
145                if has_inputs {
146                    Self::FuzzTest { should_fail }
147                } else {
148                    Self::UnitTest { should_fail }
149                }
150            }
151            _ if name.starts_with("invariant") || name.starts_with("statefulFuzz") => {
152                Self::InvariantTest
153            }
154            _ if name.starts_with("table") => Self::TableTest,
155            _ if name.eq_ignore_ascii_case("setup") => Self::Setup,
156            _ if name.eq_ignore_ascii_case("afterinvariant") => Self::AfterInvariant,
157            _ if name.starts_with("fixture") => Self::Fixture,
158            _ => Self::Unknown,
159        }
160    }
161
162    /// Returns the name of the function kind.
163    pub const fn name(&self) -> &'static str {
164        match self {
165            Self::Setup => "setUp",
166            Self::UnitTest { should_fail: false } => "test",
167            Self::UnitTest { should_fail: true } => "testFail",
168            Self::FuzzTest { should_fail: false } => "fuzz",
169            Self::FuzzTest { should_fail: true } => "fuzz fail",
170            Self::InvariantTest => "invariant",
171            Self::TableTest => "table",
172            Self::AfterInvariant => "afterInvariant",
173            Self::Fixture => "fixture",
174            Self::Unknown => "unknown",
175        }
176    }
177
178    /// Returns `true` if this function is a `setUp` function.
179    #[inline]
180    pub const fn is_setup(&self) -> bool {
181        matches!(self, Self::Setup)
182    }
183
184    /// Returns `true` if this function is a unit, fuzz, or invariant test.
185    #[inline]
186    pub const fn is_any_test(&self) -> bool {
187        matches!(
188            self,
189            Self::UnitTest { .. } | Self::FuzzTest { .. } | Self::TableTest | Self::InvariantTest
190        )
191    }
192
193    /// Returns `true` if this function is a test that should fail.
194    #[inline]
195    pub const fn is_any_test_fail(&self) -> bool {
196        matches!(self, Self::UnitTest { should_fail: true } | Self::FuzzTest { should_fail: true })
197    }
198
199    /// Returns `true` if this function is a unit test.
200    #[inline]
201    pub fn is_unit_test(&self) -> bool {
202        matches!(self, Self::UnitTest { .. })
203    }
204
205    /// Returns `true` if this function is a fuzz test.
206    #[inline]
207    pub const fn is_fuzz_test(&self) -> bool {
208        matches!(self, Self::FuzzTest { .. })
209    }
210
211    /// Returns `true` if this function is an invariant test.
212    #[inline]
213    pub const fn is_invariant_test(&self) -> bool {
214        matches!(self, Self::InvariantTest)
215    }
216
217    /// Returns `true` if this function is a table test.
218    #[inline]
219    pub const fn is_table_test(&self) -> bool {
220        matches!(self, Self::TableTest)
221    }
222
223    /// Returns `true` if this function is an `afterInvariant` function.
224    #[inline]
225    pub const fn is_after_invariant(&self) -> bool {
226        matches!(self, Self::AfterInvariant)
227    }
228
229    /// Returns `true` if this function is a `fixture` function.
230    #[inline]
231    pub const fn is_fixture(&self) -> bool {
232        matches!(self, Self::Fixture)
233    }
234
235    /// Returns `true` if this function kind is known.
236    #[inline]
237    pub const fn is_known(&self) -> bool {
238        !matches!(self, Self::Unknown)
239    }
240
241    /// Returns `true` if this function kind is unknown.
242    #[inline]
243    pub const fn is_unknown(&self) -> bool {
244        matches!(self, Self::Unknown)
245    }
246}
247
248impl fmt::Display for TestFunctionKind {
249    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250        self.name().fmt(f)
251    }
252}
253
254/// An extension trait for `std::error::Error` for ABI encoding.
255pub trait ErrorExt: std::error::Error {
256    /// ABI-encodes the error using `Revert(string)`.
257    fn abi_encode_revert(&self) -> Bytes;
258}
259
260impl<T: std::error::Error> ErrorExt for T {
261    fn abi_encode_revert(&self) -> Bytes {
262        alloy_sol_types::Revert::from(self.to_string()).abi_encode().into()
263    }
264}