Testing
For the exercises below you may want to consult the functions provided
by the QuickCheck library, in particular functions such as choose,
sized, elements and frequency.
We encourage experimenting with your code in an interpreter
session. To be able to experiment with QuickCheck, the first two
exercises work better if you can show functions. For that you can
add the following definitions to your code. We’ve also packaged up
these helper definitions in a small cabal package. You can run the code using cabal run, or start an
interpeter using cabal repl Main.hs. Alternatively, include this at
the top of your file:
{-# LANGUAGE FlexibleInstances #-} -- put this at the top of your file
instance (Enum a, Bounded a, Show a) => Show (a -> Bool) where
show f = intercalate "\n" (map (\x -> "f " ++ show x ++ " = " ++ show (f x)) [minBound .. maxBound])Also when you run your tests, you sometimes need to specialize the types a bit. For example, the following code calls all kinds of test functions that the exercises below (except for 4) expect you to come up with.
runTests :: IO ()
runTests = do
putStrLn "\nExercise 14.1"
quickCheck (propFilterNoLonger :: (Bool -> Bool) -> [Bool] -> Bool)
quickCheck (propFilterAllSatisfy :: (Bool -> Bool) -> [Bool] -> Bool)
quickCheck (propFilterAllElements :: (Bool -> Bool) -> [Bool] -> Bool)
quickCheck (propFilterCorrect :: (Bool -> Bool) -> [Bool] -> Bool)
putStrLn "\nExercise 14.2"
quickCheck (propMapLength :: (Bool -> Bool) -> [Bool] -> Bool)
putStrLn "\nExercise 14.3"
quickCheck $ once (propPermsLength :: [Int] -> Bool)
quickCheck $ once (propPermsArePerms :: [Int] -> Bool)
quickCheck $ once (propPermsCorrect :: [Int] -> Bool)
putStrLn "\nExercise 14.5"
quickCheck (forAll genBSTI isSearchTree) -- Use forAll to use custom generator
quickCheck (forAll genBSTI propInsertIsTree)
quickCheck (forAll genBSTI propInsertIsTreeWrong)1 atHome
Consider the ubiquitous filter function. There are many properties
that you can formulate for the input-output behaviour of filter.
Formulate the QuickCheck property that the result list cannot be longer than the input.
Formulate the QuickCheck property that all elements in the result list satisfy the given property.
Formulate the QuickCheck property that all elements in the result list are present in the input list.
Formulate a set of QuickCheck properties to completely characterize the
filterfunction (you may choose also from among the three you have just implemented). Make sure to remove properties that are implied by (a subset of) the other properties.
2 atHome
Try to come up with a number of QuickCheck-verifiable properties for
the map function, and implement these. Are there any properties of
map that are awkward to verify?
3 atHome
Consider the function permutations from the Data.List library,
which computes all the possible permutations of elements in a list. We
shall be writing QuickCheck tests to verify that this function.
Write a QuickCheck property that checks that the correct number of permutations is generated.
Write a function
isPerm :: Eq a => [a] -> [a] -> Boolthat verifies that the two argument lists are permutations of each other.Write the QuickCheck property that every list in the output of
permutationsis a permutation of the input.Formulate a set of properties to completely characterize the
permutationsfunction (you may choose also from among the ones you have just implemented). Make sure to remove properties that are implied by (a subset of) the other properties. Implement the properties that you still need as QuickCheck properties.
4 atHome
Do something similar for the function inits defined in Lecture 3.
5 atHome
Consider the following datatype definition for binary trees that we shall want to use to implement binary search trees:
data Tree a = Branch a (Tree a) (Tree a) | LeafWrite a function isSearchTree :: Ord a => Tree a -> Bool that verifies
that its argument is a binary search tree. Then write a function
genBSTI :: Gen (Tree Int) that generates binary search trees. Now test the property
that given a binary search tree t, inserting a value into the
tree results in yet another binary search tree. The code for
inserting a new value into the tree is:
insertTree :: Ord a => a -> Tree a -> Tree a
insertTree e Leaf = Branch e Leaf Leaf
insertTree e (Branch x li re)
| e <= x = Branch x (insertTree e li) re
| e > x = Branch x li (insertTree e re)Experiment with mutating the implementation of insertTree to
find out whether your property can in fact discover that the
mutated implementation no longer maps binary search trees to
binary search trees.