Skip to content

Commit 6d62052

Browse files
committed
Merge branch 'lcg'
Conflicts: .gitignore
2 parents 25b6247 + f5ad099 commit 6d62052

File tree

8 files changed

+281
-225
lines changed

8 files changed

+281
-225
lines changed

.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/.*
22
!/.gitignore
3-
/bower_components/
4-
/node_modules/
53
/output/
4+
/node_modules/
5+
/bower_components/
66
/tmp/
7+
/node_modules/

README.md

Lines changed: 110 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,111 @@
1-
purescript-quickcheck
2-
=====================
1+
# Module Documentation
32

4-
A very basic implementation of QuickCheck in PureScript
3+
## Module Test.QuickCheck
4+
5+
### Types
6+
7+
type QC a = forall eff. Eff (err :: Exception Prim.String, random :: Random, trace :: Trace | eff) a
8+
9+
data Result where
10+
Success :: Result
11+
Failed :: Prim.String -> Result
12+
13+
14+
### Type Classes
15+
16+
class Arbitrary t where
17+
arbitrary :: Gen t
18+
19+
class CoArbitrary t where
20+
coarbitrary :: forall r. t -> Gen r -> Gen r
21+
22+
class Testable prop where
23+
test :: prop -> Gen Result
24+
25+
26+
### Type Class Instances
27+
28+
instance arbArray :: (Arbitrary a) => Arbitrary [a]
29+
30+
instance arbBoolean :: Arbitrary Prim.Boolean
31+
32+
instance arbFunction :: (CoArbitrary a, Arbitrary b) => Arbitrary (a -> b)
33+
34+
instance arbNumber :: Arbitrary Prim.Number
35+
36+
instance coarbArray :: (CoArbitrary a) => CoArbitrary [a]
37+
38+
instance coarbBoolean :: CoArbitrary Prim.Boolean
39+
40+
instance coarbFunction :: (Arbitrary a, CoArbitrary b) => CoArbitrary (a -> b)
41+
42+
instance coarbNumber :: CoArbitrary Prim.Number
43+
44+
instance showResult :: Show Result
45+
46+
instance testableBoolean :: Testable Prim.Boolean
47+
48+
instance testableFunction :: (Arbitrary t, Testable prop) => Testable (t -> prop)
49+
50+
instance testableResult :: Testable Result
51+
52+
53+
### Values
54+
55+
(<?>) :: Prim.Boolean -> Prim.String -> Result
56+
57+
quickCheck :: forall prop. (Testable prop) => prop -> QC { }
58+
59+
quickCheck' :: forall prop. (Testable prop) => Prim.Number -> prop -> QC { }
60+
61+
quickCheckPure :: forall prop. (Testable prop) => Prim.Number -> Prim.Number -> prop -> [Result]
62+
63+
repeatable :: forall a b. (a -> Gen b) -> Gen (a -> b)
64+
65+
66+
## Module Test.QuickCheck.LCG
67+
68+
### Types
69+
70+
data Gen a where
71+
Gen :: LCG -> { newSeed :: LCG, value :: a } -> Gen a
72+
73+
type LCG = Prim.Number
74+
75+
76+
### Type Class Instances
77+
78+
instance applicativeGen :: Applicative Gen
79+
80+
instance applyGen :: Apply Gen
81+
82+
instance bindGen :: Bind Gen
83+
84+
instance functorGen :: Functor Gen
85+
86+
instance monadGen :: Monad Gen
87+
88+
89+
### Values
90+
91+
evalGen :: forall a. Gen a -> LCG -> a
92+
93+
float32ToInt32 :: Prim.Number -> Prim.Number
94+
95+
lcgC :: Prim.Number
96+
97+
lcgM :: Prim.Number
98+
99+
lcgN :: Prim.Number
100+
101+
lcgNext :: Prim.Number -> Prim.Number
102+
103+
lcgStep :: Gen Prim.Number
104+
105+
perturbGen :: forall a. Prim.Number -> Gen a -> Gen a
106+
107+
randomSeed :: forall eff. Eff (random :: Random | eff) Prim.Number
108+
109+
runGen :: forall a. Gen a -> LCG -> { newSeed :: LCG, value :: a }
110+
111+
uniform :: Gen Prim.Number

bower.json

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,6 @@
1414
],
1515
"dependencies": {
1616
"purescript-random": "*",
17-
"purescript-arrays": "*",
18-
"purescript-maybe": "*",
19-
"purescript-either": "*",
20-
"purescript-tuples": "*",
21-
"purescript-exceptions": "*",
22-
"purescript-strings": "*"
17+
"purescript-exceptions": "*"
2318
}
2419
}

examples/Prelude.purs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,23 @@ import Test.QuickCheck
88
testConst :: Number -> Number -> Number -> Boolean
99
testConst a b c = const a b == const a c
1010

11+
mkMessage :: (Number -> Number) -> String
12+
mkMessage f = "Test failed for function ("
13+
++ show (f 0) ++ ", "
14+
++ show (f 1) ++ ", "
15+
++ show (f 2) ++ ")"
16+
1117
main = do
1218
Debug.Trace.trace "testConst:"
1319
quickCheck testConst
1420

21+
Debug.Trace.trace "id is a left unit for <<<"
22+
quickCheck $ \f a -> ((id <<< f) (a :: Number) == (f a) :: Number) <?> mkMessage f
23+
1524
Debug.Trace.trace "Precedence of && and ||:"
1625
quickCheck $ \a b c -> ((a :: Boolean && b) || c) == ((a || c) && (b || c))
1726

1827
Debug.Trace.trace "Test Eq instance for Ref:"
1928
quickCheck $ \a -> (Ref a :: Ref Number) == Ref a
2029
quickCheck $ \a -> not $ (Ref a :: Ref Number /= Ref a)
30+

src/Test/QuickCheck.purs

Lines changed: 84 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,71 @@
11
module Test.QuickCheck where
22

3-
import Prelude
4-
import Data.Array
5-
import Data.Maybe
6-
import Data.Either
7-
import Data.Tuple
83
import Debug.Trace
94
import Control.Monad.Eff
10-
import Control.Monad.Eff.Exception
115
import 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

1316
data 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

4967
class Testable prop where
50-
test :: forall eff. prop -> Eff (random :: Random | eff) Result
68+
test :: prop -> Gen Result
5169

5270
instance 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 {}
79114
quickCheck prop = quickCheck' 100 prop

src/Test/QuickCheck/Classes.purs

Lines changed: 0 additions & 66 deletions
This file was deleted.

0 commit comments

Comments
 (0)