1 -----------------------------------------------------------------------------
3 -- Module : Debug.QuickCheck
4 -- Copyright : (c) Koen Claessen, John Hughes 2001
5 -- License : BSD-style (see the file libraries/core/LICENSE)
7 -- Maintainer : libraries@haskell.org
8 -- Stability : experimental
9 -- Portability : portable
12 -- DRAFT implementation; last update 000104.
13 -- Koen Claessen, John Hughes.
14 -- This file represents work in progress, and might change at a later date.
16 -----------------------------------------------------------------------------
18 module Debug.QuickCheck
20 ( quickCheck -- :: prop -> IO ()
21 , verboseCheck -- :: prop -> IO ()
22 , test -- :: prop -> IO () -- = quickCheck
25 , check -- :: Config -> prop -> IO ()
27 -- property combinators
28 , forAll -- :: Gen a -> (a -> prop) -> prop
29 , (==>) -- :: Bool -> prop -> prop
31 -- gathering test-case information
32 , label -- :: String -> prop -> prop
33 , collect -- :: Show a => a -> prop -> prop
34 , classify -- :: Bool -> String -> prop -> prop
35 , trivial -- :: Bool -> prop -> prop
37 -- generator combinators
38 , Gen -- :: * -> * ; Functor, Monad
40 , elements -- :: [a] -> Gen a
41 , two -- :: Gen a -> Gen (a,a)
42 , three -- :: Gen a -> Gen (a,a,a)
43 , four -- :: Gen a -> Gen (a,a,a,a)
45 , sized -- :: (Int -> Gen a) -> Gen a
46 , resize -- :: Int -> Gen a -> Gen a
47 , choose -- :: Random a => (a, a) -> Gen a
48 , oneof -- :: [Gen a] -> Gen a
49 , frequency -- :: [(Int, Gen a)] -> Gen a
51 , vector -- :: Arbitrary a => Int -> Gen [a]
54 , Arbitrary(..) -- :: class
55 , rand -- :: Gen StdGen
56 , promote -- :: (a -> Gen b) -> Gen (a -> b)
57 , variant -- :: Int -> Gen a -> Gen a
60 , Testable(..) -- :: class
63 -- For writing your own driver
64 , Result(..) -- :: data
65 , generate -- :: Int -> StdGen -> Gen a -> a
66 , evaluate -- :: Testable a => a -> Gen Result
71 import Data.List( group, sort, intersperse )
72 import Control.Monad( liftM2, liftM3, liftM4 )
77 --------------------------------------------------------------------
81 = Gen (Int -> StdGen -> a)
83 sized :: (Int -> Gen a) -> Gen a
84 sized fgen = Gen (\n r -> let Gen m = fgen n in m n r)
86 resize :: Int -> Gen a -> Gen a
87 resize n (Gen m) = Gen (\_ r -> m n r)
90 rand = Gen (\n r -> r)
92 promote :: (a -> Gen b) -> Gen (a -> b)
93 promote f = Gen (\n r -> \a -> let Gen m = f a in m n r)
95 variant :: Int -> Gen a -> Gen a
96 variant v (Gen m) = Gen (\n r -> m n (rands r !! (v+1)))
98 rands r0 = r1 : rands r2 where (r1, r2) = split r0
100 generate :: Int -> StdGen -> Gen a -> a
101 generate n rnd (Gen m) = m size rnd'
103 (size, rnd') = randomR (0, n) rnd
105 instance Functor Gen where
106 fmap f m = m >>= return . f
108 instance Monad Gen where
109 return a = Gen (\n r -> a)
111 Gen (\n r0 -> let (r1,r2) = split r0
117 choose :: Random a => (a, a) -> Gen a
118 choose bounds = (fst . randomR bounds) `fmap` rand
120 elements :: [a] -> Gen a
121 elements xs = (xs !!) `fmap` choose (0, length xs - 1)
123 vector :: Arbitrary a => Int -> Gen [a]
124 vector n = sequence [ arbitrary | i <- [1..n] ]
126 oneof :: [Gen a] -> Gen a
127 oneof gens = elements gens >>= id
129 frequency :: [(Int, Gen a)] -> Gen a
130 frequency xs = choose (1, tot) >>= (`pick` xs)
132 tot = sum (map fst xs)
136 | otherwise = pick (n-k) xs
140 two :: Monad m => m a -> m (a, a)
141 two m = liftM2 (,) m m
143 three :: Monad m => m a -> m (a, a, a)
144 three m = liftM3 (,,) m m m
146 four :: Monad m => m a -> m (a, a, a, a)
147 four m = liftM4 (,,,) m m m m
149 --------------------------------------------------------------------
152 class Arbitrary a where
154 coarbitrary :: a -> Gen b -> Gen b
156 instance Arbitrary () where
157 arbitrary = return ()
158 coarbitrary _ = variant 0
160 instance Arbitrary Bool where
161 arbitrary = elements [True, False]
162 coarbitrary b = if b then variant 0 else variant 1
164 instance Arbitrary Int where
165 arbitrary = sized $ \n -> choose (-n,n)
166 coarbitrary n = variant (if n >= 0 then 2*n else 2*(-n) + 1)
168 instance Arbitrary Integer where
169 arbitrary = sized $ \n -> choose (-fromIntegral n,fromIntegral n)
170 coarbitrary n = variant (fromInteger (if n >= 0 then 2*n else 2*(-n) + 1))
172 instance Arbitrary Float where
173 arbitrary = liftM3 fraction arbitrary arbitrary arbitrary
174 coarbitrary x = coarbitrary (decodeFloat x)
176 instance Arbitrary Double where
177 arbitrary = liftM3 fraction arbitrary arbitrary arbitrary
178 coarbitrary x = coarbitrary (decodeFloat x)
180 fraction a b c = fromInteger a + (fromInteger b / (abs (fromInteger c) + 1))
182 instance (Arbitrary a, Arbitrary b) => Arbitrary (a, b) where
183 arbitrary = liftM2 (,) arbitrary arbitrary
184 coarbitrary (a, b) = coarbitrary a . coarbitrary b
186 instance (Arbitrary a, Arbitrary b, Arbitrary c) => Arbitrary (a, b, c) where
187 arbitrary = liftM3 (,,) arbitrary arbitrary arbitrary
188 coarbitrary (a, b, c) = coarbitrary a . coarbitrary b . coarbitrary c
190 instance (Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d)
191 => Arbitrary (a, b, c, d)
193 arbitrary = liftM4 (,,,) arbitrary arbitrary arbitrary arbitrary
194 coarbitrary (a, b, c, d) =
195 coarbitrary a . coarbitrary b . coarbitrary c . coarbitrary d
197 instance Arbitrary a => Arbitrary [a] where
198 arbitrary = sized (\n -> choose (0,n) >>= vector)
199 coarbitrary [] = variant 0
200 coarbitrary (a:as) = coarbitrary a . variant 1 . coarbitrary as
202 instance (Arbitrary a, Arbitrary b) => Arbitrary (a -> b) where
203 arbitrary = promote (`coarbitrary` arbitrary)
204 coarbitrary f gen = arbitrary >>= ((`coarbitrary` gen) . f)
206 --------------------------------------------------------------------
210 = Result { ok :: Maybe Bool, stamp :: [String], arguments :: [String] }
213 nothing = Result{ ok = Nothing, stamp = [], arguments = [] }
218 result :: Result -> Property
219 result res = Prop (return res)
221 evaluate :: Testable a => a -> Gen Result
222 evaluate a = gen where Prop gen = property a
224 class Testable a where
225 property :: a -> Property
227 instance Testable () where
228 property _ = result nothing
230 instance Testable Bool where
231 property b = result (nothing{ ok = Just b })
233 instance Testable Result where
234 property res = result res
236 instance Testable Property where
239 instance (Arbitrary a, Show a, Testable b) => Testable (a -> b) where
240 property f = forAll arbitrary f
242 forAll :: (Show a, Testable b) => Gen a -> (a -> b) -> Property
243 forAll gen body = Prop $
245 res <- evaluate (body a)
246 return (argument a res)
248 argument a res = res{ arguments = show a : arguments res }
250 (==>) :: Testable a => Bool -> a -> Property
251 True ==> a = property a
252 False ==> a = property ()
254 label :: Testable a => String -> a -> Property
255 label s a = Prop (add `fmap` evaluate a)
257 add res = res{ stamp = s : stamp res }
259 classify :: Testable a => Bool -> String -> a -> Property
260 classify True name = label name
261 classify False _ = property
263 trivial :: Testable a => Bool -> a -> Property
264 trivial = (`classify` "trivial")
266 collect :: (Show a, Testable b) => a -> b -> Property
267 collect v = label (show v)
269 --------------------------------------------------------------------
273 { configMaxTest :: Int
274 , configMaxFail :: Int
275 , configSize :: Int -> Int
276 , configEvery :: Int -> [String] -> String
281 { configMaxTest = 100
282 , configMaxFail = 1000
283 , configSize = (+ 3) . (`div` 2)
284 , configEvery = \n args -> let s = show n in s ++ [ '\b' | _ <- s ]
289 { configEvery = \n args -> show n ++ ":\n" ++ unlines args
292 test, quickCheck, verboseCheck :: Testable a => a -> IO ()
294 quickCheck = check quick
295 verboseCheck = check verbose
297 check :: Testable a => Config -> a -> IO ()
300 tests config (evaluate a) rnd 0 0 []
302 tests :: Config -> Gen Result -> StdGen -> Int -> Int -> [[String]] -> IO ()
303 tests config gen rnd0 ntest nfail stamps
304 | ntest == configMaxTest config = do done "OK, passed" ntest stamps
305 | nfail == configMaxFail config = do done "Arguments exhausted after" ntest stamps
307 do putStr (configEvery config ntest (arguments result))
310 tests config gen rnd1 ntest (nfail+1) stamps
312 tests config gen rnd1 (ntest+1) nfail (stamp result:stamps)
314 putStr ( "Falsifiable, after "
317 ++ unlines (arguments result)
320 result = generate (configSize config ntest) rnd2 gen
321 (rnd1,rnd2) = split rnd0
323 done :: String -> Int -> [[String]] -> IO ()
324 done mesg ntest stamps =
325 do putStr ( mesg ++ " " ++ show ntest ++ " tests" ++ table )
334 . filter (not . null)
338 display [x] = " (" ++ x ++ ").\n"
339 display xs = ".\n" ++ unlines (map (++ ".") xs)
341 pairLength xss@(xs:_) = (length xss, xs)
342 entry (n, xs) = percentage n ntest
344 ++ concat (intersperse ", " xs)
346 percentage n m = show ((100 * n) `div` m) ++ "%"
348 --------------------------------------------------------------------