1use std::fmt::Debug;
7
8pub fn property_test<F: Fn()>(iterations: usize, f: F) {
22 for i in 0..iterations {
23 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(&f)) {
26 Ok(_) => {}
27 Err(e) => {
28 panic!(
29 "Property test failed on iteration {}/{}: {:?}",
30 i + 1,
31 iterations,
32 e
33 );
34 }
35 }
36 }
37}
38
39pub fn property_test_with_gen<T, G, F>(iterations: usize, gen: G, property: F)
51where
52 G: Fn() -> T,
53 F: Fn(T),
54{
55 for i in 0..iterations {
56 let value = gen();
57 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| property(value))) {
58 Ok(_) => {}
59 Err(e) => {
60 panic!(
61 "Property test failed on iteration {}/{}: {:?}",
62 i + 1,
63 iterations,
64 e
65 );
66 }
67 }
68 }
69}
70
71pub fn property_test_with_gen1<T, G, F>(iterations: usize, gen: G, property: F)
74where
75 G: Fn() -> T,
76 F: Fn(T),
77{
78 property_test_with_gen(iterations, gen, property)
79}
80
81pub fn property_test_with_gen2<T1, T2, G1, G2, F>(
98 iterations: usize,
99 gen1: G1,
100 gen2: G2,
101 property: F,
102) where
103 G1: Fn() -> T1,
104 G2: Fn() -> T2,
105 F: Fn(T1, T2),
106{
107 for i in 0..iterations {
108 let val1 = gen1();
109 let val2 = gen2();
110 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| property(val1, val2))) {
111 Ok(_) => {}
112 Err(e) => {
113 panic!(
114 "Property test failed on iteration {}/{}: {:?}",
115 i + 1,
116 iterations,
117 e
118 );
119 }
120 }
121 }
122}
123
124pub fn property_test_with_gen3<T1, T2, T3, G1, G2, G3, F>(
126 iterations: usize,
127 gen1: G1,
128 gen2: G2,
129 gen3: G3,
130 property: F,
131) where
132 G1: Fn() -> T1,
133 G2: Fn() -> T2,
134 G3: Fn() -> T3,
135 F: Fn(T1, T2, T3),
136{
137 for i in 0..iterations {
138 let val1 = gen1();
139 let val2 = gen2();
140 let val3 = gen3();
141 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| property(val1, val2, val3)))
142 {
143 Ok(_) => {}
144 Err(e) => {
145 panic!(
146 "Property test failed on iteration {}/{}: {:?}",
147 i + 1,
148 iterations,
149 e
150 );
151 }
152 }
153 }
154}
155
156pub fn shrink_int(value: i64) -> Vec<i64> {
159 let mut shrinks = vec![0];
160 let mut current = value.abs() / 2;
161 while current > 0 {
162 shrinks.push(current);
163 shrinks.push(-current);
164 current /= 2;
165 }
166 shrinks
167}
168
169pub fn find_minimal_failing<T: Copy + Debug, F, S>(initial: T, shrink: S, property: F) -> Option<T>
186where
187 F: Fn(T) -> bool,
188 S: Fn(T) -> Vec<T>,
189{
190 if property(initial) {
192 return None;
193 }
194
195 let mut current_failure = initial;
196
197 for shrunk in shrink(current_failure) {
199 if !property(shrunk) {
200 current_failure = shrunk;
201 }
202 }
203
204 Some(current_failure)
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
212 fn test_property_test_passes() {
213 property_test(10, || {
214 let a = 5;
216 let b = 3;
217 assert_eq!(a + b, b + a);
218 });
219 }
220
221 #[test]
222 #[should_panic(expected = "Property test failed")]
223 fn test_property_test_fails() {
224 property_test(10, || {
225 panic!("Intentional failure");
226 });
227 }
228
229 #[test]
230 fn test_property_test_with_gen() {
231 property_test_with_gen(
232 10,
233 || 42,
234 |value| {
235 assert_eq!(value, 42);
236 },
237 );
238 }
239
240 #[test]
241 fn test_property_test_with_gen2() {
242 property_test_with_gen2(
243 10,
244 || 1,
245 || 2,
246 |a, b| {
247 assert_eq!(a + b, 3);
248 },
249 );
250 }
251
252 #[test]
253 fn test_property_test_with_gen3() {
254 property_test_with_gen3(
255 10,
256 || 1,
257 || 2,
258 || 3,
259 |a, b, c| {
260 assert_eq!(a + b + c, 6);
261 },
262 );
263 }
264
265 #[test]
266 fn test_shrink_int() {
267 let shrinks = shrink_int(100);
268 assert!(shrinks.contains(&0));
269 assert!(shrinks.contains(&50));
270 assert!(shrinks.contains(&-50));
271 assert!(shrinks.contains(&25));
272 }
273
274 #[test]
275 fn test_find_minimal_failing() {
276 let minimal = find_minimal_failing(
277 1000,
278 shrink_int,
279 |x| x < 100, );
281
282 assert!(minimal.is_some());
283 let min = minimal.unwrap();
284 assert!(min >= 100); }
286}