1use std::cmp;
2use std::env;
3use std::fmt::Debug;
4use std::panic;
5
6use crate::{
7 tester::Status::{Discard, Fail, Pass},
8 Arbitrary, Gen,
9};
10
11pub struct QuickCheck {
14 tests: u64,
15 max_tests: u64,
16 min_tests_passed: u64,
17 rng: Gen,
18}
19
20fn qc_tests() -> u64 {
21 let default = 100;
22 match env::var("QUICKCHECK_TESTS") {
23 Ok(val) => val.parse().unwrap_or(default),
24 Err(_) => default,
25 }
26}
27
28fn qc_max_tests() -> u64 {
29 let default = 10_000;
30 match env::var("QUICKCHECK_MAX_TESTS") {
31 Ok(val) => val.parse().unwrap_or(default),
32 Err(_) => default,
33 }
34}
35
36fn qc_gen_size() -> usize {
37 let default = 100;
38 match env::var("QUICKCHECK_GENERATOR_SIZE") {
39 Ok(val) => val.parse().unwrap_or(default),
40 Err(_) => default,
41 }
42}
43
44fn qc_min_tests_passed() -> u64 {
45 let default = 0;
46 match env::var("QUICKCHECK_MIN_TESTS_PASSED") {
47 Ok(val) => val.parse().unwrap_or(default),
48 Err(_) => default,
49 }
50}
51
52impl Default for QuickCheck {
53 fn default() -> Self {
54 Self::new()
55 }
56}
57
58impl QuickCheck {
59 pub fn new() -> QuickCheck {
69 let rng = Gen::new(qc_gen_size());
70 let tests = qc_tests();
71 let max_tests = cmp::max(tests, qc_max_tests());
72 let min_tests_passed = qc_min_tests_passed();
73
74 QuickCheck { tests, max_tests, min_tests_passed, rng }
75 }
76
77 pub fn rng(self, rng: Gen) -> QuickCheck {
79 QuickCheck { rng, ..self }
80 }
81
82 #[deprecated(since = "1.1.0", note = "use `set_rng` instead")]
88 pub fn r#gen(self, rng: Gen) -> QuickCheck {
89 self.rng(rng)
90 }
91
92 pub fn tests(mut self, tests: u64) -> QuickCheck {
99 self.tests = tests;
100 self
101 }
102
103 pub fn max_tests(mut self, max_tests: u64) -> QuickCheck {
109 self.max_tests = max_tests;
110 self
111 }
112
113 pub fn min_tests_passed(mut self, min_tests_passed: u64) -> QuickCheck {
118 self.min_tests_passed = min_tests_passed;
119 self
120 }
121
122 pub fn quicktest<A>(&mut self, f: A) -> Result<u64, TestResult>
130 where
131 A: Testable,
132 {
133 let mut n_tests_passed = 0;
134 for _ in 0..self.max_tests {
135 if n_tests_passed >= self.tests {
136 break;
137 }
138 match f.result(&mut self.rng) {
139 TestResult { status: Pass, .. } => n_tests_passed += 1,
140 TestResult { status: Discard, .. } => continue,
141 r @ TestResult { status: Fail, .. } => return Err(r),
142 }
143 }
144 Ok(n_tests_passed)
145 }
146
147 pub fn quickcheck<A>(&mut self, f: A)
174 where
175 A: Testable,
176 {
177 let _ = crate::env_logger_init();
179
180 let n_tests_passed = match self.quicktest(f) {
181 Ok(n_tests_passed) => n_tests_passed,
182 Err(result) => panic!("{}", result.failed_msg()),
183 };
184
185 if n_tests_passed >= self.min_tests_passed {
186 info!("(Passed {} QuickCheck tests.)", n_tests_passed);
187 } else {
188 panic!(
189 "(Unable to generate enough tests, {} not discarded.)",
190 n_tests_passed
191 );
192 }
193 }
194}
195
196pub fn quickcheck<A: Testable>(f: A) {
200 QuickCheck::new().quickcheck(f)
201}
202
203#[derive(Clone, Debug)]
207pub struct TestResult {
208 status: Status,
209 arguments: Option<Vec<String>>,
210 err: Option<String>,
211}
212
213#[derive(Clone, Debug)]
215enum Status {
216 Pass,
217 Fail,
218 Discard,
219}
220
221impl TestResult {
222 pub fn passed() -> TestResult {
224 TestResult::from_bool(true)
225 }
226
227 pub fn failed() -> TestResult {
229 TestResult::from_bool(false)
230 }
231
232 pub fn error<S: Into<String>>(msg: S) -> TestResult {
234 let mut r = TestResult::from_bool(false);
235 r.err = Some(msg.into());
236 r
237 }
238
239 pub fn discard() -> TestResult {
244 TestResult { status: Discard, arguments: None, err: None }
245 }
246
247 pub fn from_bool(b: bool) -> TestResult {
251 TestResult {
252 status: if b { Pass } else { Fail },
253 arguments: None,
254 err: None,
255 }
256 }
257
258 pub fn must_fail<T, F>(f: F) -> TestResult
261 where
262 F: FnOnce() -> T,
263 F: 'static,
264 T: 'static,
265 {
266 let f = panic::AssertUnwindSafe(f);
267 TestResult::from_bool(panic::catch_unwind(f).is_err())
268 }
269
270 pub fn is_failure(&self) -> bool {
273 match self.status {
274 Fail => true,
275 Pass | Discard => false,
276 }
277 }
278
279 pub fn is_error(&self) -> bool {
282 self.is_failure() && self.err.is_some()
283 }
284
285 fn failed_msg(&self) -> String {
286 let arguments_msg = match self.arguments {
287 None => "No Arguments Provided".to_owned(),
288 Some(ref args) => format!("Arguments: ({})", args.join(", ")),
289 };
290 match self.err {
291 None => format!("[quickcheck] TEST FAILED. {arguments_msg}"),
292 Some(ref err) => format!(
293 "[quickcheck] TEST FAILED (runtime error). {arguments_msg}\nError: {err}"
294 ),
295 }
296 }
297}
298
299impl From<bool> for TestResult {
310 fn from(b: bool) -> TestResult {
311 TestResult::from_bool(b)
312 }
313}
314
315pub trait Testable: 'static {
327 fn result(&self, _: &mut Gen) -> TestResult;
328}
329
330impl Testable for bool {
331 fn result(&self, _: &mut Gen) -> TestResult {
332 TestResult::from_bool(*self)
333 }
334}
335
336impl Testable for () {
337 fn result(&self, _: &mut Gen) -> TestResult {
338 TestResult::passed()
339 }
340}
341
342impl Testable for TestResult {
343 fn result(&self, _: &mut Gen) -> TestResult {
344 self.clone()
345 }
346}
347
348impl<A, E> Testable for Result<A, E>
349where
350 A: Testable,
351 E: Debug + 'static,
352{
353 fn result(&self, g: &mut Gen) -> TestResult {
354 match *self {
355 Ok(ref r) => r.result(g),
356 Err(ref err) => TestResult::error(format!("{err:?}")),
357 }
358 }
359}
360
361fn debug_reprs(args: &[&dyn Debug]) -> Vec<String> {
363 args.iter().map(|x| format!("{x:?}")).collect()
364}
365
366macro_rules! testable_fn {
367 ($($name: ident),*) => {
368
369impl<T: Testable,
370 $($name: Arbitrary + Debug),*> Testable for fn($($name),*) -> T {
371 #[allow(non_snake_case)]
372 fn result(&self, g: &mut Gen) -> TestResult {
373 let self_ = *self;
374 let a: ($($name,)*) = Arbitrary::arbitrary(g);
375 let ( $($name,)* ) = a.clone();
376 let mut r = safe(move || {self_($($name),*)}).result(g);
377
378 if r.is_failure() {
379 let mut a = a.shrink();
380 while let Some(t) = a.next() {
381 let ($($name,)*) = t.clone();
382 let mut r_new = safe(move || {self_($($name),*)}).result(g);
383 if r_new.is_failure() {
384 {
385 let ($(ref $name,)*) : ($($name,)*) = t;
386 r_new.arguments = Some(debug_reprs(&[$($name),*]));
387 }
388
389 r = r_new;
392
393 a = t.shrink()
396 }
397 }
398 }
399
400 r
401 }
402}}}
403
404testable_fn!();
405testable_fn!(A);
406testable_fn!(A, B);
407testable_fn!(A, B, C);
408testable_fn!(A, B, C, D);
409testable_fn!(A, B, C, D, E);
410testable_fn!(A, B, C, D, E, F);
411testable_fn!(A, B, C, D, E, F, G);
412testable_fn!(A, B, C, D, E, F, G, H);
413
414fn safe<T, F>(fun: F) -> Result<T, String>
415where
416 F: FnOnce() -> T,
417 F: 'static,
418 T: 'static,
419{
420 panic::catch_unwind(panic::AssertUnwindSafe(fun)).map_err(|any_err| {
421 if let Some(&s) = any_err.downcast_ref::<&str>() {
424 s.to_owned()
425 } else if let Some(s) = any_err.downcast_ref::<String>() {
426 s.to_owned()
427 } else {
428 "UNABLE TO SHOW RESULT OF PANIC.".to_owned()
429 }
430 })
431}
432
433#[cfg(test)]
434mod test {
435 use crate::{Gen, QuickCheck};
436
437 #[test]
438 fn shrinking_regression_issue_126() {
439 fn thetest(vals: Vec<bool>) -> bool {
440 vals.iter().filter(|&v| *v).count() < 2
441 }
442 let failing_case = QuickCheck::new()
443 .quicktest(thetest as fn(vals: Vec<bool>) -> bool)
444 .unwrap_err();
445 let expected_argument = format!("{:?}", [true, true]);
446 assert_eq!(failing_case.arguments, Some(vec![expected_argument]));
447 }
448
449 #[test]
450 fn size_for_small_types_issue_143() {
451 fn t(_: i8) -> bool {
452 true
453 }
454 QuickCheck::new().rng(Gen::new(129)).quickcheck(t as fn(i8) -> bool);
455 }
456
457 #[test]
458 fn regression_signed_shrinker_panic() {
459 fn foo_can_shrink(v: i8) -> bool {
460 let _ = crate::Arbitrary::shrink(&v).take(100).count();
461 true
462 }
463 crate::quickcheck(foo_can_shrink as fn(i8) -> bool);
464 }
465}