diff --git a/Cargo.toml b/Cargo.toml index e4e3b90..ddb887b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,5 +28,5 @@ name = "quickcheck" [dependencies] env_logger = { version = "0.7.0", default-features = false, optional = true } log = { version = "0.4", optional = true } -rand = "0.7" +rand = { version = "0.7", features = ["small_rng"] } rand_core = "0.5" diff --git a/src/arbitrary.rs b/src/arbitrary.rs index 9333f26..bddcc32 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -22,103 +22,60 @@ use std::sync::Arc; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use rand::seq::SliceRandom; -use rand::{self, Rng, RngCore}; +use rand::{self, Rng, SeedableRng}; -/// `Gen` wraps a `rand::RngCore` with parameters to control the range of -/// random values. +/// Gen represents a PRNG. /// -/// A value with type satisfying the `Gen` trait can be constructed with the -/// `gen` function in this crate. -pub trait Gen: RngCore { - /// Controls the range of values of certain types created with this Gen. - /// For an explaination of which types behave how, see `Arbitrary` - fn size(&self) -> usize; -} - -/// StdGen is the default implementation of `Gen`. +/// It is the source of randomness from which QuickCheck will generate +/// values. An instance of `Gen` is passed to every invocation of +/// `Arbitrary::arbitrary`, which permits callers to use lower level RNG +/// routines to generate values. /// -/// Values of type `StdGen` can be created with the `gen` function in this -/// crate. -pub struct StdGen { - rng: R, +/// It is unspecified whether this is a secure RNG or not. Therefore, callers +/// should assume it is insecure. +pub struct Gen { + rng: rand::rngs::SmallRng, size: usize, } -impl StdGen { - /// Returns a `StdGen` with the given configuration using any random number - /// generator. - /// - /// The `size` parameter controls the size of random values generated. For - /// example, it specifies the maximum length of a randomly generated - /// vector, but not the maximum magnitude of a randomly generated number. - /// For further explaination, see `Arbitrary`. - pub fn new(rng: R, size: usize) -> StdGen { - StdGen { rng: rng, size: size } - } -} - -impl RngCore for StdGen { - fn next_u32(&mut self) -> u32 { - self.rng.next_u32() - } - - // some RNGs implement these more efficiently than the default, so - // we might as well defer to them. - fn next_u64(&mut self) -> u64 { - self.rng.next_u64() - } - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.rng.fill_bytes(dest) - } - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { - self.rng.try_fill_bytes(dest) - } -} - -impl Gen for StdGen { - fn size(&self) -> usize { - self.size - } -} - -/// StdThreadGen is an RNG in thread-local memory. -/// -/// This is the default RNG used by quickcheck. -pub struct StdThreadGen(StdGen); - -impl StdThreadGen { - /// Returns a new thread-local RNG. +impl Gen { + /// Returns a `Gen` with the given size configuration. /// /// The `size` parameter controls the size of random values generated. /// For example, it specifies the maximum length of a randomly generated - /// vector, but not the maximum magnitude of a randomly generated number. - /// For further explaination, see `Arbitrary`. - pub fn new(size: usize) -> StdThreadGen { - StdThreadGen(StdGen { rng: rand::thread_rng(), size: size }) + /// vector, but is and should not be used to control the range of a + /// randomly generated number. (Unless that number is used to control the + /// size of a data structure.) + pub fn new(size: usize) -> Gen { + Gen { rng: rand::rngs::SmallRng::from_entropy(), size: size } } -} -impl RngCore for StdThreadGen { - fn next_u32(&mut self) -> u32 { - self.0.next_u32() + /// Returns the size configured with this generator. + pub fn size(&self) -> usize { + self.size } - // some RNGs implement these more efficiently than the default, so - // we might as well defer to them. - fn next_u64(&mut self) -> u64 { - self.0.next_u64() + /// Choose among the possible alternatives in the slice given. If the slice + /// is empty, then `None` is returned. Otherwise, a non-`None` value is + /// guaranteed to be returned. + pub fn choose<'a, T>(&mut self, slice: &'a [T]) -> Option<&'a T> { + slice.choose(&mut self.rng) } - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.0.fill_bytes(dest) - } - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { - self.0.try_fill_bytes(dest) + + fn gen(&mut self) -> T + where + rand::distributions::Standard: rand::distributions::Distribution, + { + self.rng.gen() } -} -impl Gen for StdThreadGen { - fn size(&self) -> usize { - self.0.size + fn gen_range(&mut self, low: B1, high: B2) -> T + where + T: rand::distributions::uniform::SampleUniform, + B1: rand::distributions::uniform::SampleBorrow + Sized, + B2: rand::distributions::uniform::SampleBorrow + Sized, + { + self.rng.gen_range(low, high) } } @@ -135,32 +92,53 @@ pub fn single_shrinker(value: A) -> Box> { /// `Arbitrary` describes types whose values can be randomly generated and /// shrunk. /// -/// Aside from shrinking, `Arbitrary` is different from the `std::Rand` trait -/// in that it respects `Gen::size()` for certain types. For example, -/// `Vec::arbitrary()` respects `Gen::size()` to decide the maximum `len()` -/// of the vector. This behavior is necessary due to practical speed and size -/// limitations. Conversely, `i32::arbitrary()` ignores `size()`, as all `i32` -/// values require `O(1)` memory and operations between `i32`s require `O(1)` -/// time (with the exception of exponentiation). +/// Aside from shrinking, `Arbitrary` is different from typical RNGs in that +/// it respects `Gen::size()` for controlling how much memory a particular +/// value uses, for practical purposes. For example, `Vec::arbitrary()` +/// respects `Gen::size()` to decide the maximum `len()` of the vector. +/// This behavior is necessary due to practical speed and size limitations. +/// Conversely, `i32::arbitrary()` ignores `size()` since all `i32` values +/// require `O(1)` memory and operations between `i32`s require `O(1)` time +/// (with the exception of exponentiation). /// -/// As of now, all types that implement `Arbitrary` must also implement -/// `Clone`. (I'm not sure if this is a permanent restriction.) +/// Additionally, all types that implement `Arbitrary` must also implement +/// `Clone`. pub trait Arbitrary: Clone + 'static { - fn arbitrary(g: &mut G) -> Self; - + /// Return an arbitrary value. + /// + /// Implementations should respect `Gen::size()` when decisions about how + /// big a particular value should be. Implementations should generally + /// defer to other `Arbitrary` implementations to generate other random + /// values when necessary. The `Gen` type also offers a few RNG helper + /// routines. + fn arbitrary(g: &mut Gen) -> Self; + + /// Return an iterator of values that are smaller than itself. + /// + /// The way in which a value is "smaller" is implementation defined. In + /// some cases, the interpretation is obvious: shrinking an integer should + /// produce integers smaller than itself. Others are more complex, for + /// example, shrinking a `Vec` should both shrink its size and shrink its + /// component values. + /// + /// The iterator returned should be bounded to some reasonable size. + /// + /// It is always correct to return an empty iterator, and indeed, this + /// is the default implementation. The downside of this approach is that + /// witnesses to failures in properties will be more inscrutable. fn shrink(&self) -> Box> { empty_shrinker() } } impl Arbitrary for () { - fn arbitrary(_: &mut G) -> () { + fn arbitrary(_: &mut Gen) -> () { () } } impl Arbitrary for bool { - fn arbitrary(g: &mut G) -> bool { + fn arbitrary(g: &mut Gen) -> bool { g.gen() } @@ -174,7 +152,7 @@ impl Arbitrary for bool { } impl Arbitrary for Option { - fn arbitrary(g: &mut G) -> Option { + fn arbitrary(g: &mut Gen) -> Option { if g.gen() { None } else { @@ -194,7 +172,7 @@ impl Arbitrary for Option { } impl Arbitrary for Result { - fn arbitrary(g: &mut G) -> Result { + fn arbitrary(g: &mut Gen) -> Result { if g.gen() { Ok(Arbitrary::arbitrary(g)) } else { @@ -223,7 +201,7 @@ macro_rules! impl_arb_for_single_tuple { impl<$($type_param),*> Arbitrary for ($($type_param,)*) where $($type_param: Arbitrary,)* { - fn arbitrary(g: &mut GEN) -> ($($type_param,)*) { + fn arbitrary(g: &mut Gen) -> ($($type_param,)*) { ( $( $type_param::arbitrary(g), @@ -272,7 +250,7 @@ impl_arb_for_tuples! { } impl Arbitrary for Vec { - fn arbitrary(g: &mut G) -> Vec { + fn arbitrary(g: &mut Gen) -> Vec { let size = { let s = g.size(); g.gen_range(0, s) @@ -386,7 +364,7 @@ where } impl Arbitrary for BTreeMap { - fn arbitrary(g: &mut G) -> BTreeMap { + fn arbitrary(g: &mut Gen) -> BTreeMap { let vec: Vec<(K, V)> = Arbitrary::arbitrary(g); vec.into_iter().collect() } @@ -405,7 +383,7 @@ impl< S: BuildHasher + Default + Clone + 'static, > Arbitrary for HashMap { - fn arbitrary(g: &mut G) -> Self { + fn arbitrary(g: &mut Gen) -> Self { let vec: Vec<(K, V)> = Arbitrary::arbitrary(g); vec.into_iter().collect() } @@ -417,7 +395,7 @@ impl< } impl Arbitrary for BTreeSet { - fn arbitrary(g: &mut G) -> BTreeSet { + fn arbitrary(g: &mut Gen) -> BTreeSet { let vec: Vec = Arbitrary::arbitrary(g); vec.into_iter().collect() } @@ -429,7 +407,7 @@ impl Arbitrary for BTreeSet { } impl Arbitrary for BinaryHeap { - fn arbitrary(g: &mut G) -> BinaryHeap { + fn arbitrary(g: &mut Gen) -> BinaryHeap { let vec: Vec = Arbitrary::arbitrary(g); vec.into_iter().collect() } @@ -445,7 +423,7 @@ impl Arbitrary for BinaryHeap { impl Arbitrary for HashSet { - fn arbitrary(g: &mut G) -> Self { + fn arbitrary(g: &mut Gen) -> Self { let vec: Vec = Arbitrary::arbitrary(g); vec.into_iter().collect() } @@ -457,7 +435,7 @@ impl } impl Arbitrary for LinkedList { - fn arbitrary(g: &mut G) -> LinkedList { + fn arbitrary(g: &mut Gen) -> LinkedList { let vec: Vec = Arbitrary::arbitrary(g); vec.into_iter().collect() } @@ -471,7 +449,7 @@ impl Arbitrary for LinkedList { } impl Arbitrary for VecDeque { - fn arbitrary(g: &mut G) -> VecDeque { + fn arbitrary(g: &mut Gen) -> VecDeque { let vec: Vec = Arbitrary::arbitrary(g); vec.into_iter().collect() } @@ -483,7 +461,7 @@ impl Arbitrary for VecDeque { } impl Arbitrary for IpAddr { - fn arbitrary(g: &mut G) -> IpAddr { + fn arbitrary(g: &mut Gen) -> IpAddr { let ipv4: bool = g.gen(); if ipv4 { IpAddr::V4(Arbitrary::arbitrary(g)) @@ -494,13 +472,13 @@ impl Arbitrary for IpAddr { } impl Arbitrary for Ipv4Addr { - fn arbitrary(g: &mut G) -> Ipv4Addr { + fn arbitrary(g: &mut Gen) -> Ipv4Addr { Ipv4Addr::new(g.gen(), g.gen(), g.gen(), g.gen()) } } impl Arbitrary for Ipv6Addr { - fn arbitrary(g: &mut G) -> Ipv6Addr { + fn arbitrary(g: &mut Gen) -> Ipv6Addr { Ipv6Addr::new( g.gen(), g.gen(), @@ -515,25 +493,25 @@ impl Arbitrary for Ipv6Addr { } impl Arbitrary for SocketAddr { - fn arbitrary(g: &mut G) -> SocketAddr { + fn arbitrary(g: &mut Gen) -> SocketAddr { SocketAddr::new(Arbitrary::arbitrary(g), g.gen()) } } impl Arbitrary for SocketAddrV4 { - fn arbitrary(g: &mut G) -> SocketAddrV4 { + fn arbitrary(g: &mut Gen) -> SocketAddrV4 { SocketAddrV4::new(Arbitrary::arbitrary(g), g.gen()) } } impl Arbitrary for SocketAddrV6 { - fn arbitrary(g: &mut G) -> SocketAddrV6 { + fn arbitrary(g: &mut Gen) -> SocketAddrV6 { SocketAddrV6::new(Arbitrary::arbitrary(g), g.gen(), g.gen(), g.gen()) } } impl Arbitrary for PathBuf { - fn arbitrary(g: &mut G) -> PathBuf { + fn arbitrary(g: &mut Gen) -> PathBuf { // use some real directories as guesses, so we may end up with // actual working directories in case that is relevant. let here = @@ -541,16 +519,18 @@ impl Arbitrary for PathBuf { let temp = env::temp_dir(); #[allow(deprecated)] let home = env::home_dir().unwrap_or(PathBuf::from("/home/user")); - let choices = &[ - here, - temp, - home, - PathBuf::from("."), - PathBuf::from(".."), - PathBuf::from("../../.."), - PathBuf::new(), - ]; - let mut p = choices.choose(g).unwrap().clone(); + let mut p = g + .choose(&[ + here, + temp, + home, + PathBuf::from("."), + PathBuf::from(".."), + PathBuf::from("../../.."), + PathBuf::new(), + ]) + .unwrap() + .to_owned(); p.extend(Vec::::arbitrary(g).iter()); p } @@ -582,7 +562,7 @@ impl Arbitrary for PathBuf { } impl Arbitrary for OsString { - fn arbitrary(g: &mut G) -> OsString { + fn arbitrary(g: &mut Gen) -> OsString { OsString::from(String::arbitrary(g)) } @@ -593,7 +573,7 @@ impl Arbitrary for OsString { } impl Arbitrary for String { - fn arbitrary(g: &mut G) -> String { + fn arbitrary(g: &mut Gen) -> String { let size = { let s = g.size(); g.gen_range(0, s) @@ -609,7 +589,7 @@ impl Arbitrary for String { } impl Arbitrary for CString { - fn arbitrary(g: &mut G) -> Self { + fn arbitrary(g: &mut Gen) -> Self { let size = { let s = g.size(); g.gen_range(0, s) @@ -649,7 +629,7 @@ impl Arbitrary for CString { } impl Arbitrary for char { - fn arbitrary(g: &mut G) -> char { + fn arbitrary(g: &mut Gen) -> char { let mode = g.gen_range(0, 100); match mode { 0..=49 => { @@ -667,20 +647,19 @@ impl Arbitrary for char { } 60..=84 => { // Characters often used in programming languages - [ + g.choose(&[ ' ', ' ', ' ', '\t', '\n', '~', '`', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '-', '=', '+', '[', ']', '{', '}', ':', ';', '\'', '"', '\\', '|', ',', '<', '>', '.', '/', '?', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - ] - .choose(g) + ]) .unwrap() .to_owned() } 85..=89 => { // Tricky Unicode, part 1 - [ + g.choose(&[ '\u{0149}', // a deprecated character '\u{fff0}', // some of "Other, format" category: '\u{fff1}', @@ -730,8 +709,7 @@ impl Arbitrary for char { '\u{1680}', // other space characters are already covered by two next // branches - ] - .choose(g) + ]) .unwrap() .to_owned() } @@ -792,7 +770,7 @@ macro_rules! unsigned_shrinker { macro_rules! unsigned_problem_values { ($t:ty) => { - [<$t>::min_value(), 1, <$t>::max_value()] + &[<$t>::min_value(), 1, <$t>::max_value()] }; } @@ -800,10 +778,10 @@ macro_rules! unsigned_arbitrary { ($($ty:tt),*) => { $( impl Arbitrary for $ty { - fn arbitrary(g: &mut G) -> $ty { + fn arbitrary(g: &mut Gen) -> $ty { match g.gen_range(0, 10) { 0 => { - *unsigned_problem_values!($ty).choose(g).unwrap() + *g.choose(unsigned_problem_values!($ty)).unwrap() }, _ => g.gen() } @@ -862,7 +840,7 @@ macro_rules! signed_shrinker { macro_rules! signed_problem_values { ($t:ty) => { - [<$t>::min_value(), 0, <$t>::max_value()] + &[<$t>::min_value(), 0, <$t>::max_value()] }; } @@ -870,10 +848,10 @@ macro_rules! signed_arbitrary { ($($ty:tt),*) => { $( impl Arbitrary for $ty { - fn arbitrary(g: &mut G) -> $ty { + fn arbitrary(g: &mut Gen) -> $ty { match g.gen_range(0, 10) { 0 => { - *signed_problem_values!($ty).choose(g).unwrap() + *g.choose(signed_problem_values!($ty)).unwrap() }, _ => g.gen() } @@ -895,21 +873,21 @@ macro_rules! float_problem_values { ($path:path) => {{ // hack. see: https://github.com/rust-lang/rust/issues/48067 use $path as p; - [p::NAN, p::NEG_INFINITY, p::MIN, -0., 0., p::MAX, p::INFINITY] + &[p::NAN, p::NEG_INFINITY, p::MIN, -0., 0., p::MAX, p::INFINITY] }}; } macro_rules! float_arbitrary { ($($t:ty, $path:path, $shrinkable:ty),+) => {$( impl Arbitrary for $t { - fn arbitrary(g: &mut G) -> $t { + fn arbitrary(g: &mut Gen) -> $t { match g.gen_range(0, 10) { - 0 => *float_problem_values!($path).choose(g).unwrap(), + 0 => *g.choose(float_problem_values!($path)).unwrap(), _ => { use $path as p; let exp = g.gen_range(0., p::MAX_EXP as i16 as $t); let mantissa = g.gen_range(1., 2.); - let sign = *[-1., 1.].choose(g).unwrap(); + let sign = *g.choose(&[-1., 1.]).unwrap(); sign * mantissa * exp.exp2() } } @@ -970,7 +948,7 @@ macro_rules! unsigned_non_zero_arbitrary { ($($ty:tt => $inner:tt),*) => { $( impl Arbitrary for $ty { - fn arbitrary(g: &mut G) -> $ty { + fn arbitrary(g: &mut Gen) -> $ty { let mut v: $inner = g.gen(); if v == 0 { v += 1; @@ -999,7 +977,7 @@ unsigned_non_zero_arbitrary! { } impl Arbitrary for Wrapping { - fn arbitrary(g: &mut G) -> Wrapping { + fn arbitrary(g: &mut Gen) -> Wrapping { Wrapping(T::arbitrary(g)) } fn shrink(&self) -> Box>> { @@ -1008,7 +986,7 @@ impl Arbitrary for Wrapping { } impl Arbitrary for Bound { - fn arbitrary(g: &mut G) -> Bound { + fn arbitrary(g: &mut Gen) -> Bound { match g.gen_range(0, 3) { 0 => Bound::Included(T::arbitrary(g)), 1 => Bound::Excluded(T::arbitrary(g)), @@ -1029,7 +1007,7 @@ impl Arbitrary for Bound { } impl Arbitrary for Range { - fn arbitrary(g: &mut G) -> Range { + fn arbitrary(g: &mut Gen) -> Range { Arbitrary::arbitrary(g)..Arbitrary::arbitrary(g) } fn shrink(&self) -> Box>> { @@ -1040,7 +1018,7 @@ impl Arbitrary for Range { } impl Arbitrary for RangeInclusive { - fn arbitrary(g: &mut G) -> RangeInclusive { + fn arbitrary(g: &mut Gen) -> RangeInclusive { Arbitrary::arbitrary(g)..=Arbitrary::arbitrary(g) } fn shrink(&self) -> Box>> { @@ -1053,7 +1031,7 @@ impl Arbitrary for RangeInclusive { } impl Arbitrary for RangeFrom { - fn arbitrary(g: &mut G) -> RangeFrom { + fn arbitrary(g: &mut Gen) -> RangeFrom { Arbitrary::arbitrary(g).. } fn shrink(&self) -> Box>> { @@ -1062,7 +1040,7 @@ impl Arbitrary for RangeFrom { } impl Arbitrary for RangeTo { - fn arbitrary(g: &mut G) -> RangeTo { + fn arbitrary(g: &mut Gen) -> RangeTo { ..Arbitrary::arbitrary(g) } fn shrink(&self) -> Box>> { @@ -1071,7 +1049,7 @@ impl Arbitrary for RangeTo { } impl Arbitrary for RangeToInclusive { - fn arbitrary(g: &mut G) -> RangeToInclusive { + fn arbitrary(g: &mut Gen) -> RangeToInclusive { ..=Arbitrary::arbitrary(g) } fn shrink(&self) -> Box>> { @@ -1080,13 +1058,13 @@ impl Arbitrary for RangeToInclusive { } impl Arbitrary for RangeFull { - fn arbitrary(_: &mut G) -> RangeFull { + fn arbitrary(_: &mut Gen) -> RangeFull { .. } } impl Arbitrary for Duration { - fn arbitrary(gen: &mut G) -> Self { + fn arbitrary(gen: &mut Gen) -> Self { let seconds = gen.gen_range(0, gen.size() as u64); let nanoseconds = gen.gen_range(0, 1_000_000); Duration::new(seconds, nanoseconds) @@ -1102,7 +1080,7 @@ impl Arbitrary for Duration { } impl Arbitrary for Box { - fn arbitrary(g: &mut G) -> Box { + fn arbitrary(g: &mut Gen) -> Box { Box::new(A::arbitrary(g)) } @@ -1112,7 +1090,7 @@ impl Arbitrary for Box { } impl Arbitrary for Arc { - fn arbitrary(g: &mut G) -> Arc { + fn arbitrary(g: &mut Gen) -> Arc { Arc::new(A::arbitrary(g)) } @@ -1122,7 +1100,7 @@ impl Arbitrary for Arc { } impl Arbitrary for SystemTime { - fn arbitrary(gen: &mut G) -> Self { + fn arbitrary(gen: &mut Gen) -> Self { let after_epoch = bool::arbitrary(gen); let duration = Duration::arbitrary(gen); if after_epoch { @@ -1147,9 +1125,6 @@ impl Arbitrary for SystemTime { #[cfg(test)] mod test { - use super::Arbitrary; - use super::StdGen; - use rand; use std::collections::{ BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque, }; @@ -1158,6 +1133,8 @@ mod test { use std::num::Wrapping; use std::path::PathBuf; + use super::{Arbitrary, Gen}; + #[test] fn arby_unit() { assert_eq!(arby::<()>(), ()); @@ -1243,7 +1220,7 @@ mod test { } fn arby() -> A { - Arbitrary::arbitrary(&mut StdGen::new(rand::thread_rng(), 5)) + Arbitrary::arbitrary(&mut Gen::new(5)) } // Shrink testing. diff --git a/src/lib.rs b/src/lib.rs index 07f73e4..b3016a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,11 +14,8 @@ extern crate log; extern crate rand; extern crate rand_core; -pub use crate::arbitrary::{ - empty_shrinker, single_shrinker, Arbitrary, Gen, StdGen, StdThreadGen, -}; +pub use crate::arbitrary::{empty_shrinker, single_shrinker, Arbitrary, Gen}; pub use crate::tester::{quickcheck, QuickCheck, TestResult, Testable}; -pub use rand_core::RngCore; /// A macro for writing quickcheck tests. /// diff --git a/src/tester.rs b/src/tester.rs index 491cb48..cb6ef2e 100644 --- a/src/tester.rs +++ b/src/tester.rs @@ -3,15 +3,17 @@ use std::env; use std::fmt::Debug; use std::panic; -use crate::tester::Status::{Discard, Fail, Pass}; -use crate::{Arbitrary, Gen, StdThreadGen}; +use crate::{ + tester::Status::{Discard, Fail, Pass}, + Arbitrary, Gen, +}; /// The main QuickCheck type for setting configuration and running QuickCheck. -pub struct QuickCheck { +pub struct QuickCheck { tests: u64, max_tests: u64, min_tests_passed: u64, - gen: G, + gen: Gen, } fn qc_tests() -> u64 { @@ -46,71 +48,56 @@ fn qc_min_tests_passed() -> u64 { } } -impl QuickCheck { +impl QuickCheck { /// Creates a new QuickCheck value. /// - /// This can be used to run QuickCheck on things that implement - /// `Testable`. You may also adjust the configuration, such as - /// the number of tests to run. + /// This can be used to run QuickCheck on things that implement `Testable`. + /// You may also adjust the configuration, such as the number of tests to + /// run. /// - /// By default, the maximum number of passed tests is set to `100`, - /// the max number of overall tests is set to `10000` and the generator - /// is set to a `StdThreadGen` with a default size of `100`. - pub fn new() -> QuickCheck { - let gen_size = qc_gen_size(); - QuickCheck::with_gen(StdThreadGen::new(gen_size)) + /// By default, the maximum number of passed tests is set to `100`, the max + /// number of overall tests is set to `10000` and the generator is created + /// with a size of `100`. + pub fn new() -> QuickCheck { + let gen = Gen::new(qc_gen_size()); + let tests = qc_tests(); + let max_tests = cmp::max(tests, qc_max_tests()); + let min_tests_passed = qc_min_tests_passed(); + + QuickCheck { tests, max_tests, min_tests_passed, gen } + } + + /// Set the random number generator to be used by QuickCheck. + pub fn gen(self, gen: Gen) -> QuickCheck { + QuickCheck { gen, ..self } } -} -impl QuickCheck { /// Set the number of tests to run. /// /// This actually refers to the maximum number of *passed* tests that /// can occur. Namely, if a test causes a failure, future testing on that /// property stops. Additionally, if tests are discarded, there may be /// fewer than `tests` passed. - pub fn tests(mut self, tests: u64) -> QuickCheck { + pub fn tests(mut self, tests: u64) -> QuickCheck { self.tests = tests; self } - /// Create a new instance of `QuickCheck` using the given generator. - pub fn with_gen(generator: G) -> QuickCheck { - let tests = qc_tests(); - let max_tests = cmp::max(tests, qc_max_tests()); - let min_tests_passed = qc_min_tests_passed(); - - QuickCheck { - tests: tests, - max_tests: max_tests, - min_tests_passed: min_tests_passed, - gen: generator, - } - } - /// Set the maximum number of tests to run. /// /// The number of invocations of a property will never exceed this number. /// This is necessary to cap the number of tests because QuickCheck /// properties can discard tests. - pub fn max_tests(mut self, max_tests: u64) -> QuickCheck { + pub fn max_tests(mut self, max_tests: u64) -> QuickCheck { self.max_tests = max_tests; self } - /// Set the random number generator to be used by QuickCheck. - pub fn gen(self, gen: N) -> QuickCheck { - // unfortunately this is necessary because using QuickCheck{ ..self, gen } - // wouldn't work due to mismatched types. - let QuickCheck { tests, max_tests, min_tests_passed, .. } = self; - QuickCheck { tests, max_tests, min_tests_passed, gen } - } - /// Set the minimum number of tests that needs to pass. /// /// This actually refers to the minimum number of *valid* *passed* tests /// that needs to pass for the property to be considered successful. - pub fn min_tests_passed(mut self, min_tests_passed: u64) -> QuickCheck { + pub fn min_tests_passed(mut self, min_tests_passed: u64) -> QuickCheck { self.min_tests_passed = min_tests_passed; self } @@ -306,23 +293,23 @@ impl TestResult { /// /// It's unlikely that you'll have to implement this trait yourself. pub trait Testable: 'static { - fn result(&self, _: &mut G) -> TestResult; + fn result(&self, _: &mut Gen) -> TestResult; } impl Testable for bool { - fn result(&self, _: &mut G) -> TestResult { + fn result(&self, _: &mut Gen) -> TestResult { TestResult::from_bool(*self) } } impl Testable for () { - fn result(&self, _: &mut G) -> TestResult { + fn result(&self, _: &mut Gen) -> TestResult { TestResult::passed() } } impl Testable for TestResult { - fn result(&self, _: &mut G) -> TestResult { + fn result(&self, _: &mut Gen) -> TestResult { self.clone() } } @@ -332,7 +319,7 @@ where A: Testable, E: Debug + 'static, { - fn result(&self, g: &mut G) -> TestResult { + fn result(&self, g: &mut Gen) -> TestResult { match *self { Ok(ref r) => r.result(g), Err(ref err) => TestResult::error(format!("{:?}", err)), @@ -351,9 +338,9 @@ macro_rules! testable_fn { impl Testable for fn($($name),*) -> T { #[allow(non_snake_case)] - fn result(&self, g: &mut G_) -> TestResult { - fn shrink_failure( - g: &mut G_, + fn result(&self, g: &mut Gen) -> TestResult { + fn shrink_failure( + g: &mut Gen, self_: fn($($name),*) -> T, a: ($($name,)*), ) -> Option { @@ -431,8 +418,7 @@ impl AShow for A {} #[cfg(test)] mod test { - use crate::{QuickCheck, StdGen}; - use rand::{self, rngs::OsRng}; + use crate::{Gen, QuickCheck}; #[test] fn shrinking_regression_issue_126() { @@ -445,26 +431,11 @@ mod test { let expected_argument = format!("{:?}", [true, true]); assert_eq!(failing_case.arguments, vec![expected_argument]); } - #[test] fn size_for_small_types_issue_143() { fn t(_: i8) -> bool { true } - QuickCheck::new() - .gen(StdGen::new(rand::thread_rng(), 129)) - .quickcheck(t as fn(i8) -> bool); - } - - #[test] - fn different_generator() { - fn prop(_: i32) -> bool { - true - } - QuickCheck::with_gen(StdGen::new(OsRng, 129)) - .quickcheck(prop as fn(i32) -> bool); - QuickCheck::new() - .gen(StdGen::new(OsRng, 129)) - .quickcheck(prop as fn(i32) -> bool); + QuickCheck::new().gen(Gen::new(129)).quickcheck(t as fn(i8) -> bool); } } diff --git a/src/tests.rs b/src/tests.rs index 5a02a3f..465ef15 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -5,9 +5,7 @@ use std::ffi::CString; use std::hash::BuildHasherDefault; use std::path::PathBuf; -use rand; - -use super::{quickcheck, QuickCheck, StdGen, TestResult}; +use super::{quickcheck, Gen, QuickCheck, TestResult}; #[test] fn prop_oob() { @@ -182,9 +180,7 @@ fn regression_issue_83() { fn prop(_: u8) -> bool { true } - QuickCheck::new() - .gen(StdGen::new(rand::thread_rng(), 1024)) - .quickcheck(prop as fn(u8) -> bool) + QuickCheck::new().gen(Gen::new(1024)).quickcheck(prop as fn(u8) -> bool) } #[test] @@ -192,9 +188,7 @@ fn regression_issue_83_signed() { fn prop(_: i8) -> bool { true } - QuickCheck::new() - .gen(StdGen::new(rand::thread_rng(), 1024)) - .quickcheck(prop as fn(i8) -> bool) + QuickCheck::new().gen(Gen::new(1024)).quickcheck(prop as fn(i8) -> bool) } // Test that we can show the message after panic