Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add a convenience type for properties involving equivalence #281

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

neithernut
Copy link

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. See #280.

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)
}

@neithernut neithernut mentioned this pull request Mar 18, 2021
@neithernut neithernut force-pushed the equivalence-testing branch from a99cd9e to 89a056a Compare March 18, 2021 12:50
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)
    }
@neithernut neithernut force-pushed the equivalence-testing branch from 89a056a to 4ac7722 Compare March 18, 2021 12:52
`Equivalence` is meant to be trivially constructible, which was the
movitation behind making it a tuple struct. However, it's still a
_struct_ and its fields are not pubilic by default. Therefore, the
type was not constructible as advertised, e.g. via `Equivalence(a, b)`.

This change makes these fields public, as they are supposed to be.
`quickcheck` allows to skip tests (for given inputs) by returning a
`TestResult` indicating that it should be ignored. Naturally, an
`Equivalence` cannot express such an intent and neither can any other
`Testable`s other than functions and `TestResult` itself. Hence, if we
need the ability to skip tests, we need to return a `TestResult` or some
dependent type (e.g. `Result<TestResult>`) from our property function.

If we still want to make use of `Equivalence` in such tests, we need to
convert it to a `TestResult`. Previously, we had to resort to using
`Testable::result` with some dummy `&mut Gen`, even though it isn't even
used in the conversion. As a remedy, this change moves the conversion
into a dedicates function (with no additional parameters) which may be
called inside property functions.
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant