1 use std::cmp;
2 use std::env;
3 use std::fmt::Debug;
4 use std::panic;
5 
6 use crate::{
7     tester::Status::{Discard, Fail, Pass},
8     Arbitrary, Gen,
9 };
10 
11 /// The main QuickCheck type for setting configuration and running QuickCheck.
12 pub struct QuickCheck {
13     tests: u64,
14     max_tests: u64,
15     min_tests_passed: u64,
16     gen: Gen,
17 }
18 
qc_tests() -> u6419 fn 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 
qc_max_tests() -> u6427 fn 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 
qc_gen_size() -> usize35 fn 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 
qc_min_tests_passed() -> u6443 fn 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 
51 impl QuickCheck {
52     /// Creates a new QuickCheck value.
53     ///
54     /// This can be used to run QuickCheck on things that implement `Testable`.
55     /// You may also adjust the configuration, such as the number of tests to
56     /// run.
57     ///
58     /// By default, the maximum number of passed tests is set to `100`, the max
59     /// number of overall tests is set to `10000` and the generator is created
60     /// with a size of `100`.
new() -> QuickCheck61     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     /// Set the random number generator to be used by QuickCheck.
gen(self, gen: Gen) -> QuickCheck71     pub fn gen(self, gen: Gen) -> QuickCheck {
72         QuickCheck { gen, ..self }
73     }
74 
75     /// Set the number of tests to run.
76     ///
77     /// This actually refers to the maximum number of *passed* tests that
78     /// can occur. Namely, if a test causes a failure, future testing on that
79     /// property stops. Additionally, if tests are discarded, there may be
80     /// fewer than `tests` passed.
tests(mut self, tests: u64) -> QuickCheck81     pub fn tests(mut self, tests: u64) -> QuickCheck {
82         self.tests = tests;
83         self
84     }
85 
86     /// Set the maximum number of tests to run.
87     ///
88     /// The number of invocations of a property will never exceed this number.
89     /// This is necessary to cap the number of tests because QuickCheck
90     /// properties can discard tests.
max_tests(mut self, max_tests: u64) -> QuickCheck91     pub fn max_tests(mut self, max_tests: u64) -> QuickCheck {
92         self.max_tests = max_tests;
93         self
94     }
95 
96     /// Set the minimum number of tests that needs to pass.
97     ///
98     /// This actually refers to the minimum number of *valid* *passed* tests
99     /// that needs to pass for the property to be considered successful.
min_tests_passed(mut self, min_tests_passed: u64) -> QuickCheck100     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     /// Tests a property and returns the result.
106     ///
107     /// The result returned is either the number of tests passed or a witness
108     /// of failure.
109     ///
110     /// (If you're using Rust's unit testing infrastructure, then you'll
111     /// want to use the `quickcheck` method, which will `panic!` on failure.)
quicktest<A>(&mut self, f: A) -> Result<u64, TestResult> where A: Testable,112     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     /// Tests a property and calls `panic!` on failure.
131     ///
132     /// The `panic!` message will include a (hopefully) minimal witness of
133     /// failure.
134     ///
135     /// It is appropriate to use this method with Rust's unit testing
136     /// infrastructure.
137     ///
138     /// Note that if the environment variable `RUST_LOG` is set to enable
139     /// `info` level log messages for the `quickcheck` crate, then this will
140     /// include output on how many QuickCheck tests were passed.
141     ///
142     /// # Example
143     ///
144     /// ```rust
145     /// use quickcheck::QuickCheck;
146     ///
147     /// fn prop_reverse_reverse() {
148     ///     fn revrev(xs: Vec<usize>) -> bool {
149     ///         let rev: Vec<_> = xs.clone().into_iter().rev().collect();
150     ///         let revrev: Vec<_> = rev.into_iter().rev().collect();
151     ///         xs == revrev
152     ///     }
153     ///     QuickCheck::new().quickcheck(revrev as fn(Vec<usize>) -> bool);
154     /// }
155     /// ```
quickcheck<A>(&mut self, f: A) where A: Testable,156     pub fn quickcheck<A>(&mut self, f: A)
157     where
158         A: Testable,
159     {
160         // Ignore log init failures, implying it has already been done.
161         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 
179 /// Convenience function for running QuickCheck.
180 ///
181 /// This is an alias for `QuickCheck::new().quickcheck(f)`.
quickcheck<A: Testable>(f: A)182 pub fn quickcheck<A: Testable>(f: A) {
183     QuickCheck::new().quickcheck(f)
184 }
185 
186 /// Describes the status of a single instance of a test.
187 ///
188 /// All testable things must be capable of producing a `TestResult`.
189 #[derive(Clone, Debug)]
190 pub struct TestResult {
191     status: Status,
192     arguments: Vec<String>,
193     err: Option<String>,
194 }
195 
196 /// Whether a test has passed, failed or been discarded.
197 #[derive(Clone, Debug)]
198 enum Status {
199     Pass,
200     Fail,
201     Discard,
202 }
203 
204 impl TestResult {
205     /// Produces a test result that indicates the current test has passed.
passed() -> TestResult206     pub fn passed() -> TestResult {
207         TestResult::from_bool(true)
208     }
209 
210     /// Produces a test result that indicates the current test has failed.
failed() -> TestResult211     pub fn failed() -> TestResult {
212         TestResult::from_bool(false)
213     }
214 
215     /// Produces a test result that indicates failure from a runtime error.
error<S: Into<String>>(msg: S) -> TestResult216     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     /// Produces a test result that instructs `quickcheck` to ignore it.
223     /// This is useful for restricting the domain of your properties.
224     /// When a test is discarded, `quickcheck` will replace it with a
225     /// fresh one (up to a certain limit).
discard() -> TestResult226     pub fn discard() -> TestResult {
227         TestResult { status: Discard, arguments: vec![], err: None }
228     }
229 
230     /// Converts a `bool` to a `TestResult`. A `true` value indicates that
231     /// the test has passed and a `false` value indicates that the test
232     /// has failed.
from_bool(b: bool) -> TestResult233     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     /// Tests if a "procedure" fails when executed. The test passes only if
242     /// `f` generates a task failure during its execution.
must_fail<T, F>(f: F) -> TestResult where F: FnOnce() -> T, F: 'static, T: 'static,243     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     /// Returns `true` if and only if this test result describes a failing
254     /// test.
is_failure(&self) -> bool255     pub fn is_failure(&self) -> bool {
256         match self.status {
257             Fail => true,
258             Pass | Discard => false,
259         }
260     }
261 
262     /// Returns `true` if and only if this test result describes a failing
263     /// test as a result of a run time error.
is_error(&self) -> bool264     pub fn is_error(&self) -> bool {
265         self.is_failure() && self.err.is_some()
266     }
267 
failed_msg(&self) -> String268     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 
284 /// `Testable` describes types (e.g., a function) whose values can be
285 /// tested.
286 ///
287 /// Anything that can be tested must be capable of producing a `TestResult`
288 /// given a random number generator. This is trivial for types like `bool`,
289 /// which are just converted to either a passing or failing test result.
290 ///
291 /// For functions, an implementation must generate random arguments
292 /// and potentially shrink those arguments if they produce a failure.
293 ///
294 /// It's unlikely that you'll have to implement this trait yourself.
295 pub trait Testable: 'static {
result(&self, _: &mut Gen) -> TestResult296     fn result(&self, _: &mut Gen) -> TestResult;
297 }
298 
299 impl Testable for bool {
result(&self, _: &mut Gen) -> TestResult300     fn result(&self, _: &mut Gen) -> TestResult {
301         TestResult::from_bool(*self)
302     }
303 }
304 
305 impl Testable for () {
result(&self, _: &mut Gen) -> TestResult306     fn result(&self, _: &mut Gen) -> TestResult {
307         TestResult::passed()
308     }
309 }
310 
311 impl Testable for TestResult {
result(&self, _: &mut Gen) -> TestResult312     fn result(&self, _: &mut Gen) -> TestResult {
313         self.clone()
314     }
315 }
316 
317 impl<A, E> Testable for Result<A, E>
318 where
319     A: Testable,
320     E: Debug + 'static,
321 {
result(&self, g: &mut Gen) -> TestResult322     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 
330 /// Return a vector of the debug formatting of each item in `args`
debug_reprs(args: &[&dyn Debug]) -> Vec<String>331 fn debug_reprs(args: &[&dyn Debug]) -> Vec<String> {
332     args.iter().map(|x| format!("{:?}", x)).collect()
333 }
334 
335 macro_rules! testable_fn {
336     ($($name: ident),*) => {
337 
338 impl<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                     // The shrunk value *does* witness a failure, so keep
357                     // trying to shrink it.
358                     let shrunk = shrink_failure(g, self_, t);
359 
360                     // If we couldn't witness a failure on any shrunk value,
361                     // then return the failure we already have.
362                     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 
386 testable_fn!();
387 testable_fn!(A);
388 testable_fn!(A, B);
389 testable_fn!(A, B, C);
390 testable_fn!(A, B, C, D);
391 testable_fn!(A, B, C, D, E);
392 testable_fn!(A, B, C, D, E, F);
393 testable_fn!(A, B, C, D, E, F, G);
394 testable_fn!(A, B, C, D, E, F, G, H);
395 
safe<T, F>(fun: F) -> Result<T, String> where F: FnOnce() -> T, F: 'static, T: 'static,396 fn safe<T, F>(fun: F) -> Result<T, String>
397 where
398     F: FnOnce() -> T,
399     F: 'static,
400     T: 'static,
401 {
402     panic::catch_unwind(panic::AssertUnwindSafe(fun)).map_err(|any_err| {
403         // Extract common types of panic payload:
404         // panic and assert produce &str or String
405         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 
415 /// Convenient aliases.
416 trait AShow: Arbitrary + Debug {}
417 impl<A: Arbitrary + Debug> AShow for A {}
418 
419 #[cfg(test)]
420 mod test {
421     use crate::{Gen, QuickCheck};
422 
423     #[test]
shrinking_regression_issue_126()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]
size_for_small_types_issue_143()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]
regression_signed_shrinker_panic()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 }
452