random-1.3.0: test/Spec/Stateful.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Spec.Stateful where
import Control.Concurrent.STM
import Control.Monad
import Control.Monad.ST
import Data.Proxy
import Data.Typeable
import System.Random (uniformShortByteString)
import System.Random.Stateful hiding (uniformShortByteString)
import Test.SmallCheck.Series
import Test.Tasty
import Test.Tasty.SmallCheck as SC
instance Monad m => Serial m StdGen where
series = mkStdGen <$> series
instance (Monad m, Serial m g) => Serial m (AtomicGen g) where
series = AtomicGen <$> series
instance (Monad m, Serial m g) => Serial m (IOGen g) where
series = IOGen <$> series
instance (Monad m, Serial m g) => Serial m (STGen g) where
series = STGen <$> series
instance (Monad m, Serial m g) => Serial m (TGen g) where
series = TGen <$> series
instance (Monad m, Serial m g) => Serial m (StateGen g) where
series = StateGen <$> series
matchRandomGenSpec ::
forall f a sg m. (StatefulGen sg m, RandomGen f, Eq f, Show f, Eq a)
=> (forall g n. StatefulGen g n => g -> n a)
-> (forall g. RandomGen g => g -> (a, g))
-> (StdGen -> f)
-> (f -> StdGen)
-> (f -> (sg -> m a) -> IO (a, f))
-> Property IO
matchRandomGenSpec genM gen fromStdGen toStdGen runStatefulGen =
forAll $ \seed -> monadic $ do
let stdGen = mkStdGen seed
g = fromStdGen stdGen
(x1, g1) = gen stdGen
(x2, g2) = gen g
(x3, g3) <- runStatefulGen g genM
pure $ and [x1 == x2, x2 == x3, g1 == toStdGen g2, g1 == toStdGen g3, g2 == g3]
withMutableGenSpec ::
forall f m. (ThawedGen f m, Eq f, Show f)
=> (forall a. m a -> IO a)
-> f
-> Property IO
withMutableGenSpec toIO frozen =
forAll $ \n -> monadic $ toIO $ do
let action = uniformListM n
x@(_, _) :: ([Word], f) <- withMutableGen frozen action
y@(r, _) <- withMutableGen frozen action
r' <- withMutableGen_ frozen action
pure $ x == y && r == r'
overwriteMutableGenSpec ::
forall f m. (ThawedGen f m, Eq f, Show f)
=> (forall a. m a -> IO a)
-> f
-> Property IO
overwriteMutableGenSpec toIO frozen =
forAll $ \n -> monadic $ toIO $ do
let action = uniformListM (abs n + 1) -- Non-empty
((r1, r2), frozen') :: ((String, String), f) <- withMutableGen frozen $ \mutable -> do
r1 <- action mutable
overwriteGen mutable frozen
r2 <- action mutable
modifyGen mutable (const ((), frozen))
pure (r1, r2)
pure $ r1 == r2 && frozen == frozen'
indepMutableGenSpec ::
forall f m. (RandomGen f, ThawedGen f m, Eq f, Show f)
=> (forall a. m a -> IO a) -> [f] -> Property IO
indepMutableGenSpec toIO fgs =
monadic $ toIO $ do
(fgs ==) <$> (mapM freezeGen =<< mapM thawGen fgs)
immutableFrozenGenSpec ::
forall f m. (RandomGen f, ThawedGen f m, Eq f, Show f)
=> (forall a. m a -> IO a) -> f -> Property IO
immutableFrozenGenSpec toIO frozen =
forAll $ \n -> monadic $ toIO $ do
let action = do
mg <- thawGen frozen
(,) <$> uniformWord8 mg <*> freezeGen mg
x <- action
xs <- replicateM n action
pure $ all (x ==) xs
splitMutableGenSpec ::
forall f m. (SplitGen f, ThawedGen f m, Eq f, Show f)
=> (forall a. m a -> IO a)
-> f
-> Property IO
splitMutableGenSpec toIO frozen =
monadic $ toIO $ do
(sfg1, fg1) <- withMutableGen frozen splitGenM
(smg2, fg2) <- withMutableGen frozen splitMutableGenM
sfg3 <- freezeGen smg2
pure $ fg1 == fg2 && sfg1 == sfg3
thawedGenSpecFor ::
forall f m. (SplitGen f, ThawedGen f m, Eq f, Show f, Serial IO f, Typeable f)
=> (forall a. m a -> IO a)
-> Proxy f
-> TestTree
thawedGenSpecFor toIO px =
testGroup
(showsTypeRep (typeRep px) "")
[ testProperty "withMutableGen" $
forAll $ \(f :: f) -> withMutableGenSpec toIO f
, testProperty "overwriteGen" $
forAll $ \(f :: f) -> overwriteMutableGenSpec toIO f
, testProperty "independent mutable generators" $
forAll $ \(fs :: [f]) -> indepMutableGenSpec toIO fs
, testProperty "immutable frozen generators" $
forAll $ \(f :: f) -> immutableFrozenGenSpec toIO f
, testProperty "splitGen" $
forAll $ \(f :: f) -> splitMutableGenSpec toIO f
]
frozenGenSpecFor ::
forall f sg m. (RandomGen f, StatefulGen sg m, Eq f, Show f, Typeable f)
=> (StdGen -> f)
-> (f -> StdGen)
-> (forall a. f -> (sg -> m a) -> IO (a, f))
-> TestTree
frozenGenSpecFor fromStdGen toStdGen runStatefulGen =
testGroup (showsTypeRep (typeRep (Proxy :: Proxy f)) "")
[ testGroup "matchRandomGenSpec"
[ testProperty "uniformWord8/genWord8" $
matchRandomGenSpec uniformWord8 genWord8 fromStdGen toStdGen runStatefulGen
, testProperty "uniformWord16/genWord16" $
matchRandomGenSpec uniformWord16 genWord16 fromStdGen toStdGen runStatefulGen
, testProperty "uniformWord32/genWord32" $
matchRandomGenSpec uniformWord32 genWord32 fromStdGen toStdGen runStatefulGen
, testProperty "uniformWord64/genWord64" $
matchRandomGenSpec uniformWord64 genWord64 fromStdGen toStdGen runStatefulGen
, testProperty "uniformWord32R/genWord32R" $
forAll $ \w32 ->
matchRandomGenSpec (uniformWord32R w32) (genWord32R w32) fromStdGen toStdGen runStatefulGen
, testProperty "uniformWord64R/genWord64R" $
forAll $ \w64 ->
matchRandomGenSpec (uniformWord64R w64) (genWord64R w64) fromStdGen toStdGen runStatefulGen
, testProperty "uniformShortByteStringM/uniformShortByteString" $
forAll $ \(NonNegative n') ->
let n = n' `mod` 100000 -- Ensure it is not too big
in matchRandomGenSpec
(uniformShortByteStringM n)
(uniformShortByteString n)
fromStdGen
toStdGen
runStatefulGen
, testProperty "uniformByteStringM/uniformByteString" $
forAll $ \(NonNegative n') ->
let n = n' `mod` 100000 -- Ensure it is not too big
in matchRandomGenSpec
(uniformByteStringM n)
(uniformByteString n)
fromStdGen
toStdGen
runStatefulGen
, testProperty "uniformByteArrayM/genByteArray" $
forAll $ \(NonNegative n', isPinned1 :: Bool, isPinned2 :: Bool) ->
let n = n' `mod` 100000 -- Ensure it is not too big
in matchRandomGenSpec
(uniformByteArrayM isPinned1 n)
(uniformByteArray isPinned2 n)
fromStdGen
toStdGen
runStatefulGen
]
]
statefulGenSpec :: TestTree
statefulGenSpec =
testGroup
"StatefulGen"
[ testGroup "ThawedGen"
[ thawedGenSpecFor id (Proxy :: Proxy (IOGen StdGen))
, thawedGenSpecFor id (Proxy :: Proxy (AtomicGen StdGen))
, thawedGenSpecFor stToIO (Proxy :: Proxy (STGen StdGen))
, thawedGenSpecFor atomically (Proxy :: Proxy (TGen StdGen))
]
, testGroup "FrozenGen"
[ frozenGenSpecFor StateGen unStateGen runStateGenT
, frozenGenSpecFor IOGen unIOGen $ \g action -> do
mg <- newIOGenM (unIOGen g)
res <- action mg
g' <- freezeGen mg
pure (res, g')
, frozenGenSpecFor AtomicGen unAtomicGen $ \g action -> do
mg <- newAtomicGenM (unAtomicGen g)
res <- action mg
g' <- freezeGen mg
pure (res, g')
, frozenGenSpecFor STGen unSTGen $ \g action -> stToIO $ do
mg <- newSTGenM (unSTGen g)
res <- action mg
g' <- freezeGen mg
pure (res, g')
, frozenGenSpecFor TGen unTGen $ \g action -> atomically $ do
mg <- newTGenM (unTGen g)
res <- action mg
g' <- freezeGen mg
pure (res, g')
]
]