1 -----------------------------------------------------------------------------
3 -- Module : Debug.QuickCheck.Batch
4 -- Copyright : (c) Andy Gill 2001
5 -- License : BSD-style (see the file libraries/base/LICENSE)
7 -- Maintainer : libraries@haskell.org
8 -- Stability : experimental
9 -- Portability : non-portable (uses Control.Exception, Control.Concurrent)
11 -- This is a batch driver for running QuickCheck (GHC only).
13 -----------------------------------------------------------------------------
16 - Here is the key for reading the output.
18 - ? = every example passed, but quickcheck did not find enough good examples
19 - * = test aborted for some reason (out-of-time, bottom, etc)
20 - # = test failed outright
22 - We also provide the dangerous "isBottom".
24 - Here is is an example of use for sorting:
26 - testOptions :: TestOptions
27 - testOptions = TestOptions
28 - { no_of_tests = 100 -- number of tests to run
29 - , length_of_tests = 1 -- 1 second max per check
30 - -- where a check == n tests
31 - , debug_tests = False -- True => debugging info
34 - prop_sort1 xs = sort xs == sortBy compare xs
35 - where types = (xs :: [OrdALPHA])
38 - (head (sort xs) == minimum xs)
39 - where types = (xs :: [OrdALPHA])
40 - prop_sort3 xs = (not (null xs)) ==>
41 - last (sort xs) == maximum xs
42 - where types = (xs :: [OrdALPHA])
46 - (head (sort (xs ++ ys)) == min (minimum xs) (minimum ys))
47 - where types = (xs :: [OrdALPHA], ys :: [OrdALPHA])
51 - (last (sort (xs ++ ys)) == max (maximum xs) (maximum ys))
52 - where types = (xs :: [OrdALPHA], ys :: [OrdALPHA])
56 - (head (sort (xs ++ ys)) == max (maximum xs) (maximum ys))
57 - where types = (xs :: [OrdALPHA], ys :: [OrdALPHA])
59 - test_sort = runTests "sort" testOptions
67 - When run, this gives
71 - You would tie together all the test_* functions
72 - into one test_everything, on a per module basis.
74 - Examples of use of bottom and isBottom:
75 - {- test for abort -}
76 - prop_head2 = isBottom (head [])
77 - {- test for strictness -}
78 - prop_head3 = isBottom (head bottom)
81 module Debug.QuickCheck.Batch
82 ( run -- :: Testable a => a -> TestOptions -> IO TestResult
83 , runTests -- :: String -> TestOptions ->
84 -- [TestOptions -> IO TestResult] -> IO ()
85 , defOpt -- :: TestOptions
88 , isBottom -- :: a -> Bool
89 , bottom -- :: a {- _|_ -}
95 import Control.Concurrent
96 import Control.Exception hiding (catch, evaluate)
97 import qualified Control.Exception as Exception (catch, evaluate)
98 import Debug.QuickCheck
99 import System.IO.Unsafe
101 data TestOptions = TestOptions {
103 length_of_tests :: Int,
104 debug_tests :: Bool }
106 defOpt :: TestOptions
109 , length_of_tests = 1
110 , debug_tests = False
113 data TestResult = TestOk String Int [[String]]
114 | TestExausted String Int [[String]]
115 | TestFailed [String] Int
116 | TestAborted Exception
118 tests :: Config -> Gen Result -> StdGen -> Int -> Int -> [[String]]
120 tests config gen rnd0 ntest nfail stamps
121 | ntest == configMaxTest config = return (TestOk "OK, passed" ntest stamps)
122 | nfail == configMaxFail config = return (TestExausted "Arguments exhausted after"
125 do (if not (null txt) then putStr txt else return ())
128 tests config gen rnd1 ntest (nfail+1) stamps
130 tests config gen rnd1 (ntest+1) nfail (stamp result:stamps)
132 do return (TestFailed (arguments result) ntest)
134 txt = configEvery config ntest (arguments result)
135 result = generate (configSize config ntest) rnd2 gen
136 (rnd1,rnd2) = split rnd0
140 , configMaxFail = n * 10
141 , configSize = (+ 3) . (`div` 2)
142 , configEvery = \n args -> if v then show n ++ ":\n" ++ unlines args else ""
145 -- Here we use the same random number each time,
146 -- so we get reproducable results!
147 run :: Testable a => a -> TestOptions -> IO TestResult
148 run a TestOptions { no_of_tests = n, length_of_tests = len, debug_tests = debug } =
150 ready <- newEmptyMVar
154 -- This waits a bit, then raises an exception in its parent,
155 -- saying, right, you've had long enough!
156 watcher <- forkIO (Exception.catch
157 (do threadDelay (len * 1000 * 1000)
159 throwTo me NonTermination
162 -- Tell the watcher we are starting...
164 -- This is cheating, because possibly some of the internal message
165 -- inside "r" might be _|_, but anyway....
167 -- Now, we turn off the watcher.
168 -- Ignored if the watcher is already dead,
169 -- (unless some unlucky thread picks up the same name)
174 Left e -> return (TestAborted e)
176 theTest = tests (batch n debug) (evaluate a) (mkStdGen 0) 0 0 []
178 -- Prints a one line summary of various tests with common theme
179 runTests :: String -> TestOptions -> [TestOptions -> IO TestResult] -> IO ()
180 runTests name scale actions =
181 do putStr (rjustify 25 name ++ " : ")
182 f <- tr 1 actions [] 0
186 rjustify n s = replicate (max 0 (n - length s)) ' ' ++ s
189 putStr (rjustify (max 0 (35-n)) " (" ++ show c ++ ")\n")
191 tr n (action:actions) others c =
196 tr (n+1) actions others (c+m) }
197 (TestExausted s m ss)
200 tr (n+1) actions others (c+m) }
203 tr (n+1) actions others c }
206 tr (n+1) actions ((f,n,num):others) (c+num) }
208 fa :: ([String],Int,Int) -> IO ()
215 ++ " failed with the binding(s)\n")
216 sequence_ [putStr (" ** " ++ v ++ "\n")
220 -- Look out behind you! These can be misused badly.
221 -- However, in the context of a batch tester, can also be very useful.
225 isBottom :: a -> Bool
226 isBottom a = unsafePerformIO (do
227 a' <- try (Exception.evaluate a)
229 Left _ -> return True
230 Right _ -> return False)