11module Test.QuickCheck where
22
3- import Prelude
4- import Data.Array
5- import Data.Maybe
6- import Data.Either
7- import Data.Tuple
83import Debug.Trace
94import Control.Monad.Eff
10- import Control.Monad.Eff.Exception
115import Control.Monad.Eff.Random
6+ import Control.Monad.Eff.Exception
7+
8+ import Test.QuickCheck.LCG
9+
10+ class Arbitrary t where
11+ arbitrary :: Gen t
12+
13+ class CoArbitrary t where
14+ coarbitrary :: forall r . t -> Gen r -> Gen r
1215
1316data Result = Success | Failed String
1417
15- type QC = forall eff . Eff (random :: Random , trace :: Trace , err :: Exception String | eff ) { }
18+ instance showResult :: Show Result where
19+ show Success = " Success"
20+ show (Failed msg) = " Failed: " ++ msg
1621
17- class (Show t ) <= Arb t where
18- arb :: forall eff . Eff (random :: Random | eff ) t
22+ (<?>) :: Boolean -> String -> Result
23+ (<?>) true _ = Success
24+ (<?>) false msg = Failed msg
1925
20- instance arbNumber :: Arb Number where
21- arb = random
26+ instance arbNumber :: Arbitrary Number where
27+ arbitrary = uniform
2228
23- instance arbBoolean :: Arb Boolean where
24- arb = do
25- n <- random
26- return ((n * 2 ) < 1 )
29+ instance coarbNumber :: CoArbitrary Number where
30+ coarbitrary = perturbGen
2731
28- instance arbArray :: (Arb a ) => Arb [a ] where
29- arb = do
30- b <- arb
31- if b then return [] else do
32- a <- arb
33- as <- arb
34- return (a : as)
32+ instance arbBoolean :: Arbitrary Boolean where
33+ arbitrary = do
34+ n <- uniform
35+ return $ (n * 2 ) < 1
36+
37+ instance coarbBoolean :: CoArbitrary Boolean where
38+ coarbitrary true (Gen f) = Gen $ \l -> f (l + 1 )
39+ coarbitrary false (Gen f) = Gen $ \l -> f (l + 2 )
3540
36- instance arbMaybe :: (Arb a ) => Arb (Maybe a ) where
37- arb = do
38- b <- arb
39- if b then pure Nothing else Just <$> arb
41+ instance arbFunction :: (CoArbitrary a , Arbitrary b ) => Arbitrary (a -> b ) where
42+ arbitrary = repeatable (\a -> coarbitrary a arbitrary)
4043
41- instance arbEither :: (Arb a , Arb b ) => Arb (Either a b ) where
42- arb = do
43- b <- arb
44- if b then Left <$> arb else Right <$> arb
44+ repeatable :: forall a b . (a -> Gen b ) -> Gen (a -> b )
45+ repeatable f = Gen $ \l -> { value: \a -> (runGen (f a) l).value, newSeed: l }
4546
46- instance arbTuple :: (Arb a , Arb b ) => Arb (Tuple a b ) where
47- arb = Tuple <$> arb <*> arb
47+ instance coarbFunction :: (Arbitrary a , CoArbitrary b ) => CoArbitrary (a -> b ) where
48+ coarbitrary f gen = do
49+ xs <- arbitrary
50+ coarbitrary (map f xs) gen
51+ where
52+ map _ [] = []
53+ map f (x : xs) = f x : map f xs
54+
55+ instance arbArray :: (Arbitrary a ) => Arbitrary [a ] where
56+ arbitrary = do
57+ b <- arbitrary
58+ if b then return [] else do
59+ a <- arbitrary
60+ as <- arbitrary
61+ return (a : as)
62+
63+ instance coarbArray :: (CoArbitrary a ) => CoArbitrary [a ] where
64+ coarbitrary [] = id
65+ coarbitrary (x : xs) = coarbitrary xs <<< coarbitrary x
4866
4967class Testable prop where
50- test :: forall eff . prop -> Eff ( random :: Random | eff ) Result
68+ test :: prop -> Gen Result
5169
5270instance testableResult :: Testable Result where
5371 test = return
@@ -56,24 +74,41 @@ instance testableBoolean :: Testable Boolean where
5674 test true = return Success
5775 test false = return $ Failed " Test returned false"
5876
59- instance testableFunction :: (Show t , Arb t , Testable prop ) => Testable (t -> prop ) where
77+ instance testableFunction :: (Arbitrary t , Testable prop ) => Testable (t -> prop ) where
6078 test f = do
61- t <- arb
62- result <- test (f t)
63- case result of
64- Success -> return Success
65- Failed msg -> return $ Failed $ " Failed on input " ++ show t ++ " : \n " ++ msg
66-
67- quickCheck' :: forall prop . (Testable prop ) => Number -> prop -> QC
68- quickCheck' n prop = run 1 prop n
79+ t <- arbitrary
80+ test (f t)
81+
82+ quickCheckPure :: forall prop . (Testable prop ) => Number -> Number -> prop -> [Result ]
83+ quickCheckPure seed n prop = evalGen (go n) seed
6984 where
70- run 2 _ 1 = trace $ " Test passed"
71- run n _ t | n > t = trace $ show t ++ " tests passed"
72- run n prop t = do
85+ go n | n <= 0 = return []
86+ go n = do
7387 result <- test prop
74- case result of
75- Success -> run (n + 1 ) prop t
76- Failed msg -> throwException $ " Test " ++ show n ++ " failed: \n " ++ msg
88+ rest <- go (n - 1 )
89+ return $ result : rest
90+
91+ type QC a = forall eff . Eff (trace :: Trace , random :: Random , err :: Exception String | eff ) a
92+
93+ quickCheck' :: forall prop . (Testable prop ) => Number -> prop -> QC { }
94+ quickCheck' n prop = do
95+ seed <- randomSeed
96+ let results = quickCheckPure seed n prop
97+ let successes = countSuccesses results
98+ trace $ show successes ++ " /" ++ show n ++ " test(s) passed."
99+ throwOnFirstFailure 1 results
100+
101+ where
102+
103+ throwOnFirstFailure :: Number -> [Result ] -> QC { }
104+ throwOnFirstFailure _ [] = return {}
105+ throwOnFirstFailure n (Failed msg : _) = throwException $ " Test " ++ show n ++ " failed: \n " ++ msg
106+ throwOnFirstFailure n (_ : rest) = throwOnFirstFailure (n + 1 ) rest
107+
108+ countSuccesses :: [Result ] -> Number
109+ countSuccesses [] = 0
110+ countSuccesses (Success : rest) = 1 + countSuccesses rest
111+ countSuccesses (_ : rest) = countSuccesses rest
77112
78- quickCheck :: forall prop . (Testable prop ) => prop -> QC
113+ quickCheck :: forall prop . (Testable prop ) => prop -> QC { }
79114quickCheck prop = quickCheck' 100 prop
0 commit comments