Skip to content

Commit 4ac7722

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 defde6f commit 4ac7722

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
@@ -15,7 +15,9 @@ semver compatible releases.
1515
*/
1616

1717
pub use crate::arbitrary::{empty_shrinker, single_shrinker, Arbitrary, Gen};
18-
pub use crate::tester::{quickcheck, QuickCheck, TestResult, Testable};
18+
pub use crate::tester::{
19+
quickcheck, Equivalence, QuickCheck, TestResult, Testable,
20+
};
1921

2022
/// A macro for writing quickcheck tests.
2123
///

src/tester.rs

+59
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,49 @@ impl TestResult {
281281
}
282282
}
283283

284+
/// Utility type for properties involving an equivalence
285+
///
286+
/// Sometimes, properties we want to test for are the equivalence of two values.
287+
/// For example, we may construct a pseudeo-identity from a formatter and a
288+
/// parser in order to test a parser. In such cases, we want to compare the
289+
/// input of the pseudo-identity to its output.
290+
///
291+
/// `Equivalence` is a `Testable` type which expresses this intent, but also
292+
/// includes both values as part of the failure report if a test fails.
293+
///
294+
/// # Example
295+
///
296+
/// ```rust
297+
/// use quickcheck::{QuickCheck, Equivalence};
298+
///
299+
/// fn prop_reverse_reverse() {
300+
/// fn revrev(xs: Vec<usize>) -> Equivalence<Vec<usize>> {
301+
/// let rev: Vec<_> = xs.clone().into_iter().rev().collect();
302+
/// let revrev: Vec<_> = rev.into_iter().rev().collect();
303+
/// Equivalence::of(xs, revrev)
304+
/// }
305+
/// QuickCheck::new().quickcheck(revrev as fn(Vec<usize>) -> Equivalence<Vec<usize>>);
306+
/// }
307+
/// ```
308+
#[derive(Clone, Debug)]
309+
pub struct Equivalence<T>(T, T)
310+
where
311+
T: Debug + PartialEq + 'static;
312+
313+
impl<T> Equivalence<T>
314+
where
315+
T: Debug + PartialEq + 'static,
316+
{
317+
/// Construct a value expressing the equivalence of the given values
318+
///
319+
/// In many cases, you'll be able to construct an instance for two values
320+
/// `a` and `b` via `Equivalence(a, b)`. This function is intended for
321+
/// situations where you can't for whatever reasons.
322+
pub fn of(left: T, right: T) -> Self {
323+
Self(left, right)
324+
}
325+
}
326+
284327
/// `Testable` describes types (e.g., a function) whose values can be
285328
/// tested.
286329
///
@@ -314,6 +357,22 @@ impl Testable for TestResult {
314357
}
315358
}
316359

360+
impl<T> Testable for Equivalence<T>
361+
where
362+
T: Debug + PartialEq + 'static,
363+
{
364+
fn result(&self, _: &mut Gen) -> TestResult {
365+
if self.0 == self.1 {
366+
TestResult::passed()
367+
} else {
368+
TestResult::error(format!(
369+
"Missmatch! Left: '{:?}', Right: '{:?}'",
370+
self.0, self.1
371+
))
372+
}
373+
}
374+
}
375+
317376
impl<A, E> Testable for Result<A, E>
318377
where
319378
A: Testable,

0 commit comments

Comments
 (0)