1 % -----------------------------------------------------------------------------
2 % $Id: AxiomTesting.lhs,v 1.1 1999/10/25 05:19:22 andy Exp $
4 % (c) The Hugs/GHC Team 1999
7 This is a testing framework for using axiomatic like specifications
23 -- advanced user functions below
35 import Exception (tryAll)
36 import IOExts (unsafePtrEq)
42 ------------------------------------------------------------------------------
44 newtype TestM a = TestM { runTestM :: TestMState -> IO (TestMResult a) }
46 data TestMState = TestMState {
48 bindingPairs :: [(String,String)]
51 initTestMState ref = TestMState {
58 | TestMFail TestMState
59 | TestMOk [(a,TestMState)]
61 runTestsM :: (a -> TestM b) -> [(a,TestMState)]
62 -> [(b,TestMState)] -> Int -> IO (TestMResult b)
63 runTestsM f [] [] n = return (TestMComplete n)
64 runTestsM f [] xs n = return (TestMOk xs)
65 runTestsM f ((a,s):as) ys n =
66 do r <- runTestM (f a) s
68 (TestMFail _) -> return r
69 (TestMComplete m) -> runTestsM f as ys (n+m)
70 (TestMOk xs) -> runTestsM f as (xs++ys) n
72 instance Monad TestM where
73 return v = TestM (\ b -> return (TestMOk [(v,b)]))
74 p >>= f = TestM (\ b ->
75 do res <- runTestM p b
77 (TestMComplete m) -> return (TestMComplete m)
78 (TestMFail f) -> return (TestMFail f)
79 -- The following pattern is an optimization
80 TestMOk [(x,s)] -> runTestM (f x) s
81 TestMOk xs -> runTestsM f xs [] 0)
83 runIOTestM :: IO a -> TestM a
84 runIOTestM m = TestM (\ b -> do { r <- m ; return (TestMOk [(r,b)]) })
86 testComplete = TestM (\ b -> return (TestMComplete 1))
87 testFail = TestM (\ b -> return (TestMFail b))
90 oneTest :: TestM () -> TestM ()
92 TestM (\ b -> do r <- runTestM p b
94 (TestMComplete n) -> return (TestMComplete 1)
95 other -> return other)
98 - This also has the nice effect of stoping the bindings
99 - of vars escaping for each side of the test.
100 - This is why (>>=) does not have this semantics.
104 (&&&) :: TestM () -> TestM () -> TestM ()
106 TestM (\ b -> do r <- runTestM p b
109 do r2 <- runTestM q b
111 (TestMComplete m) -> return (TestMComplete (n+m))
112 other -> return other
113 (TestMFail _) -> return r
114 _ -> return (error "&&& failed"))
117 (|||) :: TestM () -> TestM () -> TestM ()
119 TestM (\ b -> do r <- runTestM p b
121 (TestMComplete n) -> return r
122 (TestMFail _) -> runTestM q b
123 _ -> return (error "||| failed"))
125 pairUp :: String -> [(a,String)] -> TestM a
129 (a,b { bindingPairs = (name,r) : bindingPairs b })
132 funVar :: String -> String -> TestM ()
133 funVar name r = pairUp name [((),r)]
136 uniqId = TestM (\ s ->
137 do v <- readIORef (uniqIds s)
139 writeIORef (uniqIds s) v'
140 return (TestMOk [(v',s)]))
142 - For debugging, you can make the monad display each binding
145 displayVars :: TestM ()
146 displayVars = TestM (\ s ->
148 sequence_ [putStr (" ** " ++ k ++ " = " ++ v ++ "\n")
149 | (k,v) <- reverse (bindingPairs s) ]
150 return (TestMOk [((),s)]))
152 ------------------------------------------------------------------------------
154 - This function lets you test a list of rules
155 - about a specific function.
158 testRules :: String -> [TestM ()] -> IO ()
159 testRules name actions =
160 do putStr (rjustify 25 name ++ " : ")
161 f <- tr 1 actions [] 0
165 rjustify n s = replicate (max 0 (n - length s)) ' ' ++ s
168 putStr (rjustify (45 - n) (" (" ++ show c ++ ")\n")) ;
170 tr n [] xs c = do { putStr ("\n") ; return xs }
171 tr n (action:actions) others c =
173 r <- runTestM action (initTestMState ref)
177 tr (n+1) actions others (c+m) }
178 TestMFail f -> do { putStr "#" ;
179 tr (n+1) actions ((n,f):others) c}
180 _ -> do { putStr "?" ; tr (n+1) actions others c}
189 ++ " failed with the binding(s)\n")
190 sequence_ [putStr (" ** " ++ k ++ " = " ++ v ++ "\n")
191 | (k,v) <- reverse (bindingPairs f) ]
194 var :: (Example a) => String -> TestM a
199 vars :: (Example a,Show a) => String -> [a] -> TestM a
201 do pairUp name [(a,show a) | a <- as ]
203 ------------------------------------------------------------------------------
205 class Example a where
206 -- A list of examples of values at this type.
207 getVars :: TestM [(a,String)]
209 -- A version of equality, where _|_ == _|_ ==> True, not _|_
211 (<==>) :: a -> a -> TestM ()
213 do r <- runIOTestM (magicTest a b)
216 PerhapsSame -> oneTest (a `equ` b)
217 Different -> testFail
219 isFinite :: a -> TestM ()
221 do r <- runIOTestM (magicTest a bottom)
223 -- If this is _|_, then this check
224 -- is over, because this guard is not met.
225 -- but we return success, because the
226 -- overall problem was ok.
227 -- returning "return ()" whould
228 -- continue the test.
229 -- (A bit like F => ? ==> T)
233 -- protected, use only for defintions of things.
234 equ :: a -> a -> TestM ()
237 isFiniteSpine :: a -> TestM ()
238 isFiniteSpine _ = return ()
240 data BotCmp = Same | PerhapsSame | Different
242 ------------------------------------------------------------------------------
243 -- All the compile specific parts are captured inside
244 -- the function magicTest.
248 -- Old, Classic Hugs version
249 primitive catchError :: a -> Maybe a
251 magicTest :: a -> a -> IO BotCmp
253 if unsafePtrEq a b then return Same
254 else case (catchError a,catchError b) of
255 (Nothing,Nothing) -> return Same
256 (Just a,Just b) -> return PerhapsSame
257 _ -> return Different
262 magicTest :: a -> a -> IO BotCmp
264 if unsafePtrEq a b then return Same
265 else do a' <- tryAll a
268 (Left _,Left _) -> return Same
269 (Right _,Right _) -> return PerhapsSame
270 _ -> return Different
273 ------------------------------------------------------------------------------
275 bottom = error "bottom"
276 bottomExample = [(bottom,"_|_")]
278 cmp a b = if (a == b) then testComplete else testFail
280 render :: (Show a) => [a] -> [(a,String)]
281 render as = [ (a,show a) | a <- as ]
283 instance Example Char where
284 getVars = return (render ['a','z'] ++ bottomExample)
287 instance Example Float where
288 getVars = return (render [0.0,1.0,999.0] ++ bottomExample)
291 instance Example Double where
292 getVars = return (render [0.0,1.0,999.0] ++ bottomExample)
295 instance Example Integer where
296 getVars = return (render [-1,1,1] ++ bottomExample)
299 instance Example () where
300 getVars = return (render [()] ++ bottomExample)
303 instance Example Int where
304 getVars = return (render [0,1,2,minBound,maxBound] ++ bottomExample)
307 instance Example Bool where
308 getVars = return (render [True,False] ++ bottomExample)
311 instance Example a => Example [a] where
316 concat [ [ ([e],"[" ++ t ++ "]"),
317 (e:bottom,t ++ ":_|_") ]
320 "[" ++ t1 ++ "," ++ t2 ++ "]")
321 | (e1,t1) <- e1, (e2,t2) <- e2 ]
323 "[" ++ t1 ++ "," ++ t2 ++ "," ++ t1 ++ "]")
324 | (e1,t1) <- e1, (e2,t2) <- e2 ]
328 equ [] [] = testComplete
329 equ (a:as) (b:bs) = (a <==> b) &&& (as <==> bs)
332 isFiniteSpine [] = return ()
333 isFiniteSpine (_:xs) = isFinite xs
335 instance Example a => Example (Maybe a) where
339 [ (Just e,"Just " ++ t)
341 ++ [ (Nothing,"Nothing")]
344 equ Nothing Nothing = testComplete
345 equ (Just a) (Just b) = a <==> b
348 isFiniteSpine Nothing = return ()
349 isFiniteSpine (Just _) = return ()
351 ------------------------------------------------------------------------------
353 {- We pick something isomorphic to ints because of the
354 - shape of the domain.
362 class PolyExample a where
365 namePoly :: a -> String
367 equPoly :: a -> a -> TestM ()
368 equPoly a b = (unPoly a) <==> (unPoly b)
370 getPolyVars :: TestM [(a,String)]
373 return ([mkPair (mkPoly 0) 0,
374 mkPair (mkPoly n) n] ++ bottomExample)
376 mkPair a n = (a,namePoly a ++ "_" ++ show n)
378 ------------------------------------------------------------------------------
380 newtype ALPHA = ALPHA { unALPHA :: Int }
382 instance PolyExample ALPHA where
387 instance Example ALPHA where { equ = equPoly ; getVars = getPolyVars }
389 newtype BETA = BETA { unBETA :: Int }
391 instance PolyExample BETA where
396 instance Example BETA where { equ = equPoly ; getVars = getPolyVars }
398 newtype GAMMA = GAMMA { unGAMMA :: Int }
400 instance PolyExample GAMMA where
405 instance Example GAMMA where { equ = equPoly ; getVars = getPolyVars }
407 newtype EqALPHA = EqALPHA { unEqALPHA :: Int }
410 instance PolyExample EqALPHA where
415 instance Example EqALPHA where { equ = equPoly ; getVars = getPolyVars }
417 newtype OrdALPHA = OrdALPHA { unOrdALPHA :: Int }
420 instance PolyExample OrdALPHA where
425 instance Example OrdALPHA where { equ = equPoly ; getVars = getPolyVars }
427 ------------------------------------------------------------------------------
430 testAssoc :: (Example a) => (a -> a -> a) -> TestM ()
435 ((x `fn` (y `fn` z)) <==> ((x `fn` y) `fn` z))
437 ------------------------------------------------------------------------------
440 Example specifications. They all have type IO ().
442 test_concat = testRules "concat" [
443 do (xss :: [[ALPHA]]) <- var "xss"
444 concat xss <==> foldr (++) [] xss
447 test_head = testRules "head" [
448 let def_head (x:_) = x
449 def_head [] = error ""
450 in do (xs :: [ALPHA]) <- var "xs"
451 head xs <==> def_head xs
454 test_sort = testRules "sort" [
455 do (xs :: [OrdALPHA]) <- var "xs"
456 sort xs <==> sortBy compare xs,
457 do (xs :: [OrdALPHA]) <- var "xs"
458 head (sort xs) <==> minimum xs,
459 do (xs :: [OrdALPHA]) <- var "xs"
460 last (sort xs) <==> maximum xs,
461 do (xs :: [OrdALPHA]) <- var "xs"
462 (ys :: [OrdALPHA]) <- var "ys"
464 ||| (null ys <==> True)
465 ||| (head (sort (xs ++ ys)) <==> min (minimum xs) (minimum ys)),
466 do (xs :: [OrdALPHA]) <- var "xs"
467 (ys :: [OrdALPHA]) <- var "ys"
469 ||| (null ys <==> True)
470 ||| (last (sort (xs ++ ys)) <==> max (maximum xs) (maximum ys))
473 test_map = testRules "map" [
474 let def_map f [] = []
475 def_map f (x:xs) = f x : def_map f xs
479 map f xs <==> def_map f xs
481 test (id :: ALPHA -> ALPHA)
483 &&& test ((\ a -> a + 1) :: Int -> Int)
485 &&& test ((\ a -> bottom) :: Int -> Int)
487 do (xs :: [ALPHA]) <- var "xs"
491 test_int_plus = testRules "(+)::Int->Int->Int" [
492 testAssoc ((+) :: Int -> Int -> Int)