From 6df156c92d9a372c514f67c84d06596e336411ea Mon Sep 17 00:00:00 2001 From: Andrew Gallant Date: Sun, 27 Dec 2020 14:17:57 -0500 Subject: [PATCH] rand: remove rand as a public dependency This removes the use of the rand_core crate as a public dependency. It is now an implementation detail. We achieve this primarily by turning the `Gen` trait into a concrete type and fixing the fallout. This does make it impossible for callers to use their own `Gen` implementations, but it's unclear how often this was being used (if at all). This does also limit the number of RNG utility routines that callers have easy access to. However, it should be possible to use rand's `SeedableRng::from_{rng,seed}` routines to get access to more general RNGs. Closes #241 --- Cargo.toml | 2 +- src/arbitrary.rs | 295 ++++++++++++++++++++++------------------------- src/lib.rs | 5 +- src/tester.rs | 105 ++++++----------- src/tests.rs | 12 +- 5 files changed, 179 insertions(+), 240 deletions(-) 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