Skip to content

Commit

Permalink
Add a convenience type for properties involving equivalence
Browse files Browse the repository at this point in the history
This type was originally motivated by observing that a class of
properties will involve a pseudo-identity followed by a check for
equivalence between its input and output. A generalization of would be
properties which are defined by the equivalence check, only.

For this class, we usually want to know _how_ those two values differ,
rather than only that they do. Test-authors may thus write tests like
the following in order to include those values in a failure report:

    fn revrev(xs: Vec<usize>) -> TestResult {
        let rev: Vec<_> = xs.clone().into_iter().rev().collect();
        let revrev: Vec<_> = rev.into_iter().rev().collect();
        if xs == revrev {
             TestResult::passed()
         } else {
             TestResult::error(
                format!("Original: '{:?}', Identity: '{:?}'", xs, revrev)
             )
         }
    }

This change introduces a convenience type which encapsulates the
equivalence check as well as the error message generation. Using it, the
above test could be written as:

    fn revrev(xs: Vec<usize>) -> Equivalence<Vec<usize>> {
        let rev: Vec<_> = xs.clone().into_iter().rev().collect();
        let revrev: Vec<_> = rev.into_iter().rev().collect();
        Equivalence::of(xs, revrev)
    }
  • Loading branch information
neithernut committed Mar 18, 2021
1 parent defde6f commit 89a056a
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 1 deletion.
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ semver compatible releases.
*/

pub use crate::arbitrary::{empty_shrinker, single_shrinker, Arbitrary, Gen};
pub use crate::tester::{quickcheck, QuickCheck, TestResult, Testable};
pub use crate::tester::{
quickcheck, QuickCheck, TestResult, Equivalence, Testable,
};

/// A macro for writing quickcheck tests.
///
Expand Down
58 changes: 58 additions & 0 deletions src/tester.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,49 @@ impl TestResult {
}
}

/// Utility type for properties involving an equivalence
///
/// Sometimes, properties we want to test for are the equivalence of two values.
/// For example, we may construct a pseudeo-identity from a formatter and a
/// parser in order to test a parser. In such cases, we want to compare the
/// input of the pseudo-identity to its output.
///
/// `Equivalence` is a `Testable` type which expresses this intent, but also
/// includes both values as part of the failure report if a test fails.
///
/// # Example
///
/// ```rust
/// use quickcheck::{QuickCheck, Equivalence};
///
/// fn prop_reverse_reverse() {
/// fn revrev(xs: Vec<usize>) -> Equivalence<Vec<usize>> {
/// let rev: Vec<_> = xs.clone().into_iter().rev().collect();
/// let revrev: Vec<_> = rev.into_iter().rev().collect();
/// Equivalence::of(xs, revrev)
/// }
/// QuickCheck::new().quickcheck(revrev as fn(Vec<usize>) -> Equivalence<Vec<usize>>);
/// }
/// ```
#[derive(Clone, Debug)]
pub struct Equivalence<T>(T, T)
where
T: Debug + PartialEq + 'static;

impl<T> Equivalence<T>
where
T: Debug + PartialEq + 'static
{
/// Construct a value expressing the equivalence of the given values
///
/// In many cases, you'll be able to construct an instance for two values
/// `a` and `b` via `Equivalence(a, b)`. This function is intended for
/// situations where you can't for whatever reasons.
pub fn of(left: T, right: T) -> Self {
Self(left, right)
}
}

/// `Testable` describes types (e.g., a function) whose values can be
/// tested.
///
Expand Down Expand Up @@ -314,6 +357,21 @@ impl Testable for TestResult {
}
}

impl<T> Testable for Equivalence<T>
where T: Debug + PartialEq + 'static
{
fn result(&self, _: &mut Gen) -> TestResult {
if self.0 == self.1 {
TestResult::passed()
} else {
TestResult::error(format!(
"Missmatch! Left: '{:?}', Right: '{:?}'",
self.0, self.1
))
}
}
}

impl<A, E> Testable for Result<A, E>
where
A: Testable,
Expand Down

0 comments on commit 89a056a

Please sign in to comment.