foundry_common/
traits.rs
1use alloy_json_abi::Function;
4use alloy_primitives::Bytes;
5use alloy_sol_types::SolError;
6use std::{fmt, path::Path};
7
8pub trait TestFilter: Send + Sync {
10 fn matches_test(&self, test_name: &str) -> bool;
12
13 fn matches_contract(&self, contract_name: &str) -> bool;
15
16 fn matches_path(&self, path: &Path) -> bool;
18}
19
20pub trait TestFunctionExt {
22 fn test_function_kind(&self) -> TestFunctionKind {
24 TestFunctionKind::classify(self.tfe_as_str(), self.tfe_has_inputs())
25 }
26
27 fn is_setup(&self) -> bool {
29 self.test_function_kind().is_setup()
30 }
31
32 fn is_any_test(&self) -> bool {
34 self.test_function_kind().is_any_test()
35 }
36
37 fn is_any_test_fail(&self) -> bool {
39 self.test_function_kind().is_any_test_fail()
40 }
41
42 fn is_unit_test(&self) -> bool {
44 matches!(self.test_function_kind(), TestFunctionKind::UnitTest { .. })
45 }
46
47 fn is_before_test_setup(&self) -> bool {
49 self.tfe_as_str().eq_ignore_ascii_case("beforetestsetup")
50 }
51
52 fn is_fuzz_test(&self) -> bool {
54 self.test_function_kind().is_fuzz_test()
55 }
56
57 fn is_invariant_test(&self) -> bool {
59 self.test_function_kind().is_invariant_test()
60 }
61
62 fn is_after_invariant(&self) -> bool {
64 self.test_function_kind().is_after_invariant()
65 }
66
67 fn is_fixture(&self) -> bool {
69 self.test_function_kind().is_fixture()
70 }
71
72 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#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
119pub enum TestFunctionKind {
120 Setup,
122 UnitTest { should_fail: bool },
124 FuzzTest { should_fail: bool },
126 InvariantTest,
128 TableTest,
130 AfterInvariant,
132 Fixture,
134 Unknown,
136}
137
138impl TestFunctionKind {
139 #[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 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 #[inline]
180 pub const fn is_setup(&self) -> bool {
181 matches!(self, Self::Setup)
182 }
183
184 #[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 #[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 #[inline]
201 pub fn is_unit_test(&self) -> bool {
202 matches!(self, Self::UnitTest { .. })
203 }
204
205 #[inline]
207 pub const fn is_fuzz_test(&self) -> bool {
208 matches!(self, Self::FuzzTest { .. })
209 }
210
211 #[inline]
213 pub const fn is_invariant_test(&self) -> bool {
214 matches!(self, Self::InvariantTest)
215 }
216
217 #[inline]
219 pub const fn is_table_test(&self) -> bool {
220 matches!(self, Self::TableTest)
221 }
222
223 #[inline]
225 pub const fn is_after_invariant(&self) -> bool {
226 matches!(self, Self::AfterInvariant)
227 }
228
229 #[inline]
231 pub const fn is_fixture(&self) -> bool {
232 matches!(self, Self::Fixture)
233 }
234
235 #[inline]
237 pub const fn is_known(&self) -> bool {
238 !matches!(self, Self::Unknown)
239 }
240
241 #[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
254pub trait ErrorExt: std::error::Error {
256 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}