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) => { ::std::rt::begin_panic(result.failed_msg()); }panic!(result.failed_msg()),
166 };
167
168 if n_tests_passed >= self.min_tests_passed {
169 {
{
let lvl = ::log::Level::Info;
if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() {
::log::__private_api::log({ ::log::__private_api::GlobalLogger },
format_args!("(Passed {0} QuickCheck tests.)",
n_tests_passed), lvl,
&("quickcheck::tester", "quickcheck::tester",
::log::__private_api::loc()), ());
}
}
}info!("(Passed {} QuickCheck tests.)", n_tests_passed)
170 } else {
171 {
::std::rt::panic_fmt(format_args!("(Unable to generate enough tests, {0} not discarded.)",
n_tests_passed));
}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)]
190pub struct TestResult {
191 status: Status,
192 arguments: Vec<String>,
193 err: Option<String>,
194}
195
196)]
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: ::alloc::vec::Vec::new()vec![], err: None }
228 }
229
230 pub fn from_bool(b: bool) -> TestResult {
234 TestResult {
235 status: if b { Pass } else { Fail },
236 arguments: ::alloc::vec::Vec::new()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 => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("[quickcheck] TEST FAILED. Arguments: ({0})",
self.arguments.join(", ")))
})format!(
271 "[quickcheck] TEST FAILED. Arguments: ({})",
272 self.arguments.join(", ")
273 ),
274 Some(ref err) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("[quickcheck] TEST FAILED (runtime error). Arguments: ({0})\nError: {1}",
self.arguments.join(", "), 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(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}", err))
})format!("{:?}", err)),
326 }
327 }
328}
329
330fn debug_reprs(args: &[&dyn Debug]) -> Vec<String> {
332 args.iter().map(|x| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}", 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
386impl<T: Testable> Testable for fn() -> T {
#[allow(non_snake_case)]
fn result(&self, g: &mut Gen) -> TestResult {
fn shrink_failure<T: Testable>(g: &mut Gen, self_: fn() -> T, a: ())
-> Option<TestResult> {
for t in a.shrink() {
let () = t.clone();
let mut r_new = safe(move || { self_() }).result(g);
if r_new.is_failure() {
{ let (): () = t; r_new.arguments = debug_reprs(&[]); }
let shrunk = shrink_failure(g, self_, t);
return Some(shrunk.unwrap_or(r_new))
}
}
None
}
let self_ = *self;
let a: () = Arbitrary::arbitrary(g);
let () = a.clone();
let mut r = safe(move || { self_() }).result(g);
{ let () = a; r.arguments = debug_reprs(&[]); }
match r.status {
Pass | Discard => r,
Fail => { shrink_failure(g, self_, a).unwrap_or(r) }
}
}
}testable_fn!();
387impl<T: Testable, A: Arbitrary + Debug> Testable for fn(A) -> T {
#[allow(non_snake_case)]
fn result(&self, g: &mut Gen) -> TestResult {
fn shrink_failure<T: Testable, A: Arbitrary +
Debug>(g: &mut Gen, self_: fn(A) -> T, a: (A,))
-> Option<TestResult> {
for t in a.shrink() {
let (A,) = t.clone();
let mut r_new = safe(move || { self_(A) }).result(g);
if r_new.is_failure() {
{
let (ref A,): (A,) = t;
r_new.arguments = debug_reprs(&[A]);
}
let shrunk = shrink_failure(g, self_, t);
return Some(shrunk.unwrap_or(r_new))
}
}
None
}
let self_ = *self;
let a: (A,) = Arbitrary::arbitrary(g);
let (A,) = a.clone();
let mut r = safe(move || { self_(A) }).result(g);
{ let (ref A,) = a; r.arguments = debug_reprs(&[A]); }
match r.status {
Pass | Discard => r,
Fail => { shrink_failure(g, self_, a).unwrap_or(r) }
}
}
}testable_fn!(A);
388impl<T: Testable, A: Arbitrary + Debug, B: Arbitrary + Debug> Testable for
fn(A, B) -> T {
#[allow(non_snake_case)]
fn result(&self, g: &mut Gen) -> TestResult {
fn shrink_failure<T: Testable, A: Arbitrary + Debug, B: Arbitrary +
Debug>(g: &mut Gen, self_: fn(A, B) -> T, a: (A, B))
-> Option<TestResult> {
for t in a.shrink() {
let (A, B) = t.clone();
let mut r_new = safe(move || { self_(A, B) }).result(g);
if r_new.is_failure() {
{
let (ref A, ref B): (A, B) = t;
r_new.arguments = debug_reprs(&[A, B]);
}
let shrunk = shrink_failure(g, self_, t);
return Some(shrunk.unwrap_or(r_new))
}
}
None
}
let self_ = *self;
let a: (A, B) = Arbitrary::arbitrary(g);
let (A, B) = a.clone();
let mut r = safe(move || { self_(A, B) }).result(g);
{ let (ref A, ref B) = a; r.arguments = debug_reprs(&[A, B]); }
match r.status {
Pass | Discard => r,
Fail => { shrink_failure(g, self_, a).unwrap_or(r) }
}
}
}testable_fn!(A, B);
389impl<T: Testable, A: Arbitrary + Debug, B: Arbitrary + Debug, C: Arbitrary +
Debug> Testable for fn(A, B, C) -> T {
#[allow(non_snake_case)]
fn result(&self, g: &mut Gen) -> TestResult {
fn shrink_failure<T: Testable, A: Arbitrary + Debug, B: Arbitrary +
Debug, C: Arbitrary +
Debug>(g: &mut Gen, self_: fn(A, B, C) -> T, a: (A, B, C))
-> Option<TestResult> {
for t in a.shrink() {
let (A, B, C) = t.clone();
let mut r_new = safe(move || { self_(A, B, C) }).result(g);
if r_new.is_failure() {
{
let (ref A, ref B, ref C): (A, B, C) = t;
r_new.arguments = debug_reprs(&[A, B, C]);
}
let shrunk = shrink_failure(g, self_, t);
return Some(shrunk.unwrap_or(r_new))
}
}
None
}
let self_ = *self;
let a: (A, B, C) = Arbitrary::arbitrary(g);
let (A, B, C) = a.clone();
let mut r = safe(move || { self_(A, B, C) }).result(g);
{
let (ref A, ref B, ref C) = a;
r.arguments = debug_reprs(&[A, B, C]);
}
match r.status {
Pass | Discard => r,
Fail => { shrink_failure(g, self_, a).unwrap_or(r) }
}
}
}testable_fn!(A, B, C);
390impl<T: Testable, A: Arbitrary + Debug, B: Arbitrary + Debug, C: Arbitrary +
Debug, D: Arbitrary + Debug> Testable for fn(A, B, C, D) -> T {
#[allow(non_snake_case)]
fn result(&self, g: &mut Gen) -> TestResult {
fn shrink_failure<T: Testable, A: Arbitrary + Debug, B: Arbitrary +
Debug, C: Arbitrary + Debug, D: Arbitrary +
Debug>(g: &mut Gen, self_: fn(A, B, C, D) -> T, a: (A, B, C, D))
-> Option<TestResult> {
for t in a.shrink() {
let (A, B, C, D) = t.clone();
let mut r_new = safe(move || { self_(A, B, C, D) }).result(g);
if r_new.is_failure() {
{
let (ref A, ref B, ref C, ref D): (A, B, C, D) = t;
r_new.arguments = debug_reprs(&[A, B, C, D]);
}
let shrunk = shrink_failure(g, self_, t);
return Some(shrunk.unwrap_or(r_new))
}
}
None
}
let self_ = *self;
let a: (A, B, C, D) = Arbitrary::arbitrary(g);
let (A, B, C, D) = a.clone();
let mut r = safe(move || { self_(A, B, C, D) }).result(g);
{
let (ref A, ref B, ref C, ref D) = a;
r.arguments = debug_reprs(&[A, B, C, D]);
}
match r.status {
Pass | Discard => r,
Fail => { shrink_failure(g, self_, a).unwrap_or(r) }
}
}
}testable_fn!(A, B, C, D);
391impl<T: Testable, A: Arbitrary + Debug, B: Arbitrary + Debug, C: Arbitrary +
Debug, D: Arbitrary + Debug, E: Arbitrary + Debug> Testable for
fn(A, B, C, D, E) -> T {
#[allow(non_snake_case)]
fn result(&self, g: &mut Gen) -> TestResult {
fn shrink_failure<T: Testable, A: Arbitrary + Debug, B: Arbitrary +
Debug, C: Arbitrary + Debug, D: Arbitrary + Debug, E: Arbitrary +
Debug>(g: &mut Gen, self_: fn(A, B, C, D, E) -> T,
a: (A, B, C, D, E)) -> Option<TestResult> {
for t in a.shrink() {
let (A, B, C, D, E) = t.clone();
let mut r_new =
safe(move || { self_(A, B, C, D, E) }).result(g);
if r_new.is_failure() {
{
let (ref A, ref B, ref C, ref D, ref E): (A, B, C, D, E) =
t;
r_new.arguments = debug_reprs(&[A, B, C, D, E]);
}
let shrunk = shrink_failure(g, self_, t);
return Some(shrunk.unwrap_or(r_new))
}
}
None
}
let self_ = *self;
let a: (A, B, C, D, E) = Arbitrary::arbitrary(g);
let (A, B, C, D, E) = a.clone();
let mut r = safe(move || { self_(A, B, C, D, E) }).result(g);
{
let (ref A, ref B, ref C, ref D, ref E) = a;
r.arguments = debug_reprs(&[A, B, C, D, E]);
}
match r.status {
Pass | Discard => r,
Fail => { shrink_failure(g, self_, a).unwrap_or(r) }
}
}
}testable_fn!(A, B, C, D, E);
392impl<T: Testable, A: Arbitrary + Debug, B: Arbitrary + Debug, C: Arbitrary +
Debug, D: Arbitrary + Debug, E: Arbitrary + Debug, F: Arbitrary + Debug>
Testable for fn(A, B, C, D, E, F) -> T {
#[allow(non_snake_case)]
fn result(&self, g: &mut Gen) -> TestResult {
fn shrink_failure<T: Testable, A: Arbitrary + Debug, B: Arbitrary +
Debug, C: Arbitrary + Debug, D: Arbitrary + Debug, E: Arbitrary +
Debug, F: Arbitrary +
Debug>(g: &mut Gen, self_: fn(A, B, C, D, E, F) -> T,
a: (A, B, C, D, E, F)) -> Option<TestResult> {
for t in a.shrink() {
let (A, B, C, D, E, F) = t.clone();
let mut r_new =
safe(move || { self_(A, B, C, D, E, F) }).result(g);
if r_new.is_failure() {
{
let (ref A, ref B, ref C, ref D, ref E, ref F):
(A, B, C, D, E, F) = t;
r_new.arguments = debug_reprs(&[A, B, C, D, E, F]);
}
let shrunk = shrink_failure(g, self_, t);
return Some(shrunk.unwrap_or(r_new))
}
}
None
}
let self_ = *self;
let a: (A, B, C, D, E, F) = Arbitrary::arbitrary(g);
let (A, B, C, D, E, F) = a.clone();
let mut r = safe(move || { self_(A, B, C, D, E, F) }).result(g);
{
let (ref A, ref B, ref C, ref D, ref E, ref F) = a;
r.arguments = debug_reprs(&[A, B, C, D, E, F]);
}
match r.status {
Pass | Discard => r,
Fail => { shrink_failure(g, self_, a).unwrap_or(r) }
}
}
}testable_fn!(A, B, C, D, E, F);
393impl<T: Testable, A: Arbitrary + Debug, B: Arbitrary + Debug, C: Arbitrary +
Debug, D: Arbitrary + Debug, E: Arbitrary + Debug, F: Arbitrary + Debug,
G: Arbitrary + Debug> Testable for fn(A, B, C, D, E, F, G) -> T {
#[allow(non_snake_case)]
fn result(&self, g: &mut Gen) -> TestResult {
fn shrink_failure<T: Testable, A: Arbitrary + Debug, B: Arbitrary +
Debug, C: Arbitrary + Debug, D: Arbitrary + Debug, E: Arbitrary +
Debug, F: Arbitrary + Debug, G: Arbitrary +
Debug>(g: &mut Gen, self_: fn(A, B, C, D, E, F, G) -> T,
a: (A, B, C, D, E, F, G)) -> Option<TestResult> {
for t in a.shrink() {
let (A, B, C, D, E, F, G) = t.clone();
let mut r_new =
safe(move || { self_(A, B, C, D, E, F, G) }).result(g);
if r_new.is_failure() {
{
let (ref A, ref B, ref C, ref D, ref E, ref F, ref G):
(A, B, C, D, E, F, G) = t;
r_new.arguments = debug_reprs(&[A, B, C, D, E, F, G]);
}
let shrunk = shrink_failure(g, self_, t);
return Some(shrunk.unwrap_or(r_new))
}
}
None
}
let self_ = *self;
let a: (A, B, C, D, E, F, G) = Arbitrary::arbitrary(g);
let (A, B, C, D, E, F, G) = a.clone();
let mut r = safe(move || { self_(A, B, C, D, E, F, G) }).result(g);
{
let (ref A, ref B, ref C, ref D, ref E, ref F, ref G) = a;
r.arguments = debug_reprs(&[A, B, C, D, E, F, G]);
}
match r.status {
Pass | Discard => r,
Fail => { shrink_failure(g, self_, a).unwrap_or(r) }
}
}
}testable_fn!(A, B, C, D, E, F, G);
394impl<T: Testable, A: Arbitrary + Debug, B: Arbitrary + Debug, C: Arbitrary +
Debug, D: Arbitrary + Debug, E: Arbitrary + Debug, F: Arbitrary + Debug,
G: Arbitrary + Debug, H: Arbitrary + Debug> Testable for
fn(A, B, C, D, E, F, G, H) -> T {
#[allow(non_snake_case)]
fn result(&self, g: &mut Gen) -> TestResult {
fn shrink_failure<T: Testable, A: Arbitrary + Debug, B: Arbitrary +
Debug, C: Arbitrary + Debug, D: Arbitrary + Debug, E: Arbitrary +
Debug, F: Arbitrary + Debug, G: Arbitrary + Debug, H: Arbitrary +
Debug>(g: &mut Gen, self_: fn(A, B, C, D, E, F, G, H) -> T,
a: (A, B, C, D, E, F, G, H)) -> Option<TestResult> {
for t in a.shrink() {
let (A, B, C, D, E, F, G, H) = t.clone();
let mut r_new =
safe(move || { self_(A, B, C, D, E, F, G, H) }).result(g);
if r_new.is_failure() {
{
let (ref A, ref B, ref C, ref D, ref E, ref F, ref G,
ref H): (A, B, C, D, E, F, G, H) = t;
r_new.arguments = debug_reprs(&[A, B, C, D, E, F, G, H]);
}
let shrunk = shrink_failure(g, self_, t);
return Some(shrunk.unwrap_or(r_new))
}
}
None
}
let self_ = *self;
let a: (A, B, C, D, E, F, G, H) = Arbitrary::arbitrary(g);
let (A, B, C, D, E, F, G, H) = a.clone();
let mut r = safe(move || { self_(A, B, C, D, E, F, G, H) }).result(g);
{
let (ref A, ref B, ref C, ref D, ref E, ref F, ref G, ref H) = a;
r.arguments = debug_reprs(&[A, B, C, D, E, F, G, H]);
}
match r.status {
Pass | Discard => r,
Fail => { shrink_failure(g, self_, a).unwrap_or(r) }
}
}
}testable_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}