Skip to content

Commit c20e88b

Browse files
committed
Expose generate via Graphula.Arbitrary
Anything in tests that utilizes random generation can use the graphula seed and remain deterministic, instead of falling back to `IO`.
1 parent b026557 commit c20e88b

File tree

3 files changed

+48
-22
lines changed

3 files changed

+48
-22
lines changed

graphula-core/src/Graphula.hs

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ import Control.Monad.IO.Unlift
9090
import Control.Monad.Reader (MonadReader, ReaderT, ask, asks, runReaderT)
9191
import Control.Monad.Trans (MonadTrans, lift)
9292
import Data.Foldable (for_, traverse_)
93-
import Data.IORef (IORef, modifyIORef', newIORef, readIORef, writeIORef)
93+
import Data.IORef (IORef, modifyIORef', newIORef, readIORef)
9494
import Data.Kind (Constraint, Type)
9595
import Data.Proxy (Proxy(..))
9696
import Data.Semigroup.Generic (gmappend, gmempty)
@@ -115,15 +115,15 @@ import Database.Persist
115115
import Database.Persist.Sql (SqlBackend)
116116
import Generics.Eot (Eot, HasEot, fromEot, toEot)
117117
import GHC.Generics (Generic)
118+
import Graphula.Arbitrary (generate)
118119
import Graphula.Internal
119120
import System.Directory (createDirectoryIfMissing, getTemporaryDirectory)
120121
import System.IO (Handle, IOMode(..), hClose, openFile)
121122
import System.IO.Temp (openTempFile)
122-
import System.Random (randomIO, split)
123+
import System.Random (randomIO)
123124
import Test.HUnit.Lang
124125
(FailureReason(..), HUnitFailure(..), formatFailureReason)
125-
import Test.QuickCheck (Arbitrary(..), Gen)
126-
import Test.QuickCheck.Gen (unGen)
126+
import Test.QuickCheck (Arbitrary(..))
127127
import Test.QuickCheck.Random (QCGen, mkQCGen)
128128
import UnliftIO.Exception
129129
(Exception, SomeException, bracket, catch, mask, throwIO)
@@ -148,14 +148,6 @@ type family GraphulaContext (m :: Type -> Type) (ts :: [Type]) :: Constraint whe
148148
GraphulaContext m '[] = MonadGraphula m
149149
GraphulaContext m (t ': ts) = (GraphulaNode m t, GraphulaContext m ts)
150150

151-
class MonadGraphulaBackend m where
152-
type Logging m :: Type -> Constraint
153-
-- ^ A constraint provided to log details of the graph to some form of
154-
-- persistence. This is used by 'runGraphulaLogged' to store graph nodes as
155-
-- 'Show'n 'Text' values
156-
askGen :: m (IORef QCGen)
157-
logNode :: Logging m a => a -> m ()
158-
159151
class MonadGraphulaFrontend m where
160152
insert
161153
:: (PersistEntityBackend a ~ SqlBackend, PersistEntity a, Monad m)
@@ -561,13 +553,3 @@ newtype Only a = Only { fromOnly :: a }
561553

562554
only :: a -> Only a
563555
only = Only
564-
565-
generate :: (MonadIO m, MonadGraphulaBackend m) => Gen a -> m a
566-
generate gen = do
567-
genRef <- askGen
568-
g <- liftIO $ readIORef genRef
569-
let
570-
(g1, g2) = split g
571-
x = unGen gen g1 30
572-
liftIO $ writeIORef genRef g2
573-
pure x
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{-|
2+
Graphula tracks its own 'QCGen' for deterministic generation with 'Arbitrary'
3+
and 'Gen'. 'generate' can be used to produce arbitrary values utilizing
4+
graphula's generation.
5+
-}
6+
module Graphula.Arbitrary
7+
( generate
8+
)
9+
where
10+
11+
import Prelude
12+
13+
import Control.Monad.IO.Unlift (MonadIO, liftIO)
14+
import Data.IORef (readIORef, writeIORef)
15+
import Graphula.Internal (MonadGraphulaBackend, askGen)
16+
import System.Random (split)
17+
import Test.QuickCheck (Gen)
18+
import Test.QuickCheck.Gen (unGen)
19+
20+
-- | Run a generator
21+
--
22+
-- This is akin to 'Test.QuickCheck.generate', but utilizing graphula's
23+
-- generation. The size passed to the generator is always 30; if you want
24+
-- another size then you should explicitly use 'Test.QuickCheck.resize'.
25+
--
26+
generate :: (MonadIO m, MonadGraphulaBackend m) => Gen a -> m a
27+
generate gen = do
28+
genRef <- askGen
29+
g <- liftIO $ readIORef genRef
30+
let
31+
(g1, g2) = split g
32+
x = unGen gen g1 30
33+
liftIO $ writeIORef genRef g2
34+
pure x

graphula-core/src/Graphula/Internal.hs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,21 @@
1111

1212
module Graphula.Internal where
1313

14+
import Data.IORef (IORef)
1415
import Data.Kind (Constraint, Type)
1516
import Database.Persist (Key)
1617
import Generics.Eot (Proxy(..), Void)
1718
import GHC.TypeLits (ErrorMessage(..), TypeError)
1819
import Test.QuickCheck (Arbitrary(..), Gen)
20+
import Test.QuickCheck.Random (QCGen)
21+
22+
class MonadGraphulaBackend m where
23+
type Logging m :: Type -> Constraint
24+
-- ^ A constraint provided to log details of the graph to some form of
25+
-- persistence. This is used by 'runGraphulaLogged' to store graph nodes as
26+
-- 'Show'n 'Text' values
27+
askGen :: m (IORef QCGen)
28+
logNode :: Logging m a => a -> m ()
1929

2030
data Match t
2131
= NoMatch t

0 commit comments

Comments
 (0)