From 6ecdf5bb4b0132ce66670b4d46453aa022ea892c Mon Sep 17 00:00:00 2001 From: Jakob Schikowski Date: Tue, 9 Feb 2021 02:13:10 +0100 Subject: [PATCH 1/3] api: add Gen::set_size and Gen::from_seed The seed still can't be set by QuickCheck users, but the new Gen constructor is useful for other crates that use QuickCheck only for its Arbitrary trait. Closes #277 --- src/arbitrary.rs | 24 +++++++++++++++++++++--- src/tester.rs | 2 +- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index 92f893b..26b0be8 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -39,15 +39,33 @@ pub struct Gen { } impl Gen { - /// Returns a `Gen` with the given size configuration. + pub(crate) const DEFAULT_SIZE: usize = 100; + + /// Returns a `Gen` with a random seed and the given size configuration. + pub fn new(size: usize) -> Gen { + Gen { rng: rand::rngs::SmallRng::from_entropy(), size: size } + } + + /// Returns a `Gen` with the given seed and a default size configuration. + /// + /// Two `Gen`s created with the same seed will generate the same values. Though the values + /// may vary between QuickCheck releases. + pub fn from_seed(seed: u64) -> Gen { + Gen { + rng: rand::rngs::SmallRng::seed_from_u64(seed), + size: Self::DEFAULT_SIZE, + } + } + + /// Sets the size configuration for this generator. /// /// The `size` parameter controls the size of random values generated. /// For example, it specifies the maximum length of a randomly generated /// 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 } + pub fn set_size(&mut self, size: usize) { + self.size = size; } /// Returns the size configured with this generator. diff --git a/src/tester.rs b/src/tester.rs index e2eaa20..e639973 100644 --- a/src/tester.rs +++ b/src/tester.rs @@ -33,7 +33,7 @@ fn qc_max_tests() -> u64 { } fn qc_gen_size() -> usize { - let default = 100; + let default = Gen::DEFAULT_SIZE; match env::var("QUICKCHECK_GENERATOR_SIZE") { Ok(val) => val.parse().unwrap_or(default), Err(_) => default, From e8e998dcde98bf230e15f3b2984f94553ae5d34d Mon Sep 17 00:00:00 2001 From: Julian Ganz Date: Sun, 9 May 2021 13:25:06 +0200 Subject: [PATCH 2/3] api: add fn for creating generator for recursion When generating recursive structures, users will often use the size of the generator to bound the recursion depth. Dividing the size by the number of sub-structures appears to be a common practice. However, modifying the size of the generator passed to an implementation of `Arbitrary::arbitrary` would also affect the generation of sister structures if not restored after the generation of child nodes. Instead, users can create a new generator instance with the target size. Historically, this would be the only option since public API for setting the size of an existing generator was only introduced recently. This change introduces a convenience function for creating such a generator. The new function makes use of seed from the original generator rather than entropy. In addition to bounding recursive structures, this also allows to re-create those structures based on some initial seed. --- src/arbitrary.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index 26b0be8..b66ebbc 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -80,6 +80,17 @@ impl Gen { slice.choose(&mut self.rng) } + /// Creates a generator suitable for recursion. + /// + /// The returned generator's size will be the origianl gnerator's size + /// divided by n. In addition, the new generator will be seeded from the + /// original. + pub fn sub(&mut self, num: NonZeroUsize) -> Self { + let mut res = Self::from_seed(self.gen()); + res.set_size(self.size() / num); + res + } + fn gen(&mut self) -> T where rand::distributions::Standard: rand::distributions::Distribution, From 4ae502b2477d51d5c1c067aa4cf68c66fe947ad2 Mon Sep 17 00:00:00 2001 From: Julian Ganz Date: Sun, 9 May 2021 13:55:11 +0200 Subject: [PATCH 3/3] tests: add check for size of generator returned by Gen::sub --- src/tests.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tests.rs b/src/tests.rs index 465ef15..9086e6b 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -255,6 +255,12 @@ fn all_tests_discarded_min_tests_passed_missing() { } quickcheck! { + /// The documentation on `Gen::sub_n` adversizes some easy-to-check property + /// regarding the generators' sizes. + fn gen_sub_size_sum(original: usize, num: std::num::NonZeroUsize) -> bool { + Gen::new(original).sub(num).size() * num.get() <= original + } + /// The following is a very simplistic test, which only verifies /// that our PathBuf::arbitrary does not panic. Still, that's /// something! :)