[prev in list] [next in list] [prev in thread] [next in thread] 

List:       haskell-cafe
Subject:    Re: [Haskell-cafe] property testing with context
From:       Oliver Charles <ollie () ocharles ! org ! uk>
Date:       2018-10-24 20:51:31
Message-ID: CAGRp5RnPRggac19doPjJSye4ZpQDsx=bWENC_5-Mbak+9xLE1w () mail ! gmail ! com
[Download RAW message or body]

[Attachment #2 (multipart/alternative)]


I agree with Ben's point though that you have to "buy in" to the testing
framework and write tests in a DSL, though. This seems unfortunate.

On Wed, 24 Oct 2018, 2:44 pm , <amindfv@gmail.com> wrote:

> Agreed, I think QuickCheck is already up to the task. The simpler
> prop_recommute could be rewritten with (===), (.&&.), and maybe
> "counterexample" to label which branch failed.
>
> Tom
>
> > El 22 oct 2018, a las 05:59, arjenvanweelden@gmail.com escribió:
> >
> >> 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.
> >
> > _______________________________________________
> > 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.
> _______________________________________________
> 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.

[Attachment #5 (text/html)]

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


[Attachment #6 (text/plain)]

_______________________________________________
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.

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic