1use std::fmt::Debug;
2use std::panic;
3
4use rand;
5
6use tester::Status::{Discard, Fail, Pass};
7use {Arbitrary, Gen, StdGen};
8
9pub struct QuickCheck<G> {
11 pub tests: usize,
12 pub max_tests: usize,
13 pub gen: G,
14}
15
16impl QuickCheck<StdGen<rand::ThreadRng>> {
17 pub fn new() -> QuickCheck<StdGen<rand::ThreadRng>> {
27 QuickCheck {
28 tests: 100,
29 max_tests: 10000,
30 gen: StdGen::new(rand::thread_rng(), 100),
31 }
32 }
33}
34
35impl<G: Gen> QuickCheck<G> {
36 pub fn tests(mut self, tests: usize) -> QuickCheck<G> {
43 self.tests = tests;
44 self
45 }
46
47 pub fn max_tests(mut self, max_tests: usize) -> QuickCheck<G> {
53 self.max_tests = max_tests;
54 self
55 }
56
57 pub fn gen(mut self, gen: G) -> QuickCheck<G> {
59 self.gen = gen;
60 self
61 }
62
63 pub fn quicktest<A>(&mut self, f: A) -> Result<usize, TestResult>
71 where A: Testable {
72 let mut ntests: usize = 0;
73 for _ in 0..self.max_tests {
74 if ntests >= self.tests {
75 break
76 }
77 match f.result(&mut self.gen) {
78 TestResult { status: Pass, .. } => ntests += 1,
79 TestResult { status: Discard, .. } => continue,
80 r @ TestResult { status: Fail, .. } => return Err(r),
81 }
82 }
83 Ok(ntests)
84 }
85
86 pub fn quickcheck<A>(&mut self, f: A) where A: Testable {
113 let _ = ::env_logger::init();
115
116 match self.quicktest(f) {
117 Ok(ntests) => info!("(Passed {} QuickCheck tests.)", ntests),
118 Err(result) => panic!(result.failed_msg()),
119 }
120 }
121}
122
123pub fn quickcheck<A: Testable>(f: A) { QuickCheck::new().quickcheck(f) }
127
128#[derive(Clone, Debug)]
132pub struct TestResult {
133 status: Status,
134 arguments: Vec<String>,
135 err: Option<String>,
136}
137
138#[derive(Clone, Debug)]
140enum Status { Pass, Fail, Discard }
141
142impl TestResult {
143 pub fn passed() -> TestResult { TestResult::from_bool(true) }
145
146 pub fn failed() -> TestResult { TestResult::from_bool(false) }
148
149 pub fn error<S: Into<String>>(msg: S) -> TestResult {
151 let mut r = TestResult::from_bool(false);
152 r.err = Some(msg.into());
153 r
154 }
155
156 pub fn discard() -> TestResult {
161 TestResult {
162 status: Discard,
163 arguments: vec![],
164 err: None,
165 }
166 }
167
168 pub fn from_bool(b: bool) -> TestResult {
172 TestResult {
173 status: if b { Pass } else { Fail },
174 arguments: vec![],
175 err: None,
176 }
177 }
178
179 pub fn must_fail<T, F>(f: F) -> TestResult
182 where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static {
183 let f = panic::AssertUnwindSafe(f);
184 TestResult::from_bool(panic::catch_unwind(f).is_err())
185 }
186
187 pub fn is_failure(&self) -> bool {
190 match self.status {
191 Fail => true,
192 Pass|Discard => false,
193 }
194 }
195
196 pub fn is_error(&self) -> bool {
199 self.is_failure() && self.err.is_some()
200 }
201
202 fn failed_msg(&self) -> String {
203 match self.err {
204 None => {
205 format!("[quickcheck] TEST FAILED. Arguments: ({})",
206 self.arguments.connect(", "))
207 }
208 Some(ref err) => {
209 format!("[quickcheck] TEST FAILED (runtime error). \
210 Arguments: ({})\nError: {}",
211 self.arguments.connect(", "), err)
212 }
213 }
214 }
215}
216
217pub trait Testable : Send + 'static {
229 fn result<G: Gen>(&self, &mut G) -> TestResult;
230}
231
232impl Testable for bool {
233 fn result<G: Gen>(&self, _: &mut G) -> TestResult {
234 TestResult::from_bool(*self)
235 }
236}
237
238impl Testable for () {
239 fn result<G: Gen>(&self, _: &mut G) -> TestResult {
240 TestResult::passed()
241 }
242}
243
244impl Testable for TestResult {
245 fn result<G: Gen>(&self, _: &mut G) -> TestResult { self.clone() }
246}
247
248impl<A, E> Testable for Result<A, E>
249 where A: Testable, E: Debug + Send + 'static {
250 fn result<G: Gen>(&self, g: &mut G) -> TestResult {
251 match *self {
252 Ok(ref r) => r.result(g),
253 Err(ref err) => TestResult::error(format!("{:?}", err)),
254 }
255 }
256}
257
258macro_rules! testable_fn {
259 ($($name: ident),*) => {
260
261impl<T: Testable,
262 $($name: Arbitrary + Debug),*> Testable for fn($($name),*) -> T {
263 #[allow(non_snake_case)]
264 fn result<G_: Gen>(&self, g: &mut G_) -> TestResult {
265 fn shrink_failure<T: Testable, G_: Gen, $($name: Arbitrary + Debug),*>(
266 g: &mut G_,
267 self_: fn($($name),*) -> T,
268 a: ($($name,)*),
269 ) -> Option<TestResult> {
270 for t in a.shrink() {
271 let ($($name,)*) = t.clone();
272 let mut r_new = safe(move || {self_($($name),*)}).result(g);
273 if r_new.is_failure() {
274 let ($($name,)*) : ($($name,)*) = t.clone();
275 r_new.arguments = vec![$(format!("{:?}", $name),)*];
276
277 let shrunk = shrink_failure(g, self_, t);
280
281 return Some(shrunk.unwrap_or(r_new))
284 }
285 }
286 None
287 }
288
289 let self_ = *self;
290 let a: ($($name,)*) = Arbitrary::arbitrary(g);
291 let ( $($name,)* ) = a.clone();
292 let mut r = safe(move || {self_($($name),*)}).result(g);
293
294 let ( $($name,)* ) = a.clone();
295 r.arguments = vec![$(format!("{:?}", $name),)*];
296 match r.status {
297 Pass|Discard => r,
298 Fail => {
299 shrink_failure(g, self_, a).unwrap_or(r)
300 }
301 }
302 }
303}}}
304
305testable_fn!();
306testable_fn!(A);
307testable_fn!(A, B);
308testable_fn!(A, B, C);
309testable_fn!(A, B, C, D);
310testable_fn!(A, B, C, D, E);
311testable_fn!(A, B, C, D, E, F);
312testable_fn!(A, B, C, D, E, F, G);
313testable_fn!(A, B, C, D, E, F, G, H);
314testable_fn!(A, B, C, D, E, F, G, H, I);
315testable_fn!(A, B, C, D, E, F, G, H, I, J);
316testable_fn!(A, B, C, D, E, F, G, H, I, J, K);
317testable_fn!(A, B, C, D, E, F, G, H, I, J, K, L);
318
319fn safe<T, F>(fun: F) -> Result<T, String>
320 where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static {
321 panic::catch_unwind(panic::AssertUnwindSafe(fun)).map_err(|any_err| {
322 if let Some(&s) = any_err.downcast_ref::<&str>() {
325 s.to_owned()
326 } else if let Some(s) = any_err.downcast_ref::<String>() {
327 s.to_owned()
328 } else {
329 "UNABLE TO SHOW RESULT OF PANIC.".to_owned()
330 }
331 })
332}
333
334trait AShow : Arbitrary + Debug {}
336impl<A: Arbitrary + Debug> AShow for A {}
337
338#[cfg(test)]
339mod test {
340 use QuickCheck;
341
342 #[test]
343 fn shrinking_regression_issue_126() {
344 fn thetest(vals: Vec<bool>) -> bool {
345 vals.iter().filter(|&v| *v).count() < 2
346 }
347 let failing_case =
348 QuickCheck::new()
349 .quicktest(thetest as fn(vals: Vec<bool>) -> bool)
350 .unwrap_err();
351 let expected_argument = format!("{:?}", [true, true]);
352 assert_eq!(failing_case.arguments, vec![expected_argument]);
353 }
354}