Lab 5
Language: Intermediate Student with lambda
This lab explores the idea of generating random inputs to a function and then testing that the function works properly for those random inputs. This consists of three major pieces.
First, you must write a function you wish to test.
Second, you must come up with a property that the function should have and implement this property as a predicate (ie, a function that returns true when the property holds, and false when it doesn’t.)
Third, you must come up with a way to generate random inputs to the function.
This lab works thru two examples of how to do this.
Certainly random testing has its limitations, but you may be surprised by how quickly it finds ordinary errors!
1 Random Testing Teachpack
To help with the actual testing, we have provided the teachpack rand-test.ss. Skim this section and skip ahead to the next, referring back here as appropriate to complete the exercises in the next two sections.
The teachpack provides three constructs. The first, check-property, is an extension of check-expect that repeatedly tests some predicate of a function.
(check-property number predicate argument-list) |
check-property runs the argument-list expression number times. Each time it is expected to produce a (random) list of arguments suitable for predicate. check-property then passes the elements of that list to predicate.
If predicate ever returns false, then check-property prints out the value of the argument-list. If it always returns true, check-property informs you that it could not find a failing test within number tries.
(If you cannot find failing tests within thousands of tries, then probably random testing cannot find a failing test.)
(make-fake-random N ) |
→ (-> natural-number/c natural-number/c) |
N : natural-number/c |
The make-fake-random procedure accepts one or more numbers and returns a procedure that looks like random, except that the function is not actually random. Instead, the function returns the arguments passed to make-fake-random, one at a time.
For example,
(define not-random (make-fake-random 1 2 3 4)) |
(list (not-random 7) (not-random 7) (not-random 7) (not-random 7)) |
returns the list '(1 2 3 4).
(find-image/00 i1 i2) → posn? |
i1 : image? |
i2 : image? |
Just like find-image, except this function always returns the location of i2 relative to the upper-left corner of i1, rather than relative to the pinholes.
2 Testing beside
The beside function from the first homework should have the property that the x-position of the second image in the result is the same as the width of the first image. Formulate this property as a predicate using find-image/00 (above).
The next step is to generate a random, solid colored image with a random pinhole. Design the following function:
(random-solid-rectangle c n) → image? c : color n : natural-number/c Returns a solid rectangle of color c with a random width and height (bounded by n) and a pinhole in a random place (using put-pinhole).
Use check-property to test your version of beside with the predicate you wrote just above. Fix beside if it finds a bug. (If the graded from homework 1 identified a problem with your beside function, do not fix it until check-property finds the problem (because that’s more fun).)
(Beware that the bug could be in the property as well as in the function. Indeed, the way the property is specified above, it is slightly wrong.)
You may have noticed that it is difficult to test random-solid-rectangle. And, in general, it is difficult to test functions that return random results. For this lab, therefore, we will not have functions that directly call random any longer. Instead, all of our random generation functions will take an extra argument named random that they use to generate random numbers and we will test the random generation functions using make-fake-random (above).
Rewrite random-solid-rectangle so that it now has the following contract and purpose, and thus can be tested properly. Test it.
(random-solid-rectangle c n random) → image? |
c : color |
n : natural-number/c |
random : (-> natural-number/c natural-number/c) |
Returns a solid rectangle of color c with a random width and height (bounded by n) and a pinhole in a random place (using put-pinhole).
Uses random to choose the sizes.
3 Testing plot-dataset
The plot-dataset function from lab 3 should have the property that plot-dataset returns the same size image for an empty list as it does for a list of length 1, as long as the max and min arguments are the same.
Design a predicate that captures that property.
(good-plot-dataset min-x max-x min-y max-y p) → boolean? min-x : number? max-x : number? min-y : number? max-y : number? p : posn? Returns true when the image produced by
(plot-dataset min-x max-x min-y max-y '())
has the same size as
(plot-dataset min-x max-x min-y max-y (list p))
under the assumption that that the posn is inside the rectangle bounded by the other arguments.
The built-in random function always has an upper bound on the random number it returns. Design a random number generation that lifts this restriction by simulating a sequence of biased coin tosses.
(random-natural random) → natural-number/c random : (-> natural-number/c natural-number/c) With probability 1/10 random-natural returns 0, and with probablility 9/10, it adds one to a recursive call to itself.
Using random-natural, design random-integer (which negates a random natural 1/2 of the time). Use these two (and any other useful random number generating functions you might want) to formulate a check-property expression for testing plot-dataset.