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