Skip to content

Make GenState a newtype and Add Instance for (Testable prop) => Testable (Gen prop) #46

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/Test/QuickCheck.purs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import Prelude
import Control.Monad.Eff (Eff())
import Control.Monad.Eff.Console (CONSOLE(), log)
import Control.Monad.Eff.Exception (EXCEPTION(), throwException, error)
import Control.Monad.State.Trans (StateT())
import Data.Identity (Identity())
import Control.Monad.Eff.Random (RANDOM())
import Data.List (List(..), replicateM)
import Test.QuickCheck.Arbitrary
Expand Down Expand Up @@ -65,7 +67,7 @@ quickCheck' n prop = do
-- | The first argument is the _random seed_ to be passed to the random generator.
-- | The second argument is the number of tests to run.
quickCheckPure :: forall prop. (Testable prop) => Seed -> Int -> prop -> List Result
quickCheckPure s n prop = evalGen (replicateM n (test prop)) { newSeed: s, size: 10 }
quickCheckPure s n prop = evalGen (replicateM n (test prop)) (genState s 10)

-- | The `Testable` class represents _testable properties_.
-- |
Expand All @@ -79,6 +81,9 @@ class Testable prop where
instance testableResult :: Testable Result where
test = return

instance testableGen :: (Testable t) => Testable (StateT GenState Identity t) where
test = flip bind test

instance testableBoolean :: Testable Boolean where
test true = return Success
test false = return $ Failed "Test returned false"
Expand Down
33 changes: 26 additions & 7 deletions src/Test/QuickCheck/Gen.purs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module Test.QuickCheck.Gen
( Gen()
, GenState()
, Size()
, genState
, repeatable
, stateful
, variant
Expand Down Expand Up @@ -51,7 +52,25 @@ import qualified Math as M
type Size = Int

-- | The state of the random generator monad
type GenState = { newSeed :: Seed, size :: Size }
newtype GenState = GenState (Tuple Seed Size)

genState :: Seed -> Size -> GenState
genState sd sz = GenState (Tuple sd sz)

getSeed :: GenState -> Seed
getSeed (GenState (Tuple sd sz)) = sd

setSeed :: Seed -> GenState -> GenState
setSeed sd' (GenState (Tuple sd sz)) = genState sd' sz

updateSeed :: (Seed -> Seed) -> GenState -> GenState
updateSeed f st = setSeed (f (getSeed st)) st

getSize :: GenState -> Size
getSize (GenState (Tuple sd sz)) = sz

setSize :: Size -> GenState -> GenState
setSize sz' (GenState (Tuple sd sz)) = genState sd sz'

-- | The random generator monad
-- |
Expand All @@ -68,15 +87,15 @@ stateful f = state $ \s -> runGen (f s) s

-- | Modify a random generator by setting a new random seed.
variant :: forall a. Seed -> Gen a -> Gen a
variant n g = state $ \s -> runGen g s { newSeed = n }
variant n g = state $ runGen g <<< setSeed n

-- | Create a random generator which depends on the size parameter.
sized :: forall a. (Size -> Gen a) -> Gen a
sized f = stateful (\s -> f s.size)
sized f = stateful (f <<< getSize)

-- | Modify a random generator by setting a new size parameter.
resize :: forall a. Size -> Gen a -> Gen a
resize sz g = state $ \s -> runGen g s { size = sz }
resize sz g = state $ runGen g <<< setSize sz

-- | Create a random generator which samples a range of `Number`s i
-- | with uniform probability.
Expand Down Expand Up @@ -161,7 +180,7 @@ evalGen = evalState

-- | Sample a random generator
sample :: forall a. Seed -> Size -> Gen a -> Array a
sample seed sz g = evalGen (vectorOf sz g) { newSeed: seed, size: sz }
sample sd sz g = evalGen (vectorOf sz g) (genState sd sz)

-- | Sample a random generator, using a randomly generated seed
randomSample' :: forall r a. Size -> Gen a -> Eff (random :: RANDOM | r) (Array a)
Expand All @@ -176,7 +195,7 @@ randomSample = randomSample' 10
-- | A random generator which simply outputs the current seed
lcgStep :: Gen Int
lcgStep = state f where
f s = Tuple (runSeed s.newSeed) (s { newSeed = lcgNext s.newSeed })
f = Tuple <$> (runSeed <<< getSeed) <*> updateSeed lcgNext

-- | A random generator which approximates a uniform random variable on `[0, 1]`
uniform :: Gen Number
Expand All @@ -187,7 +206,7 @@ foreign import float32ToInt32 :: Number -> Int
-- | Perturb a random generator by modifying the current seed
perturbGen :: forall a. Number -> Gen a -> Gen a
perturbGen n gen = do
modify \s -> s { newSeed = perturb s.newSeed }
modify (updateSeed perturb)
gen
where
perturb oldSeed = mkSeed (runSeed (lcgNext (mkSeed (float32ToInt32 n))) + runSeed oldSeed)
3 changes: 3 additions & 0 deletions test/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ module Test.Main where

import Prelude
import Control.Bind
import Control.Monad.Eff (Eff())
import Data.Array (head)
import Data.Maybe.Unsafe (fromJust)
import Data.Foldable
import Test.QuickCheck.Gen
import Test.QuickCheck.Arbitrary
import Control.Monad.Eff.Console
import Control.Monad.Eff.Random (RANDOM())

main :: forall eff. Eff (random :: RANDOM, console :: CONSOLE | eff) Unit
main = do
log "Try with some little Gens first"
print =<< go 10
Expand Down