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 {
13    tests: u64,
14    max_tests: u64,
15    min_tests_passed: u64,
16    gen: Gen,
17}
18
19fn qc_tests() -> u64 {
20    let default = 100;
21    match env::var("QUICKCHECK_TESTS") {
22        Ok(val) => val.parse().unwrap_or(default),
23        Err(_) => default,
24    }
25}
26
27fn qc_max_tests() -> u64 {
28    let default = 10_000;
29    match env::var("QUICKCHECK_MAX_TESTS") {
30        Ok(val) => val.parse().unwrap_or(default),
31        Err(_) => default,
32    }
33}
34
35fn qc_gen_size() -> usize {
36    let default = 100;
37    match env::var("QUICKCHECK_GENERATOR_SIZE") {
38        Ok(val) => val.parse().unwrap_or(default),
39        Err(_) => default,
40    }
41}
42
43fn qc_min_tests_passed() -> u64 {
44    let default = 0;
45    match env::var("QUICKCHECK_MIN_TESTS_PASSED") {
46        Ok(val) => val.parse().unwrap_or(default),
47        Err(_) => default,
48    }
49}
50
51impl QuickCheck {
52    pub fn new() -> QuickCheck {
62        let gen = Gen::new(qc_gen_size());
63        let tests = qc_tests();
64        let max_tests = cmp::max(tests, qc_max_tests());
65        let min_tests_passed = qc_min_tests_passed();
66
67        QuickCheck { tests, max_tests, min_tests_passed, gen }
68    }
69
70    pub fn gen(self, gen: Gen) -> QuickCheck {
72        QuickCheck { gen, ..self }
73    }
74
75    pub fn tests(mut self, tests: u64) -> QuickCheck {
82        self.tests = tests;
83        self
84    }
85
86    pub fn max_tests(mut self, max_tests: u64) -> QuickCheck {
92        self.max_tests = max_tests;
93        self
94    }
95
96    pub fn min_tests_passed(mut self, min_tests_passed: u64) -> QuickCheck {
101        self.min_tests_passed = min_tests_passed;
102        self
103    }
104
105    pub fn quicktest<A>(&mut self, f: A) -> Result<u64, TestResult>
113    where
114        A: Testable,
115    {
116        let mut n_tests_passed = 0;
117        for _ in 0..self.max_tests {
118            if n_tests_passed >= self.tests {
119                break;
120            }
121            match f.result(&mut self.gen) {
122                TestResult { status: Pass, .. } => n_tests_passed += 1,
123                TestResult { status: Discard, .. } => continue,
124                r @ TestResult { status: Fail, .. } => return Err(r),
125            }
126        }
127        Ok(n_tests_passed)
128    }
129
130    pub fn quickcheck<A>(&mut self, f: A)
157    where
158        A: Testable,
159    {
160        let _ = crate::env_logger_init();
162
163        let n_tests_passed = match self.quicktest(f) {
164            Ok(n_tests_passed) => n_tests_passed,
165            Err(result) => panic!(result.failed_msg()),
166        };
167
168        if n_tests_passed >= self.min_tests_passed {
169            info!("(Passed {} QuickCheck tests.)", n_tests_passed)
170        } else {
171            panic!(
172                "(Unable to generate enough tests, {} not discarded.)",
173                n_tests_passed
174            )
175        }
176    }
177}
178
179pub fn quickcheck<A: Testable>(f: A) {
183    QuickCheck::new().quickcheck(f)
184}
185
186#[derive(Clone, Debug)]
190pub struct TestResult {
191    status: Status,
192    arguments: Vec<String>,
193    err: Option<String>,
194}
195
196#[derive(Clone, Debug)]
198enum Status {
199    Pass,
200    Fail,
201    Discard,
202}
203
204impl TestResult {
205    pub fn passed() -> TestResult {
207        TestResult::from_bool(true)
208    }
209
210    pub fn failed() -> TestResult {
212        TestResult::from_bool(false)
213    }
214
215    pub fn error<S: Into<String>>(msg: S) -> TestResult {
217        let mut r = TestResult::from_bool(false);
218        r.err = Some(msg.into());
219        r
220    }
221
222    pub fn discard() -> TestResult {
227        TestResult { status: Discard, arguments: vec![], err: None }
228    }
229
230    pub fn from_bool(b: bool) -> TestResult {
234        TestResult {
235            status: if b { Pass } else { Fail },
236            arguments: vec![],
237            err: None,
238        }
239    }
240
241    pub fn must_fail<T, F>(f: F) -> TestResult
244    where
245        F: FnOnce() -> T,
246        F: 'static,
247        T: 'static,
248    {
249        let f = panic::AssertUnwindSafe(f);
250        TestResult::from_bool(panic::catch_unwind(f).is_err())
251    }
252
253    pub fn is_failure(&self) -> bool {
256        match self.status {
257            Fail => true,
258            Pass | Discard => false,
259        }
260    }
261
262    pub fn is_error(&self) -> bool {
265        self.is_failure() && self.err.is_some()
266    }
267
268    fn failed_msg(&self) -> String {
269        match self.err {
270            None => format!(
271                "[quickcheck] TEST FAILED. Arguments: ({})",
272                self.arguments.join(", ")
273            ),
274            Some(ref err) => format!(
275                "[quickcheck] TEST FAILED (runtime error). \
276                 Arguments: ({})\nError: {}",
277                self.arguments.join(", "),
278                err
279            ),
280        }
281    }
282}
283
284pub trait Testable: 'static {
296    fn result(&self, _: &mut Gen) -> TestResult;
297}
298
299impl Testable for bool {
300    fn result(&self, _: &mut Gen) -> TestResult {
301        TestResult::from_bool(*self)
302    }
303}
304
305impl Testable for () {
306    fn result(&self, _: &mut Gen) -> TestResult {
307        TestResult::passed()
308    }
309}
310
311impl Testable for TestResult {
312    fn result(&self, _: &mut Gen) -> TestResult {
313        self.clone()
314    }
315}
316
317impl<A, E> Testable for Result<A, E>
318where
319    A: Testable,
320    E: Debug + 'static,
321{
322    fn result(&self, g: &mut Gen) -> TestResult {
323        match *self {
324            Ok(ref r) => r.result(g),
325            Err(ref err) => TestResult::error(format!("{:?}", err)),
326        }
327    }
328}
329
330fn debug_reprs(args: &[&dyn Debug]) -> Vec<String> {
332    args.iter().map(|x| format!("{:?}", x)).collect()
333}
334
335macro_rules! testable_fn {
336    ($($name: ident),*) => {
337
338impl<T: Testable,
339     $($name: Arbitrary + Debug),*> Testable for fn($($name),*) -> T {
340    #[allow(non_snake_case)]
341    fn result(&self, g: &mut Gen) -> TestResult {
342        fn shrink_failure<T: Testable, $($name: Arbitrary + Debug),*>(
343            g: &mut Gen,
344            self_: fn($($name),*) -> T,
345            a: ($($name,)*),
346        ) -> Option<TestResult> {
347            for t in a.shrink() {
348                let ($($name,)*) = t.clone();
349                let mut r_new = safe(move || {self_($($name),*)}).result(g);
350                if r_new.is_failure() {
351                    {
352                        let ($(ref $name,)*) : ($($name,)*) = t;
353                        r_new.arguments = debug_reprs(&[$($name),*]);
354                    }
355
356                    let shrunk = shrink_failure(g, self_, t);
359
360                    return Some(shrunk.unwrap_or(r_new))
363                }
364            }
365            None
366        }
367
368        let self_ = *self;
369        let a: ($($name,)*) = Arbitrary::arbitrary(g);
370        let ( $($name,)* ) = a.clone();
371        let mut r = safe(move || {self_($($name),*)}).result(g);
372
373        {
374            let ( $(ref $name,)* ) = a;
375            r.arguments = debug_reprs(&[$($name),*]);
376        }
377        match r.status {
378            Pass|Discard => r,
379            Fail => {
380                shrink_failure(g, self_, a).unwrap_or(r)
381            }
382        }
383    }
384}}}
385
386testable_fn!();
387testable_fn!(A);
388testable_fn!(A, B);
389testable_fn!(A, B, C);
390testable_fn!(A, B, C, D);
391testable_fn!(A, B, C, D, E);
392testable_fn!(A, B, C, D, E, F);
393testable_fn!(A, B, C, D, E, F, G);
394testable_fn!(A, B, C, D, E, F, G, H);
395
396fn safe<T, F>(fun: F) -> Result<T, String>
397where
398    F: FnOnce() -> T,
399    F: 'static,
400    T: 'static,
401{
402    panic::catch_unwind(panic::AssertUnwindSafe(fun)).map_err(|any_err| {
403        if let Some(&s) = any_err.downcast_ref::<&str>() {
406            s.to_owned()
407        } else if let Some(s) = any_err.downcast_ref::<String>() {
408            s.to_owned()
409        } else {
410            "UNABLE TO SHOW RESULT OF PANIC.".to_owned()
411        }
412    })
413}
414
415trait AShow: Arbitrary + Debug {}
417impl<A: Arbitrary + Debug> AShow for A {}
418
419#[cfg(test)]
420mod test {
421    use crate::{Gen, QuickCheck};
422
423    #[test]
424    fn shrinking_regression_issue_126() {
425        fn thetest(vals: Vec<bool>) -> bool {
426            vals.iter().filter(|&v| *v).count() < 2
427        }
428        let failing_case = QuickCheck::new()
429            .quicktest(thetest as fn(vals: Vec<bool>) -> bool)
430            .unwrap_err();
431        let expected_argument = format!("{:?}", [true, true]);
432        assert_eq!(failing_case.arguments, vec![expected_argument]);
433    }
434
435    #[test]
436    fn size_for_small_types_issue_143() {
437        fn t(_: i8) -> bool {
438            true
439        }
440        QuickCheck::new().gen(Gen::new(129)).quickcheck(t as fn(i8) -> bool);
441    }
442
443    #[test]
444    fn regression_signed_shrinker_panic() {
445        fn foo_can_shrink(v: i8) -> bool {
446            let _ = crate::Arbitrary::shrink(&v).take(100).count();
447            true
448        }
449        crate::quickcheck(foo_can_shrink as fn(i8) -> bool);
450    }
451}