From 2d0a50b13fdcc92a271162e1f7c94509d34d1a9c Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Mon, 19 Aug 2019 02:39:58 -0700 Subject: [PATCH 01/12] make arbitrary generate full range of integers --- src/arbitrary.rs | 100 ++++++++++++++++++++++++++++++----------------- src/tests.rs | 11 +++--- 2 files changed, 70 insertions(+), 41 deletions(-) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index 8c40612..6e9dc40 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -681,15 +681,23 @@ macro_rules! unsigned_shrinker { } } +macro_rules! unsigned_problem_values { + ($t:ty) => { + [<$t>::min_value(), 1, <$t>::max_value()] + }; +} + macro_rules! unsigned_arbitrary { ($($ty:tt),*) => { $( impl Arbitrary for $ty { fn arbitrary(g: &mut G) -> $ty { - #![allow(trivial_numeric_casts)] - let s = g.size() as $ty; - use std::cmp::{min, max}; - g.gen_range(0, max(1, min(s, $ty::max_value()))) + match g.gen_range(0,10) { + 0 => { + *unsigned_problem_values!($ty).choose(g).unwrap() + }, + _ => g.gen_range($ty::min_value(), $ty::max_value()) + } } fn shrink(&self) -> Box> { unsigned_shrinker!($ty); @@ -701,11 +709,7 @@ macro_rules! unsigned_arbitrary { } unsigned_arbitrary! { - usize, u8, u16, u32, u64 -} - -unsigned_arbitrary! { - u128 + usize, u8, u16, u32, u64, u128 } macro_rules! signed_shrinker { @@ -750,19 +754,23 @@ macro_rules! signed_shrinker { } } +macro_rules! signed_problem_values { + ($t:ty) => { + [<$t>::min_value(), 0, <$t>::max_value()] + }; +} + macro_rules! signed_arbitrary { ($($ty:tt),*) => { $( impl Arbitrary for $ty { fn arbitrary(g: &mut G) -> $ty { - use std::cmp::{min,max}; - let upper = min(g.size(), $ty::max_value() as usize); - let lower = if upper > $ty::max_value() as usize { - $ty::min_value() - } else { - -(upper as $ty) - }; - g.gen_range(lower, max(1, upper as $ty)) + match g.gen_range(0,10) { + 0 => { + *signed_problem_values!($ty).choose(g).unwrap() + }, + _ => g.gen_range($ty::min_value(), $ty::max_value()) + } } fn shrink(&self) -> Box> { signed_shrinker!($ty); @@ -774,11 +782,7 @@ macro_rules! signed_arbitrary { } signed_arbitrary! { - isize, i8, i16, i32, i64 -} - -signed_arbitrary! { - i128 + isize, i8, i16, i32, i64, i128 } impl Arbitrary for f32 { @@ -936,34 +940,58 @@ mod test { use std::num::Wrapping; use std::path::PathBuf; use super::Arbitrary; + use super::StdGen; #[test] fn arby_unit() { assert_eq!(arby::<()>(), ()); } + macro_rules! arby_int { + ($($t:ty),+) => {$( + let mut gen = super::StdGen::new(rand::thread_rng(), 100); + // 1/10 chance of hitting problematic value * 10000 random values + // => ~1000 problematic values + // => probability of failure ~ (2/3)^(1000) ~ 10e-170 + let arbys:Vec<$t> = (0..10000) + .map(|_|{Arbitrary::arbitrary(& mut gen)}) + .collect(); + if !arbys.iter().any(|&x| x==<$t>::max_value()) { + panic!("Arbitrary does not generate max value") + } + if !arbys.iter().any(|&x| x==<$t>::min_value()) { + panic!("Arbitrary does not generate min value") + } + //filter out problematic values + let arbys:Vec<$t> = arbys + .into_iter() + .filter(|x| unsigned_problem_values!($t).iter().any(|y| y==x)) + .collect(); + // 9/10 chance of hitting non-problematic value * 10000 random values + // => ~9000 values + // => probability of failure ~ (19/20)^(9000) ~ 10e-200 + if <$t>::max_value()/20 * 19 > *arbys.iter().max().unwrap_or(&0) { + panic!("Arbitrary did not generate within 5% of maximum value") + } + if <$t>::min_value() + <$t>::max_value()/20 + < *arbys.iter().min().unwrap_or(&<$t>::max_value()) { + panic!("Arbitrary did not generate within 5% of minimum value") + } + )*}; + } + #[test] fn arby_int() { - rep(&mut || { let n: isize = arby(); assert!(n >= -5 && n <= 5); } ); + arby_int!(i8, i16, i32, i64, isize, i128); } #[test] fn arby_uint() { - rep(&mut || { let n: usize = arby(); assert!(n <= 5); } ); - } - - fn arby() -> A { - super::Arbitrary::arbitrary(&mut gen()) + arby_int!(u8, u16, u32, u64, usize, u128); } - fn gen() -> super::StdGen { - super::StdGen::new(rand::thread_rng(), 5) - } - - fn rep(f: &mut F) where F : FnMut() -> () { - for _ in 0..100 { - f() - } + fn arby() -> A { + Arbitrary::arbitrary(&mut StdGen::new(rand::thread_rng(), 5)) } // Shrink testing. diff --git a/src/tests.rs b/src/tests.rs index 760ebea..0cb71b1 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -129,19 +129,20 @@ fn is_prime(n: usize) -> bool { #[test] #[should_panic] fn sieve_not_prime() { - fn prop_all_prime(n: usize) -> bool { - sieve(n).into_iter().all(is_prime) + fn prop_all_prime(n: u8) -> bool { + sieve(n as usize).into_iter().all(is_prime) } - quickcheck(prop_all_prime as fn(usize) -> bool); + quickcheck(prop_all_prime as fn(u8) -> bool); } #[test] #[should_panic] fn sieve_not_all_primes() { - fn prop_prime_iff_in_the_sieve(n: usize) -> bool { + fn prop_prime_iff_in_the_sieve(n: u8) -> bool { + let n = n as usize; sieve(n) == (0..(n + 1)).filter(|&i| is_prime(i)).collect::>() } - quickcheck(prop_prime_iff_in_the_sieve as fn(usize) -> bool); + quickcheck(prop_prime_iff_in_the_sieve as fn(u8) -> bool); } #[test] From f704e9486b65ff3018aaaba4c256bcdd1e51eb2f Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Mon, 26 Aug 2019 01:15:58 -0700 Subject: [PATCH 02/12] clean up arbitrary tests for integers --- src/arbitrary.rs | 59 +++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index 6e9dc40..00380a2 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -948,46 +948,49 @@ mod test { } macro_rules! arby_int { - ($($t:ty),+) => {$( - let mut gen = super::StdGen::new(rand::thread_rng(), 100); - // 1/10 chance of hitting problematic value * 10000 random values - // => ~1000 problematic values - // => probability of failure ~ (2/3)^(1000) ~ 10e-170 - let arbys:Vec<$t> = (0..10000) - .map(|_|{Arbitrary::arbitrary(& mut gen)}) - .collect(); - if !arbys.iter().any(|&x| x==<$t>::max_value()) { - panic!("Arbitrary does not generate max value") - } - if !arbys.iter().any(|&x| x==<$t>::min_value()) { - panic!("Arbitrary does not generate min value") + ( $signed:expr, $($t:ty),+) => {$( + let arbys: Vec<$t> = (0..10000).map(|_| arby::<$t>()).collect(); + let problem_values = if $signed { + signed_problem_values!($t) + } else { + unsigned_problem_values!($t) + }; + if !problem_values.iter().all(|x| arbys.iter().any(|y| y==x)) { + panic!("Arbitrary does not generate all problematic values") } - //filter out problematic values - let arbys:Vec<$t> = arbys - .into_iter() - .filter(|x| unsigned_problem_values!($t).iter().any(|y| y==x)) + //filter out problematic values, leaving randomly generated ones + let arbys: Vec<$t> = arbys.into_iter() + .filter(|x| problem_values.iter().any(|y| y!=x)) .collect(); - // 9/10 chance of hitting non-problematic value * 10000 random values - // => ~9000 values - // => probability of failure ~ (19/20)^(9000) ~ 10e-200 - if <$t>::max_value()/20 * 19 > *arbys.iter().max().unwrap_or(&0) { - panic!("Arbitrary did not generate within 5% of maximum value") - } - if <$t>::min_value() + <$t>::max_value()/20 - < *arbys.iter().min().unwrap_or(&<$t>::max_value()) { - panic!("Arbitrary did not generate within 5% of minimum value") + let (min, max) = (<$t>::min_value(), <$t>::max_value()); + let mid = (max + min) / 2; + // split full range of $t into chunks + // Arbitrary must generate one value in each chunk + let chunks_2: $t = 9; + let chunks = chunks_2 * 2; //chunks must be an even number + let iter: Box> = if $signed { + Box::new((0..=chunks).map(|x| x - chunks / 2) + .map(|x| mid + max / (chunks / 2) * x) + ) + } else { + Box::new((0..=chunks).map(|x| max / chunks * x)) + }; + let mut iter = iter.peekable(); + while let (Some(lower), Some(upper)) = (iter.next(), iter.peek()) { + assert!(arbys.iter().find(|x| (lower..*upper).contains(x)).is_some(), + "Arbitrary doesn't generate integers in {}..{}", lower, upper) } )*}; } #[test] fn arby_int() { - arby_int!(i8, i16, i32, i64, isize, i128); + arby_int!(true, i8, i16, i32, i64, isize, i128); } #[test] fn arby_uint() { - arby_int!(u8, u16, u32, u64, usize, u128); + arby_int!(false, u8, u16, u32, u64, usize, u128); } fn arby() -> A { From abdd6173db01cc2808216e1982af80acd60b174a Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Fri, 6 Sep 2019 23:07:44 -0700 Subject: [PATCH 03/12] make duration respect gen.size --- src/arbitrary.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index 00380a2..d9d9050 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -868,7 +868,7 @@ impl Arbitrary for RangeFull { impl Arbitrary for Duration { fn arbitrary(gen: &mut G) -> Self { - let seconds = u64::arbitrary(gen); + let seconds = u64::arbitrary(gen) % (gen.size() as u64) ; let nanoseconds = u32::arbitrary(gen) % 1_000_000; Duration::new(seconds, nanoseconds) } From 41c07ad1a001c272c67a5ac15696e2d79cf6f84e Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Thu, 12 Sep 2019 20:16:55 -0700 Subject: [PATCH 04/12] improve naming and make style adjustments --- src/arbitrary.rs | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index d9d9050..c0a3167 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -950,35 +950,32 @@ mod test { macro_rules! arby_int { ( $signed:expr, $($t:ty),+) => {$( let arbys: Vec<$t> = (0..10000).map(|_| arby::<$t>()).collect(); - let problem_values = if $signed { - signed_problem_values!($t) + let mut problems = if $signed { + signed_problem_values!($t).iter() } else { - unsigned_problem_values!($t) + unsigned_problem_values!($t).iter() }; - if !problem_values.iter().all(|x| arbys.iter().any(|y| y==x)) { - panic!("Arbitrary does not generate all problematic values") - } - //filter out problematic values, leaving randomly generated ones - let arbys: Vec<$t> = arbys.into_iter() - .filter(|x| problem_values.iter().any(|y| y!=x)) - .collect(); - let (min, max) = (<$t>::min_value(), <$t>::max_value()); - let mid = (max + min) / 2; + assert!(problems.all(|p| arbys.iter().any(|arby| arby == p)), + "Arbitrary does not generate all problematic values"); + let max = <$t>::max_value(); + let mid = (max + <$t>::min_value()) / 2; // split full range of $t into chunks - // Arbitrary must generate one value in each chunk - let chunks_2: $t = 9; - let chunks = chunks_2 * 2; //chunks must be an even number - let iter: Box> = if $signed { - Box::new((0..=chunks).map(|x| x - chunks / 2) - .map(|x| mid + max / (chunks / 2) * x) - ) + // Arbitrary must return some value in each chunk + let double_chunks: $t = 9; + let chunks = double_chunks * 2; // chunks must be even + let lim: Box> = if $signed { + Box::new((0..=chunks) + .map(|idx| idx - chunks / 2) + .map(|x| mid + max / (chunks / 2) * x)) } else { - Box::new((0..=chunks).map(|x| max / chunks * x)) + Box::new((0..=chunks).map(|idx| max / chunks * idx)) }; - let mut iter = iter.peekable(); - while let (Some(lower), Some(upper)) = (iter.next(), iter.peek()) { - assert!(arbys.iter().find(|x| (lower..*upper).contains(x)).is_some(), - "Arbitrary doesn't generate integers in {}..{}", lower, upper) + let mut lim = lim.peekable(); + while let (Some(low), Some(&high)) = (lim.next(), lim.peek()) { + assert!(arbys.iter() + .find(|arby| (low..=high).contains(arby)) + .is_some(), + "Arbitrary doesn't generate numbers in {}..={}", low, high) } )*}; } From 9acb60f1b7d19a8aa11ff0d6187262848c266611 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Thu, 19 Sep 2019 01:34:16 -0700 Subject: [PATCH 05/12] switch from `%` to `gen_range()` for Duration This ensures the distribution will be uniform, rather than just close to uniform (because of problem values). --- src/arbitrary.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index c0a3167..0f62cf0 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -868,8 +868,8 @@ impl Arbitrary for RangeFull { impl Arbitrary for Duration { fn arbitrary(gen: &mut G) -> Self { - let seconds = u64::arbitrary(gen) % (gen.size() as u64) ; - let nanoseconds = u32::arbitrary(gen) % 1_000_000; + let seconds = gen.gen_range(0, gen.size() as u64); + let nanoseconds = gen.gen_range(0, 1_000_000); Duration::new(seconds, nanoseconds) } From 75cf794b01e66b9374e511ac4602d6a36d98bee3 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Wed, 2 Oct 2019 22:51:38 -0700 Subject: [PATCH 06/12] fix - max integer values weren't being generated gen_range is exclusive of the max value in the range, but gen() includes all values in the range --- src/arbitrary.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index 0f62cf0..6cc4369 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -696,7 +696,7 @@ macro_rules! unsigned_arbitrary { 0 => { *unsigned_problem_values!($ty).choose(g).unwrap() }, - _ => g.gen_range($ty::min_value(), $ty::max_value()) + _ => g.gen() } } fn shrink(&self) -> Box> { @@ -769,7 +769,7 @@ macro_rules! signed_arbitrary { 0 => { *signed_problem_values!($ty).choose(g).unwrap() }, - _ => g.gen_range($ty::min_value(), $ty::max_value()) + _ => g.gen() } } fn shrink(&self) -> Box> { From f062553ca24cbf21a9b2a30e19900abd53525f7d Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Fri, 6 Dec 2019 23:50:37 -0800 Subject: [PATCH 07/12] update documentation --- src/arbitrary.rs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index 6cc4369..eb2be14 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -23,12 +23,14 @@ use std::time::{UNIX_EPOCH, Duration, SystemTime}; use rand::{self, Rng, RngCore}; use rand::seq::SliceRandom; -/// `Gen` wraps a `rand::RngCore` with parameters to control the distribution of +/// `Gen` wraps a `rand::RngCore` with parameters to control the range of /// random values. /// /// 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; } @@ -46,9 +48,9 @@ impl StdGen { /// generator. /// /// The `size` parameter controls the size of random values generated. For - /// example, it specifies the maximum length of a randomly generated vector - /// and also will specify the maximum magnitude of a randomly generated - /// number. + /// 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 } } @@ -79,9 +81,9 @@ impl StdThreadGen { /// Returns a new thread-local RNG. /// /// The `size` parameter controls the size of random values generated. For - /// example, it specifies the maximum length of a randomly generated vector - /// and also will specify the maximum magnitude of a randomly generated - /// number. + /// 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 }) } @@ -117,7 +119,12 @@ pub fn single_shrinker(value: A) -> Box> { /// shrunk. /// /// Aside from shrinking, `Arbitrary` is different from the `std::Rand` trait -/// in that it uses a `Gen` to control the distribution of random values. +/// 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). /// /// As of now, all types that implement `Arbitrary` must also implement /// `Clone`. (I'm not sure if this is a permanent restriction.) From 29796dc637267ba8b36adb8877bbd422c2dfc7f0 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Sat, 7 Dec 2019 00:13:27 -0800 Subject: [PATCH 08/12] remove call to range.contains() range.contains is unstable in rust 1.34, which is supported by this library. --- src/arbitrary.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index 532946d..ada5746 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -1013,7 +1013,6 @@ mod test { use std::hash::Hash; use std::num::Wrapping; use std::path::PathBuf; - use super::Arbitrary; use super::StdGen; #[test] @@ -1047,7 +1046,7 @@ mod test { let mut lim = lim.peekable(); while let (Some(low), Some(&high)) = (lim.next(), lim.peek()) { assert!(arbys.iter() - .find(|arby| (low..=high).contains(arby)) + .find(|arby| low <= **arby && **arby <= high) .is_some(), "Arbitrary doesn't generate numbers in {}..={}", low, high) } From 55a5e6ffaa51b7253a0fe5ee11be3904993252b1 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Sun, 12 Jan 2020 09:30:07 -0800 Subject: [PATCH 09/12] make arbitrary generate full range of floats --- src/arbitrary.rs | 95 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 70 insertions(+), 25 deletions(-) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index ada5746..9fa0a86 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -853,30 +853,43 @@ signed_arbitrary! { isize, i8, i16, i32, i64, i128 } -impl Arbitrary for f32 { - fn arbitrary(g: &mut G) -> f32 { - let s = g.size(); - g.gen_range(-(s as f32), s as f32) - } - fn shrink(&self) -> Box> { - signed_shrinker!(i32); - let it = shrinker::SignedShrinker::new(*self as i32); - Box::new(it.map(|x| x as f32)) - } +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., p::MAX, p::INFINITY] + } + }; } -impl Arbitrary for f64 { - fn arbitrary(g: &mut G) -> f64 { - let s = g.size(); - g.gen_range(-(s as f64), s as f64) - } - fn shrink(&self) -> Box> { - signed_shrinker!(i64); - let it = shrinker::SignedShrinker::new(*self as i64); - Box::new(it.map(|x| x as f64)) - } +macro_rules! float_arbitrary { + ($($t:ty, $path:path, $shrinkable:ty),+) => {$( + impl Arbitrary for $t { + fn arbitrary(g: &mut G) -> $t { + match g.gen_range(0,10) { + 0 => *float_problem_values!($path).choose(g).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(); + sign * mantissa * exp.exp2() + } + } + } + fn shrink(&self) -> Box> { + signed_shrinker!($shrinkable); + let it = shrinker::SignedShrinker::new(*self as $shrinkable); + Box::new(it.map(|x| x as $t)) + } + } + )*}; } +float_arbitrary!(f32, std::f32, i32, + f64, std::f64, i64); + impl Arbitrary for Wrapping { fn arbitrary(g: &mut G) -> Wrapping { Wrapping(T::arbitrary(g)) @@ -1022,13 +1035,13 @@ mod test { macro_rules! arby_int { ( $signed:expr, $($t:ty),+) => {$( - let arbys: Vec<$t> = (0..10000).map(|_| arby::<$t>()).collect(); + let mut arbys = (0..1_000_000).map(|_| arby::<$t>()); let mut problems = if $signed { signed_problem_values!($t).iter() } else { unsigned_problem_values!($t).iter() }; - assert!(problems.all(|p| arbys.iter().any(|arby| arby == p)), + assert!(problems.all(|p| arbys.any(|arby| arby == *p)), "Arbitrary does not generate all problematic values"); let max = <$t>::max_value(); let mid = (max + <$t>::min_value()) / 2; @@ -1045,9 +1058,7 @@ mod test { }; let mut lim = lim.peekable(); while let (Some(low), Some(&high)) = (lim.next(), lim.peek()) { - assert!(arbys.iter() - .find(|arby| low <= **arby && **arby <= high) - .is_some(), + assert!(arbys.any(|arby| low <= arby && arby <= high), "Arbitrary doesn't generate numbers in {}..={}", low, high) } )*}; @@ -1063,6 +1074,40 @@ mod test { arby_int!(false, u8, u16, u32, u64, usize, u128); } + macro_rules! arby_float { + ($($t:ty, $path:path),+) => {$({ + use $path as p; + let mut arbys = (0..1_000_000).map(|_| arby::<$t>()); + //NaN != NaN + assert!(arbys.any(|f| f.is_nan()), + "Arbitrary does not generate the problematic value NaN" + ); + for p in float_problem_values!($path).iter().filter(|f| !f.is_nan()) { + assert!(arbys.any(|arby| arby == *p), + "Arbitrary does not generate the problematic value {}", + p + ); + } + // split full range of $t into chunks + // Arbitrary must return some value in each chunk + let double_chunks: i8 = 9; + let chunks = double_chunks * 2; // chunks must be even + let lim = (-double_chunks..=double_chunks) + .map(|idx| <$t>::from(idx)) + .map(|idx| p::MAX/(<$t>::from(chunks/2)) * idx); + let mut lim = lim.peekable(); + while let (Some(low), Some(&high)) = (lim.next(), lim.peek()) { + assert!(arbys.any(|arby| low <= arby && arby <= high), + "Arbitrary doesn't generate numbers in {:e}..={:e}", low, high) + } + })*}; + } + + #[test] + fn arby_float() { + arby_float!(f32, std::f32, f64, std::f64); + } + fn arby() -> A { Arbitrary::arbitrary(&mut StdGen::new(rand::thread_rng(), 5)) } From e8eab0237591ed0c45d1a78c0cc1e01df006c234 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Sun, 12 Jan 2020 09:38:44 -0800 Subject: [PATCH 10/12] fix formatting --- src/arbitrary.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index 25ebde0..6033c1a 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -860,13 +860,11 @@ signed_arbitrary! { } 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., p::MAX, p::INFINITY] - } - }; + ($path:path) => {{ + // hack. see: https://github.com/rust-lang/rust/issues/48067 + use $path as p; + [p::NAN, p::NEG_INFINITY, p::MIN, 0., p::MAX, p::INFINITY] + }}; } macro_rules! float_arbitrary { @@ -893,8 +891,7 @@ macro_rules! float_arbitrary { )*}; } -float_arbitrary!(f32, std::f32, i32, - f64, std::f64, i64); +float_arbitrary!(f32, std::f32, i32, f64, std::f64, i64); macro_rules! unsigned_non_zero_shrinker { ($ty:tt) => { @@ -1120,6 +1117,7 @@ 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, @@ -1128,7 +1126,6 @@ mod test { use std::hash::Hash; use std::num::Wrapping; use std::path::PathBuf; - use super::StdGen; #[test] fn arby_unit() { @@ -1147,7 +1144,7 @@ mod test { "Arbitrary does not generate all problematic values"); let max = <$t>::max_value(); let mid = (max + <$t>::min_value()) / 2; - // split full range of $t into chunks + // split full range of $t into chunks // Arbitrary must return some value in each chunk let double_chunks: $t = 9; let chunks = double_chunks * 2; // chunks must be even From d4b4c1085667ee1a34c69a9aa275ce6257d125a5 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Sun, 12 Jan 2020 09:46:21 -0800 Subject: [PATCH 11/12] fix docs formatting --- src/arbitrary.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index 6033c1a..fd26ca0 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -31,7 +31,7 @@ use rand::{self, Rng, RngCore}; /// `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 + /// For an explaination of which types behave how, see `Arbitrary` fn size(&self) -> usize; } From 850640d1195f9dfc5a8e1759f9e9b836b399b9ce Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Mon, 13 Jan 2020 15:39:47 -0800 Subject: [PATCH 12/12] add -0.0 to float_problem_values --- src/arbitrary.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index fd26ca0..715b9a3 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -863,7 +863,7 @@ 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., p::MAX, p::INFINITY] + [p::NAN, p::NEG_INFINITY, p::MIN, -0., 0., p::MAX, p::INFINITY] }}; }