[m-dev.] for review: quickcheck user guide
Mark Anthony BROWN
dougl at cs.mu.OZ.AU
Mon Feb 12 18:26:47 AEDT 2001
Hi,
Here is the first part of my review. I've addressed some issues with
grammar/typos/etc., but there are some other structural issues I haven't
mentioned. I'll post more of the review soon.
Cheers,
Mark.
Xiao Chun Simon MEI writes:
> Estimated hours taken: 40
>
> A collection of wed pages written as a user guide for quickcheck.
s/wed/web/
> It covers the syntax and features supported by current version of quickcheck.
> Below are the source code of those web pages. It's probably best viewed by
> a web browser at www.cs.mu.oz.au/~xcsm
The last two sentences don't belong in the log message. Log messages
are useful because we can look back at them and understand why a change
in the past was made. If in future we look back at this log message the
URL will be a dangling reference, since you are unlikely to leave those
pages there permanently.
(Note that it was useful to reviewers that you provided the URL, but not
in the log message.)
>
>
> quickcheck/tutes/T1.html:
> New file, covers the 1st topic
> quickcheck/tutes/T2.html:
> New file, covers the 2nd topic
> quickcheck/tutes/T3.html:
> New file, covers the 3rd topic
> quickcheck/tutes/T4.html:
> New file, covers the 4th topic
> quickcheck/tutes/T5.html:
> New file, covers the 5th topic
> quickcheck/tutes/T6.html:
> New file, covers the 6th topic
> quickcheck/tutes/T7.html:
> New file, covers the 7th topic
> quickcheck/tutes/T8.html:
> New file, covers the 8th topic
> quickcheck/tutes/T9.html:
> New file, covers the 9th topic
> quickcheck/tutes/index.html:
> New file, index page
> quickcheck/tutes/mymax.m:
> New file, contain sample function mymax
> quickcheck/tutes/numbers.html:
> New file, sub-page for T6.html
> quickcheck/tutes/testrev.m:
> New file, contains sample function testrev
> quickcheck/tutes/testrev2.m:
> New file, contains sample predicate testrev2
> quickcheck/tutes/use.m:
> New file, contains example for T1.html
> quickcheck/tutes/use1.m:
> New file, contains example for T1.html
> quickcheck/tutes/use11.m:
> New file, contains example for T1.html
> quickcheck/tutes/use2.m:
> New file, contains example for T2.html
> quickcheck/tutes/use20.m:
> New file, contains example for T2.html
> quickcheck/tutes/use21.m:
> New file, contains example for T3.html
> quickcheck/tutes/use31.m:
> New file, contains example for T3.html
> quickcheck/tutes/use33.m:
> New file, contains example for T4.html
> quickcheck/tutes/use51.m:
> New file, contains example for T5.html
> quickcheck/tutes/use62.m:
> New file, contains example for T6.html
> quickcheck/tutes/use71.m:
> New file, contains example for T7.html
> quickcheck/tutes/use81.m:
> New file, contains example for T8.html
> quickcheck/tutes/use91.m:
> New file, contains example for T9.html
>
Please group these together in a logical way, so that they are easier to
read. For example:
quickcheck/tutes/T*.html:
New files, covering each of the tutorial topics.
etc.
>
>
> Index: quickcheck/tutes/T1.html
> ===================================================================
> RCS file: T1.html
> diff -N T1.html
> --- /dev/null Wed Nov 15 09:24:47 2000
> +++ T1.html Mon Feb 5 16:01:52 2001
> @@ -0,0 +1,224 @@
> +<html>
> +<head>
> + <TITLE> QuickCheck </TITLE>
> +</head>
> +Files :
> +<a href="use.m">use.m</a>
> +<a href="use1.m">use1.m</a>
> +<a href="use11.m">use11.m</a>
> +<a href="testrev.m">testrev.m</a>
> +<a href="testrev2.m">testrev2.m</a><BR>
> +<a href="index.html">Back to main</a>
> +
> +<H1> QuickCheck Tutorial 1 </H1>
> +
> +
> +<pre>
> +In order to test the validity of a predicate/function via quickcheck,
> +the user needs the following components :
Since this is a tutorial, "the user" is the person reading this. So I
think it would be more appropriate to refer to them as "you". That is:
In order to test the validity of a predicate/function via
quickcheck, you need ...
> + qcheck.m -- quickcheck module (supplied)
> + rnd.m -- required by quickcheck module (supplied)
> + XXX.m -- module containing the predicate/function
> + the user is trying to validate
> + YYY.m -- main module, which calls qcheck/4
> +
> +A Simple Example
> +Let XXX.m be "testrev.m", which contains testrev/1 :
> +<P>
> +<table border=0 width=100% bgcolor=#eeeee0><tr><td><pre>
> + :- func testrev(list(T)) = list(T).
> + :- mode testrev(in) = out is det.
> + testrev([]) = [].
> + testrev([X|Xs]) = Ys :-
> + list__append(testrev(Xs), [X], Ys).
> +</pre></tr></table>
> +testrev/1 is the Mercury equivalent of Haskell's reverse function. We
> +want to test whether testrev/1 is properly implemented.
This function is widely used in examples of declarative programming, so
I wouldn't refer to it as Haskell's reverse. It is generally called
"naive reverse" (or nrev/1), because of the simple implementation. I would
change testrev to nrev in this example.
> +
> +Let YYY.m be "use.m". It must contain a main/2, and an
> +invariant function. If testrev/1 is correctly implemented then it
> +should satisfy the following rules:
> +<H4>
> + reverse [x] = [x]
> + reverse (xs+ys) = reverse ys + reverse xs
> + reverse (reverse xs) = xs
> +</H4>
> +To test whether 2nd rule holds for testrev(), define the invariant
> +function as :
> +<H4>
> + testing(Xs, Ys) =
> + testrev(Xs ++ Ys) `===` (testrev(Ys) ++ testrev(Xs)).
> +</H4>
> +`===` function returns 'property:[yes]' if the left side equals
> +right side, 'property:[no]' otherwise. ( like == in Haskell, but not of
> +type bool)
The comparison with '==' is probably not necessary, since you have just
described what the function actually does.
In theory testrev/1 can take a list(T), however in order
> +for quickcheck to automatically generate Xs and Ys, the programmer must
> +specify a fixed type at which the law is to be tested, eg:
> +<H4>
> + :- func testing(list(int), list(int)) = property.
> + Or
> + :- func testing(list(char), list(char)) = property.
> +</H4>
> +Now define the main() as
s,the main(),main/2,
> +<H4>
> + Main --- >
s/Main/main/
and also below.
> + qcheck(qcheck_f(testing), "sample testing").
> +</H4>
> +The complete use.m looks like this:
> +<P>
> +<table border=0 width=100% bgcolor=#eeeee0><tr><td><pre>
> +
> +:- module use.
> +
> +:- interface.
> +
> +:- use_module io.
> +
> +:- pred main(io__state, io__state).
> +:- mode main(di, uo) is det.
> +
> +%-------------------------------------------------------------------%
> +
> +:- implementation.
> +
> +:- import_module int, list.
> +:- import_module qcheck, testrev.
> +
> +%-------------------------------------------------------------------%
> +
> +main -->
> + qcheck(qcheck__f(testing), "sample testing").
> +
> +%-------------------------------------------------------------------%
> +% Invariant test functions
> +%-------------------------------------------------------------------%
> +
> +:- func testing(list(int), list(int)) = property.
> +testing(Xs, Ys) =
> + testrev(Xs ++ Ys) `===` (testrev(Ys) ++ testrev(Xs)).
> +</pre></tr></table>
> +To compiler the program, do "mmake use.depend", then "mmake use".
s/compiler/compile/
> +After running, the statistics should be displayed like :
> +<H4>
> + Test Description : sample testing
> + Number of test cases that succeeded : 100
> + Number of trivial tests : 0
> + Number of tests cases which failed the pre-condition : 0
> + Distributions of selected argument(s) :
> +</H4>
> +testrev/1 passed 100 tests and failed none.(ignore the last 3 lines
> +for now)
The full-stop should go at the end, to make this a correct sentence.
> +
> +If the invariant function is wrongly specified, or testrev/1 is not
> +properly implemented, then the statistics may look like :
> +<H4>
> + Test description : sample testing
> + Falsifiable :
> + [2]
> + [-2, 1]
> +</H4>
> +where running [2] as Xs and [-2, 1] as Ys failed the invariant function.
> +
> +Defining an invariant function which contains only functions is cleaner
The meaning of the word "cleaner" is not clear. I would say "more
concise" instead.
> +(as in Haskell) than one which contains predicates. Suppose that "testrev2.m"
I'm not sure how this relates to Haskell. I would remove the "(as in
Haskell)".
> +defines a predicate version of reversing list:
> +<P>
> +<table border=0 width=100% bgcolor=#eeeee0><tr><td><pre>
> + :- pred testrev2(list(T), list(T)).
> + :- mode testrev2(in, out) is det.
> + testrev2([], []).
> + testrev2([X|Xs], Ys):-
> + testrev2(Xs, Reversed),
> + list__append(Reversed, [X], Ys).
> +</pre></tr></table>
> +Then the invariant function looks like :
> +<h4>
> + testing2(Xs, Ys) = (Left `===` Right) :-
> + testrev2((Xs ++ Ys), Left),
> + testrev2(Ys, Part_a),
> + testrev2(Ys, Part_b),
> + Right = Part_a ++ Part_b.
> +</h4>
> +Above code is effectively the same as testing/2, however it requires a few extra
> +lines to extract the intermediate values, (use1.m) :
> +<P>
> +<table border=0 width=100% bgcolor=#eeeee0><tr><td><pre>
> +:- module use1.
> +
> +:- interface.
> +
> +:- use_module io.
> +
> +:- pred main(io__state, io__state).
> +:- mode main(di, uo) is det.
> +
> +%-------------------------------------------------------------------%
> +
> +:- implementation.
> +
> +:- import_module int, list.
> +:- import_module qcheck, testrev2.
> +
> +%-------------------------------------------------------------------%
> +
> +main -->
> + qcheck(qcheck__f(testing2), "testing2").
> +
> +%-------------------------------------------------------------------%
> +% Invariant test functions
> +%-------------------------------------------------------------------%
> +
> +:- func testing2(list(int), list(int)) = property.
> +testing2(Xs, Ys) = (Left `===` Right) :-
> + testrev2((Xs ++ Ys), Left),
> + testrev2(Ys, Part_a),
> + testrev2(Xs, Part_b),
> + Right = Part_a ++ Part_b.
> +</pre></tr></table>
> +An alternative to write use.m which does not evolving writing a separate
> +invariant function, (use11.m):
> +<P>
> +<table border=0 width=100% bgcolor=#eeeee0><tr><td><pre>
> +:- module use11.
> +
> +:- interface.
> +
> +:- use_module io.
> +
> +:- pred main(io__state, io__state).
> +:- mode main(di, uo) is det.
> +
> +%--------------------------------------------------------------------------%
> +
> +:- implementation.
> +
> +:- import_module int, list.
> +:- import_module qcheck, testrev.
> +
> +%--------------------------------------------------------------------------%
> +
> +main -->
> + qcheck(qcheck__f(func(Xs `with_type` list(int), Ys `with_type` list(int))
> + = testrev(Xs ++ Ys) `===` (testrev(Ys) ++ testrev(Xs))),
> + "sample testing").
> +</pre></tr></table>
This is a fairly normal use of higher order code, and probably doesn't
need its own example.
> +Summary
> +
> +In YYY.m, the main should be defined as :
> +<h4>
> + Main --->
> + qcheck(qcheck_f(XXXXXXXX), YYYYYYYY).
> +</h4>
You should use more meaningful variable names than these. The same
applies for other places in the tutorials.
> +Where XXXXXXXX is the (name of) invariant function and
XX... is a higher order term which represents the invariant. It doesn't
necessarily have to be the function name, as your last example shows.
> +YYYYYYYY is a string which describe/name/comment XXXXXXXX.
> +
> +The invariant function is of the form
> +<h4>
> + :- mode XXXXXXXX(in, in, in ...) = out.
> +</h4>
> +The inputs can be of any type. The output is of type 'property:[yes]' or 'peoperty:[no]'
s/peoperty/property/
That is the _value_ of the output, not the type. Also, the list can
have other flags as well -- it doesn't have to be just [yes] or [no].
You should mention that there can be any number of inputs (up to a
certain fixed limit).
> +What ever happens inside XXXXXXXX, quickcheck does not care. If output is [yes], the
> +invariant function is assumed to pass the test case satisfactory.
I would write:
If the output list contains `yes', then the invariant is assumed
to be satisfied.
> +
> +
> +</html>
> Index: quickcheck/tutes/T2.html
> ===================================================================
> RCS file: T2.html
> diff -N T2.html
> --- /dev/null Wed Nov 15 09:24:47 2000
> +++ T2.html Mon Feb 5 16:01:53 2001
> @@ -0,0 +1,133 @@
> +<html>
> +<head>
> + <TITLE> QuickCheck </TITLE>
> +</head>
> +<H1> QuickCheck Tutorial 2 </H1>
> +Files :
> +<a href="use20.m">use20.m</a>
> +<a href="use21.m">use21.m</a>
> +<a href="mymax.m">mymax.m</a><BR>
> +<a href="index.html">Back to main</a>
> +<h3> Conditional law </h3>
> +
> +<pre>
> +Conditional law
No need to repeat that line.
> +In general many laws hold only under certain conditions, Quickcheck provides
> +an implication combinator to represent such conditional laws.
That is really two sentences, not one, so
s/, /. /
Eg, the law
> + X <= Y => max x y == y
To be less ambiguous, write less-than-or-equal-to as '=<' instead of '<='.
> +can be represented by the definition :
> +<h4>
> + :- func law(int, int) = property.
> + law(X, Y) = (X =< Y) `===>` (mymax(X, Y) `===` Y).
> +</h4>
> +or alternatively can be represented as :
> +<h4>
> + :- func law2(int, int) = property.
> + law2(X, Y) = is_less_equal(X, Y) `===>` (mymax(X, Y) `===` Y).
> +
> + :- func is_less_equal(int, int) = bool.
> + is_less_equal(X, Y) = Bool :-
> + (if X =< Y
> + then
> + Bool = yes
> + else
> + Bool = no
> + ).
> +</h4>
> +The difference between law1 and law2 is the left argument of `===>`.
> +(X =< Y) is type (pred) and is_less_equal(X, Y) returns type bool.
> +`===>` is overloaded to take both types.
> +
> +
> +The complete use20.m :
> +<P>
> +<table border=0 width=100% bgcolor=#eeeee0><tr><td><pre>
> +:- module use20.
> +
> +:- interface.
> +
> +:- use_module io.
> +
> +:- pred main(io__state, io__state).
> +:- mode main(di, uo) is det.
> +
> +%---------------------------------------------------------------------------%
> +
> +:- implementation.
> +
> +:- import_module int, list, bool.
> +:- import_module qcheck, mymax.
> +
> +%---------------------------------------------------------------------------%
> +
> +main -->
> + qcheck(qcheck__f(law), "testing mymax using `===>` for (pred)"),
> + qcheck(qcheck__f(law2), "testing mymax using `===>` for bool").
> +
> +:- func law(int, int) = property.
> +law(X, Y) = (X =< Y) `===>` (mymax(X, Y) `===` Y).
> +
> +:- func law2(int, int) = property.
> +law2(X, Y) = is_less_equal(X, Y) `===>`
> + (mymax(X, Y) `===` Y).
> +
> +:- func is_less_equal(int, int) = bool.
> +is_less_equal(X, Y) = Bool :-
> + (if X =< Y
> + then
> + Bool = yes
> + else
> + Bool = no
> + ).
> +</pre></tr></table>
> +After running the program, test statistics will be something like:
> +<h4>
> + Test Description : testing mymax using `===>` for (pred)
> + Number of test cases that succeeded : 52
> + Number of trivial tests : 0
> + Number of tests cases which failed the pre-condition : 48
> + Distributions of selected argument(s) :
> +
> + Test Description : testing mymax using `===>` for bool
> + Number of test cases that succeeded : 52
> + Number of trivial tests : 0
> + Number of tests cases which failed the pre-condition : 48
> + Distributions of selected argument(s) :
> +</h4>
> +The default number of tests to run is 100, in the above test only 48/100
> +passed the invariant function, and none failed. However, 52/100 cases where
> +the inputs failed the prec-condition.
That is not a complete sentence.
s/prec-condition/pre-condition/
> +Note that both test cases succeeded 52/100 and failed pre-condition 48/100.
> +This is not a coincident; qcheck.m seeds the random generator on local time,
> +if qcheck is called twice within the same second, the number generated will
> +be the same.
s/coincident/coincidence/
Note that "coincidence" also means the fact of happening at the same
time, so this is probably a bad choice of words.
> +
> +The implication combinator can be compounded. For example, suppose mymax is
> +designed that if mymax(X, Y) is called, Y will never be zero. Thus test cases
> +with Y=0 should also be disregarded (use21.m) :
> +<h4>
> + :- func law(int, int) = property.
> + law(X, Y) = notzero(Y) `===>` ((X =< Y) `===>` (mymax(X, Y) `===` Y)).
> +
> + :- pred notzero(int).
> + :- mode notzero(in) is semidet.
> + notzero(X) :- X \= 0.
> +</h4>
> +
> +Summary:
> +<P>
> +<table border=0 width=100% bgcolor=#eeeee0><tr><td><pre>
> + :- type property == list(flag).
> +
> + :- type flag
> + ---> yes
> + ; no
> + ; trivial
> + ; info(univ)
> + ; condition.
> +</pre></tr></table>
> +All invariant functions must return a property, which is just a list of flags.
This isn't a summary, since it is introducing new material. I think
these comments should be much earlier in the tutorial, when invariant
functions are first introduced.
> +If the list contains one or more 'condition', the test result is ignored.
> +The implication combinator '===>' will add 'condition' into the property if
> +its left argument is '(pred):fail' or 'bool:no'.
> +</html>
> \ No newline at end of file
> Index: quickcheck/tutes/T3.html
> ===================================================================
> RCS file: T3.html
> diff -N T3.html
> --- /dev/null Wed Nov 15 09:24:47 2000
> +++ T3.html Mon Feb 5 16:01:53 2001
> @@ -0,0 +1,292 @@
> +<html>
> +<head>
> + <TITLE> QuickCheck </TITLE>
> +</head>
> +<H1> QuickCheck Tutorial 3 </H1>
> +Files :
> +<a href="use31.m">use31.m</a>
> +<a href="use33.m">use33.m</a>
> +<a href="testrev.m">testrev.m</a><BR>
> +<a href="index.html">Back to main</a>
> +<h3> Monitoring Test Data - to_trivial/3 </h3>
> +
> +<pre>
> +In Tutorial 1, the 3rd rule for reverse is that:
> + reverse (reverse xs) = xs
> +It's not much of a test if xs is empty list or list with only 1 element.
> +
> +Quickcheck can label a test being trivial via the function
> +to_trivial(), which does not change the meaning of a law, but it classifies
> +some of the test cases. Without classifying the invariant function could be :
> +<h4>
> + :- func testing1(list(float)) = property.
> + testing1(Xs) =
> + testrev (testrev Xs) `===` Xs.
> +</h4>
> +add to_trivial([], Xs, XXXXXX) where XXXXXX is the ordinary output.
Please write that as a complete sentence.
If the 1st
> +argument is equal to the 2nd argument, then that test case is labeled trivial,
> +in this case, an empty list.
> +<h4>
> + :- func testing2(list(float)) = property.
> + testing2(Xs) =
> + to_trivial([], Xs, testrev (testrev Xs) `===` Xs).
> +</h4>
> +Use compounded to_trivial to also classify list of 1 element as trivial
> +<h4>
> + :- func testing3(list(float)) = property.
> + testing3(Xs) =
> + to_trivial(1,
> + list_length(Xs),
> + to_trivial([], Xs, testrev(testrev(Xs)) `===` Xs)
> + ).
> +</h4>
> +The complete code (use31.m) :
> +<P>
> +<table border=0 width=100% bgcolor=#eeeee0><tr><td><pre>
> +:- module use31.
> +
> +:- interface.
> +
> +:- use_module io.
> +
> +:- pred main(io__state, io__state).
> +:- mode main(di, uo) is det.
> +
> +%---------------------------------------------------------------------------%
> +
> +:- implementation.
> +
> +:- import_module int, list, bool.
> +:- import_module qcheck, testrev.
> +
> +%---------------------------------------------------------------------------%
> +
> +main -->
> + qcheck(qcheck__f(testing1), "testing1"),
> + qcheck(qcheck__f(testing2), "testing2"),
> + qcheck(qcheck__f(testing3), "testing3").
> +
> +:- func testing1(list(float)) = property.
> +testing1(Xs) =
> + testrev(testrev(Xs)) `===` Xs.
> +
> +:- func testing2(list(float)) = property.
> +testing2(Xs) =
> + to_trivial([], Xs, testrev(testrev(Xs)) `===` Xs).
> +
> +:- func testing3(list(float)) = property.
> +testing3(Xs) =
> + to_trivial(1,
> + list_length(Xs),
> + to_trivial([], Xs, testrev(testrev(Xs)) `===` Xs)
> + ).
> +</pre></tr></table>
> +A sample output :
> +<h4>
> + Test Description : testing1
> + Number of test cases that succeeded : 100
> + Number of trivial tests : 0
> + Number of tests cases which failed the pre-condition : 0
> + Distributions of selected argument(s) :
> +
> + Test Description : testing2
> + Number of test cases that succeeded : 100
> + Number of trivial tests : 53
> + Number of tests cases which failed the pre-condition : 0
> + Distributions of selected argument(s) :
> +
> + Test Description : testing3
> + Number of test cases that succeeded : 100
> + Number of trivial tests : 75
> + Number of tests cases which failed the pre-condition : 0
> + Distributions of selected argument(s) :
> +</h4>
> +Note test1, the original, has no trivial cases. With test2, 53/100 tests have
> +an empty list as its input. Test3 shows 75/100 tests have either empty lists or
> +lists of only one element.
s/either empty lists or lists/either an empty list or a list/
It had only tested 25/100 cases where the list is
> +longer than 1 element.
s/had //
> +
> +
> +<h3> Monitoring Test Data - `>>>` </h3>
> +The combinator `>>>` will gather all values that are passed to it, and prints out
> +a histogram of these values.
s/will gather/gathers/
> +Let's use `>>>` to find out exactly what lists are generate for the previous tests :
s/generate/generated/
> +<h4>
> + :- func testing4(list(float)) = property.
> + testing4(Xs) =
> + list_length(Xs) `>>>` (testrev(testrev(Xs)) `===` Xs).
> +</h4>
> +The combinator `>>>` will convert it's left argument to a univ, and push info(univ)
s/it's/its/
> +into it's right argument(which is a property == a list of flags).
It would be better to say "into the property list".
> +The complete code (use31.m) :
> +<P>
> +<table border=0 width=100% bgcolor=#eeeee0><tr><td><pre>
> +:- module use31.
> +
> +:- interface.
> +
> +:- use_module io.
> +
> +:- pred main(io__state, io__state).
> +:- mode main(di, uo) is det.
> +
> +%---------------------------------------------------------------------------%
> +
> +:- implementation.
> +
> +:- import_module int, list, bool.
> +:- import_module qcheck, testrev.
> +
> +%---------------------------------------------------------------------------%
> +
> +main -->
> + qcheck(qcheck__f(testing1), "testing1"),
> + qcheck(qcheck__f(testing2), "testing2"),
> + qcheck(qcheck__f(testing3), "testing3"),
> + qcheck(qcheck__f(testing4), "testing4").
> +
> +:- func testing1(list(float)) = property.
> +testing1(Xs) =
> + testrev(testrev(Xs)) `===` Xs.
> +
> +:- func testing2(list(float)) = property.
> +testing2(Xs) =
> + to_trivial([], Xs, testrev(testrev(Xs)) `===` Xs).
> +
> +:- func testing3(list(float)) = property.
> +testing3(Xs) =
> + to_trivial(1,
> + list_length(Xs),
> + to_trivial([], Xs, testrev(testrev(Xs)) `===` Xs)
> + ).
> +
> +:- func testing4(list(float)) = property.
> +testing4(Xs) =
> + list_length(Xs) `>>>` (testrev(testrev(Xs)) `===` Xs).
> +</pre></tr></table>
> +A sample output :
> +<h4>
> +Test Description : testing1
> +Number of test cases that succeeded : 100
> +Number of trivial tests : 0
> +Number of tests cases which failed the pre-condition : 0
> +Distributions of selected argument(s) :
> +
> +Test Description : testing2
> +Number of test cases that succeeded : 100
> +Number of trivial tests : 53
> +Number of tests cases which failed the pre-condition : 0
> +Distributions of selected argument(s) :
> +
> +Test Description : testing3
> +Number of test cases that succeeded : 100
> +Number of trivial tests : 71
> +Number of tests cases which failed the pre-condition : 0
> +Distributions of selected argument(s) :
> +
> +Test Description : testing4
> +Number of test cases that succeeded : 100
> +Number of trivial tests : 0
> +Number of tests cases which failed the pre-condition : 0
> +Distributions of selected argument(s) :
> +1 8
> +1 4
> +1 6
> +2 5
> +8 3
> +16 2
> +18 1
> +53 0
> +</h4>
> +The display of testing4 shows that 53 cases of lenggh == 0
s/length/lenggh/
> + 18 cases of length == 1
> + 16 cases of length == 2
> + ...etc...
> +53+18 cases = 71 cases, which was marked trivial in testing3, likewise
s/was/were/
> +for testing2. The number will add up only if all the tests were ran within
> +the same second.
s/number/numbers/
s/ran/run/
I would write "with the same random number seed" rather than "within the
same second", otherwise the reason for this might not be clear.
> +
> +
> +The value passed to `>>>` does not have to be the same type, and `>>>` can
> +be compounded like to_trivial/3, eg (use33.m):
> +<P>
> +<table border=0 width=100% bgcolor=#eeeee0><tr><td><pre>
> +:- module use33.
> +
> +:- interface.
> +
> +:- use_module io.
> +
> +:- pred main(io__state, io__state).
> +:- mode main(di, uo) is det.
> +
> +%---------------------------------------------------------------------------%
> +
> +:- implementation.
> +
> +:- import_module int, list, bool.
> +:- import_module qcheck, testrev.
> +
> +%---------------------------------------------------------------------------%
> +
> +main -->
> + qcheck(qcheck__f(testing5), "testing5").
> +
> +:- func testing5(list(float)) = property.
> +testing5(Xs) =
> + odd_even(Xs) `>>>`
> + (list_length(Xs) `>>>` (testrev(testrev(Xs)) `===` Xs)).
> +
> +:- func odd_even(list(T)) = string.
> +:- mode odd_even(in) = out is det.
> +odd_even(Xs) = Y :-
> + (if list_length(Xs) mod 2 = 1
> + then
> + Y = "odd"
> + else
> + Y = "even"
> + ).
> +</pre></tr></table>
> +testing5 collects the list_length, and also collect "odd" or "even"
> +A sample output :
> +<h4>
> +Test Description : testing5
> +Number of test cases that succeeded : 100
> +Number of trivial tests : 0
> +Number of tests cases which failed the pre-condition : 0
> +Distributions of selected argument(s) :
> +1 7
> +1 5
> +2 4
> +2 6
> +8 3
> +10 2
> +29 1
> +39 "odd"
> +47 0
> +61 "even"
> +</h4>
I think the following point is self evident.
> +The display of testing5 shows that 61 cases of "even"
> + 47 cases of length == 0
> + 39 cases of "odd"
> + ...etc...
You can leave off the following, since it is not particularly relevant
to a tutorial.
> +Currently the display is sorted by the number of occurences, without any regards to
> +the type.
> +<h4>
> +1 7
> +1 5
> +2 4
> +2 6
> +8 3
> +1 B
> +1 A
> +2 C
> +10 Z
> +</h4>
> +May implement to above display later....
> +
> +
> +
> +
> +
> +
To be continued ...
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to: mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions: mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------
More information about the developers
mailing list