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    #[doc(hidden)]
73    fn tfe_as_str(&self) -> &str;
74    #[doc(hidden)]
75    fn tfe_has_inputs(&self) -> bool;
76}
77
78impl TestFunctionExt for Function {
79    fn tfe_as_str(&self) -> &str {
80        self.name.as_str()
81    }
82
83    fn tfe_has_inputs(&self) -> bool {
84        !self.inputs.is_empty()
85    }
86}
87
88impl TestFunctionExt for String {
89    fn tfe_as_str(&self) -> &str {
90        self
91    }
92
93    fn tfe_has_inputs(&self) -> bool {
94        false
95    }
96}
97
98impl TestFunctionExt for str {
99    fn tfe_as_str(&self) -> &str {
100        self
101    }
102
103    fn tfe_has_inputs(&self) -> bool {
104        false
105    }
106}
107
108/// Test function kind.
109#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
110pub enum TestFunctionKind {
111    /// `setUp`.
112    Setup,
113    /// `test*`. `should_fail` is `true` for `testFail*`.
114    UnitTest { should_fail: bool },
115    /// `test*`, with arguments. `should_fail` is `true` for `testFail*`.
116    FuzzTest { should_fail: bool },
117    /// `invariant*` or `statefulFuzz*`.
118    InvariantTest,
119    /// `afterInvariant`.
120    AfterInvariant,
121    /// `fixture*`.
122    Fixture,
123    /// Unknown kind.
124    Unknown,
125}
126
127impl TestFunctionKind {
128    /// Classify a function.
129    #[inline]
130    pub fn classify(name: &str, has_inputs: bool) -> Self {
131        match () {
132            _ if name.starts_with("test") => {
133                let should_fail = name.starts_with("testFail");
134                if has_inputs {
135                    Self::FuzzTest { should_fail }
136                } else {
137                    Self::UnitTest { should_fail }
138                }
139            }
140            _ if name.starts_with("invariant") || name.starts_with("statefulFuzz") => {
141                Self::InvariantTest
142            }
143            _ if name.eq_ignore_ascii_case("setup") => Self::Setup,
144            _ if name.eq_ignore_ascii_case("afterinvariant") => Self::AfterInvariant,
145            _ if name.starts_with("fixture") => Self::Fixture,
146            _ => Self::Unknown,
147        }
148    }
149
150    /// Returns the name of the function kind.
151    pub const fn name(&self) -> &'static str {
152        match self {
153            Self::Setup => "setUp",
154            Self::UnitTest { should_fail: false } => "test",
155            Self::UnitTest { should_fail: true } => "testFail",
156            Self::FuzzTest { should_fail: false } => "fuzz",
157            Self::FuzzTest { should_fail: true } => "fuzz fail",
158            Self::InvariantTest => "invariant",
159            Self::AfterInvariant => "afterInvariant",
160            Self::Fixture => "fixture",
161            Self::Unknown => "unknown",
162        }
163    }
164
165    /// Returns `true` if this function is a `setUp` function.
166    #[inline]
167    pub const fn is_setup(&self) -> bool {
168        matches!(self, Self::Setup)
169    }
170
171    /// Returns `true` if this function is a unit, fuzz, or invariant test.
172    #[inline]
173    pub const fn is_any_test(&self) -> bool {
174        matches!(self, Self::UnitTest { .. } | Self::FuzzTest { .. } | Self::InvariantTest)
175    }
176
177    /// Returns `true` if this function is a test that should fail.
178    #[inline]
179    pub const fn is_any_test_fail(&self) -> bool {
180        matches!(self, Self::UnitTest { should_fail: true } | Self::FuzzTest { should_fail: true })
181    }
182
183    /// Returns `true` if this function is a unit test.
184    #[inline]
185    pub fn is_unit_test(&self) -> bool {
186        matches!(self, Self::UnitTest { .. })
187    }
188
189    /// Returns `true` if this function is a fuzz test.
190    #[inline]
191    pub const fn is_fuzz_test(&self) -> bool {
192        matches!(self, Self::FuzzTest { .. })
193    }
194
195    /// Returns `true` if this function is an invariant test.
196    #[inline]
197    pub const fn is_invariant_test(&self) -> bool {
198        matches!(self, Self::InvariantTest)
199    }
200
201    /// Returns `true` if this function is an `afterInvariant` function.
202    #[inline]
203    pub const fn is_after_invariant(&self) -> bool {
204        matches!(self, Self::AfterInvariant)
205    }
206
207    /// Returns `true` if this function is a `fixture` function.
208    #[inline]
209    pub const fn is_fixture(&self) -> bool {
210        matches!(self, Self::Fixture)
211    }
212
213    /// Returns `true` if this function kind is known.
214    #[inline]
215    pub const fn is_known(&self) -> bool {
216        !matches!(self, Self::Unknown)
217    }
218
219    /// Returns `true` if this function kind is unknown.
220    #[inline]
221    pub const fn is_unknown(&self) -> bool {
222        matches!(self, Self::Unknown)
223    }
224}
225
226impl fmt::Display for TestFunctionKind {
227    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228        self.name().fmt(f)
229    }
230}
231
232/// An extension trait for `std::error::Error` for ABI encoding.
233pub trait ErrorExt: std::error::Error {
234    /// ABI-encodes the error using `Revert(string)`.
235    fn abi_encode_revert(&self) -> Bytes;
236}
237
238impl<T: std::error::Error> ErrorExt for T {
239    fn abi_encode_revert(&self) -> Bytes {
240        alloy_sol_types::Revert::from(self.to_string()).abi_encode().into()
241    }
242}