[Haskell-cafe] property testing with context
arjenvanweelden at gmail.com
arjenvanweelden at gmail.com
Mon Oct 22 09:59:24 UTC 2018
On Fri, 2018-10-19 at 09:19 +0200, Ben Franksen wrote:
> Hi everyone
>
> it seems to be the season for new variations on the "property
> testing"
> theme, so I would like to chime in... not to announce a new library,
> sadly, but with a rough idea how the existing ones could perhaps be
> improved, based on practical experience in Darcs.
>
> The problem I have is that there is a tension between
>
> (a) stating a property in a clear and simple way, so its code doubles
> as
> a formal specification
>
> and
>
> (b) writing the property in such a way that when it fails, the
> reported
> value(s) give enough information about the context to be useful for
> finding the cause of the problem.
>
> Let me give an example to demonstrate what I mean.
>
> There is a simple law that says if a sequential pair of patches A;B
> commutes to B';A' then B';A' commutes back to A;B. In code this looks
> (more or less) like this:
>
> prop_recommute :: Commute p => (p :> p) wA wB -> Bool
> prop_recommute (x:>y)
> | Just (y':>x') <- commute (x:>y) =
> case commute (y':>x')of
> Just (x'':>y'') -> x==x'' && y==y''
> Nothing -> False
> | otherwise = True
>
> This is a bit more verbose than the informal spec but still quite
> readable.
>
> Now suppose this property fails. So quickcheck reports the counter
> example pair (X:>Y) for some X and Y. But that isn't too useful in
> itself. We'd like to know a bit more:
>
> * did the second commute fail?
> * or did it succeed but x/=x'' or y/=y''?
> * and in the latter case, which of the two?
I think that this is possible by simply using QuickCheck's === and ==>
(if you have Show and Eq instances for :>):
prop_recommute :: Commute p => (p :> p) wA wB -> Bool
prop_recommute (x:>y)
= isJust commuted ==> commute commuted === Just (x:>y)
where
commuted = commute (x:>y)
See
https://hackage.haskell.org/package/QuickCheck-2.11.3/docs/Test-QuickCheck-Property.html
for information on ==> and ===.
This is more readable and quite similar to your example above. It would
print both left and right side of === when a counter-example is found.
Depending on your implementation of Show for :>, it could look
like: Nothing /= Just (...A... :> ...B...) or Just (...A... :> ...B...)
/= Just (...C... :> ...D...).
I did not try this myself, so I could have made a mistake or I may have
missed why this is not good enough for your case.
> So in practice our property code looks more like this:
>
> prop_recommute :: (ShowPatch p, Commute p) => (p :> p) wA wB -> Bool
> prop_recommute (x :> y)
> | Just (y' :> x') <- commute (x :> y) =
> case commute (y' :> x') of
> Nothing ->
> failed (redText "failed, where x" $$ displayPatch x $$
> redText ":> y" $$ displayPatch y $$
> redText "y'" $$ displayPatch y' $$
> redText ":> x'" $$ displayPatch x')
> Just (x'' :> y'') ->
> if y'' /= y
> then
> failed (redText "y'' =/\\= y failed, where x" $$
> displayPatch x $$
> redText ":> y" $$ displayPatch y $$
> redText "y'" $$ displayPatch y' $$
> redText ":> x'" $$ displayPatch x' $$
> redText "x''" $$ displayPatch x'' $$
> redText ":> y''" $$ displayPatch y'')
> else
> if x'' /= x
> then
> failed (redText "x'' /= x, where x" $$
> displayPatch x $$
> redText ":> y" $$ displayPatch y $$
> redText "y'" $$ displayPatch y' $$
> redText ":> x'" $$ displayPatch x' $$
> redText "x''" $$ displayPatch x'' $$
> redText ":> y''" $$ displayPatch y'')
> else True
> | otherwise = True
>
> Now this code tells us exactly what went wrong when the property
> fails
> but it is ugly and repetitive and the additional code obscures the
> simple logical content. So this is no longer quite as suitable for a
> (human readable) formal spec.
>
> I wonder if displaying
>
> (1) all relevant contextual variables and
> (2) "where in the code it fails"
>
> could be automated away, somehow. I guess this is not trivial and may
> require syntactic analysis, so perhaps expecting a /library/ to solve
> the problem is unrealistic, except perhaps by using Template Haskell.
> I'd also go with a separate tool that extracts properties from a
> module
> and enriches them with code that displays the additional information.
>
> Tackling this problem might be an interesting theme for a master
> thesis... ;-)
>
> Cheers
> Ben
>
> _______________________________________________
> Haskell-Cafe mailing list
> To (un)subscribe, modify options or view archives go to:
> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
> Only members subscribed via the mailman list are allowed to post.
More information about the Haskell-Cafe
mailing list