Skip to content

Commit dd7a4a0

Browse files
committed
Add a convenience type for properties involving equivalence
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) }
1 parent 03ab585 commit dd7a4a0

File tree

2 files changed

+62
-1
lines changed

2 files changed

+62
-1
lines changed

src/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ semver compatible releases.
2020
*/
2121

2222
pub use crate::arbitrary::{empty_shrinker, single_shrinker, Arbitrary, Gen};
23-
pub use crate::tester::{quickcheck, QuickCheck, TestResult, Testable};
23+
pub use crate::tester::{
24+
quickcheck, Equivalence, QuickCheck, TestResult, Testable,
25+
};
2426

2527
/// A macro for writing quickcheck tests.
2628
///

src/tester.rs

+59
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,49 @@ impl From<bool> for TestResult {
306306
}
307307
}
308308

309+
/// Utility type for properties involving an equivalence
310+
///
311+
/// Sometimes, properties we want to test for are the equivalence of two values.
312+
/// For example, we may construct a pseudeo-identity from a formatter and a
313+
/// parser in order to test a parser. In such cases, we want to compare the
314+
/// input of the pseudo-identity to its output.
315+
///
316+
/// `Equivalence` is a `Testable` type which expresses this intent, but also
317+
/// includes both values as part of the failure report if a test fails.
318+
///
319+
/// # Example
320+
///
321+
/// ```rust
322+
/// use quickcheck::{QuickCheck, Equivalence};
323+
///
324+
/// fn prop_reverse_reverse() {
325+
/// fn revrev(xs: Vec<usize>) -> Equivalence<Vec<usize>> {
326+
/// let rev: Vec<_> = xs.clone().into_iter().rev().collect();
327+
/// let revrev: Vec<_> = rev.into_iter().rev().collect();
328+
/// Equivalence::of(xs, revrev)
329+
/// }
330+
/// QuickCheck::new().quickcheck(revrev as fn(Vec<usize>) -> Equivalence<Vec<usize>>);
331+
/// }
332+
/// ```
333+
#[derive(Clone, Debug)]
334+
pub struct Equivalence<T>(T, T)
335+
where
336+
T: Debug + PartialEq + 'static;
337+
338+
impl<T> Equivalence<T>
339+
where
340+
T: Debug + PartialEq + 'static,
341+
{
342+
/// Construct a value expressing the equivalence of the given values
343+
///
344+
/// In many cases, you'll be able to construct an instance for two values
345+
/// `a` and `b` via `Equivalence(a, b)`. This function is intended for
346+
/// situations where you can't for whatever reasons.
347+
pub fn of(left: T, right: T) -> Self {
348+
Self(left, right)
349+
}
350+
}
351+
309352
/// `Testable` describes types (e.g., a function) whose values can be
310353
/// tested.
311354
///
@@ -339,6 +382,22 @@ impl Testable for TestResult {
339382
}
340383
}
341384

385+
impl<T> Testable for Equivalence<T>
386+
where
387+
T: Debug + PartialEq + 'static,
388+
{
389+
fn result(&self, _: &mut Gen) -> TestResult {
390+
if self.0 == self.1 {
391+
TestResult::passed()
392+
} else {
393+
TestResult::error(format!(
394+
"Missmatch! Left: '{:?}', Right: '{:?}'",
395+
self.0, self.1
396+
))
397+
}
398+
}
399+
}
400+
342401
impl<A, E> Testable for Result<A, E>
343402
where
344403
A: Testable,

0 commit comments

Comments
 (0)