From 90cf94a230075f40c8206d4ccb7f112be2cb173e Mon Sep 17 00:00:00 2001 From: Julian Ganz Date: Sat, 12 Jun 2021 10:24:49 +0200 Subject: [PATCH] Add a wrapper for disabling shrinking for an Arbitrary In some cases, users may want to disable shrinking for a specific test. Consider, for example, some complex recursive data type. Naturally, we'd want values to be shrunk for most tests in order to isolate the specific sub-structure provoking the failure. However, we may end up with a shrinker with a certain complexity in itself. Hence, we may want to test our shrinker. For those specific tests, the very shrinking of input values performed by quickcheck could get in our way or cause failures on its own. Previously, the only way library users could disable shrinking in such cases was to define a wrapper type (in their own code) which forwarded `Arbitrary::arbitrary` but not `Arbitrary::shrink`. We suspect that the possibility of disabling shrinking for selected tests, such as in cases described above, is not too uncommon. The wrapper pattern is easy to understand and blends in well with quickcheck. Shipping one with the library will suit the described use-cases. --- src/arbitrary.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 4 +++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index 897641a..e7418f0 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -1206,6 +1206,49 @@ impl Arbitrary for SystemTime { } } +/// Wrapper for disabling shrinking for an Arbitrary +/// +/// This type allows generating values via a given `Arbitrary` implementation +/// for a test for which we don't want to shrink input values. +/// +/// # Example +/// +/// ```rust +/// use quickcheck::{QuickCheck, NoShrink}; +/// +/// fn prop_sane_shrinker() { +/// // Yielding the original value will result in endless recursion +/// fn shrinker_no_self(value: NoShrink) -> bool { +/// use quickcheck::Arbitrary; +/// !value.as_ref().shrink().any(|v| v == *value.as_ref()) +/// } +/// QuickCheck::new().quickcheck(shrinker_no_self as fn(NoShrink) -> bool); +/// } +/// ``` +#[derive(Clone, Debug)] +pub struct NoShrink { + inner: A, +} + +impl NoShrink { + /// Unwrap the inner value + pub fn into_inner(self) -> A { + self.inner + } +} + +impl Arbitrary for NoShrink { + fn arbitrary(rnd: &mut Gen) -> Self { + Self { inner: Arbitrary::arbitrary(rnd) } + } +} + +impl AsRef for NoShrink { + fn as_ref(&self) -> &A { + &self.inner + } +} + #[cfg(test)] mod test { use std::collections::{ diff --git a/src/lib.rs b/src/lib.rs index d3a5ba4..8ff9a40 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,9 @@ new kind of witness being generated. These sorts of changes may happen in semver compatible releases. */ -pub use crate::arbitrary::{empty_shrinker, single_shrinker, Arbitrary, Gen}; +pub use crate::arbitrary::{ + empty_shrinker, single_shrinker, Arbitrary, Gen, NoShrink, +}; pub use crate::tester::{quickcheck, QuickCheck, TestResult, Testable}; /// A macro for writing quickcheck tests.