1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use std::fmt;
use std::panic;
use data::*;
use generators::*;
const NUM_TESTS: usize = 100;
const MAX_SKIPS: usize = NUM_TESTS * 10;
const DEFAULT_POOL_SIZE: usize = 1024;
pub struct Property<G> {
gen: G,
}
pub trait CheckResult {
fn is_failure(&self) -> bool;
}
pub fn property<G>(gen: G) -> Property<G> {
Property { gen: gen }
}
impl<G: Generator> Property<G>
where
G::Item: fmt::Debug,
{
pub fn check<R: CheckResult + fmt::Debug, F: Fn(G::Item) -> R>(self, subject: F) {
let mut tests_run = 0usize;
let mut items_skipped = 0usize;
while tests_run < NUM_TESTS {
let pool = InfoPool::random_of_size(DEFAULT_POOL_SIZE);
trace!("Tests run: {}; skipped:{}", tests_run, items_skipped);
match self.gen.generate(&mut pool.tap()) {
Ok(arg) => {
let res = Self::attempt(&subject, arg);
trace!(
"Result: {:?} -> {:?}",
self.gen.generate(&mut pool.tap()),
res
);
tests_run += 1;
if res.is_failure() {
let minpool = find_minimal(
&self.gen,
pool,
|v| Self::attempt(&subject, v).is_failure(),
);
panic!(
"Predicate failed for argument {:?}; check returned {:?}",
self.gen.generate(&mut minpool.tap()),
res
)
}
}
Err(DataError::SkipItem) => {
trace!("Skip: {:?}", self.gen.generate(&mut pool.tap()));
items_skipped += 1;
if items_skipped >= MAX_SKIPS {
panic!(
"Could not finish on {}/{} tests (have skipped {} times)",
tests_run,
NUM_TESTS,
items_skipped
);
}
}
Err(e) => {
trace!("Gen failure: {:?}", self.gen.generate(&mut pool.tap()));
debug!("{:?}", e);
}
}
}
trace!("Completing okay");
}
fn attempt<R: CheckResult, F: Fn(G::Item) -> R>(subject: F, arg: G::Item) -> Result<R, String> {
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| subject(arg)));
match res {
Ok(r) => Ok(r),
Err(err) => {
let msg = if let Some(s) = err.downcast_ref::<&str>() {
s.to_string()
} else if let Some(s) = err.downcast_ref::<String>() {
s.to_string()
} else {
format!("Unrecognised panic result: {:?}", err)
};
Err(msg)
}
}
}
}
impl CheckResult for bool {
fn is_failure(&self) -> bool {
!self
}
}
impl<O: CheckResult, E> CheckResult for Result<O, E> {
fn is_failure(&self) -> bool {
self.as_ref().map(|r| r.is_failure()).unwrap_or(true)
}
}
impl CheckResult for () {
fn is_failure(&self) -> bool {
false
}
}