{-# LANGUAGE NamedFieldPuns #-} -- | Define several variant models of /counters/ which are useful to -- test or use examples for various behaviours of the runtime. module Spec.DynamicLogic.Counters where import Control.Monad.Reader import Data.IORef import Test.QuickCheck import Test.QuickCheck.StateModel -- A very simple model with a single action that always succeed in -- predictable way. This model is useful for testing the runtime. newtype SimpleCounter = SimpleCounter {count :: Int} deriving (Eq, Show, Generic) deriving instance Eq (Action SimpleCounter a) deriving instance Show (Action SimpleCounter a) instance HasVariables (Action SimpleCounter a) where getAllVariables _ = mempty instance StateModel SimpleCounter where data Action SimpleCounter a where IncSimple :: Action SimpleCounter Int arbitraryAction _ _ = pure $ Some IncSimple initialState = SimpleCounter 0 nextState SimpleCounter{count} IncSimple _ = SimpleCounter (count + 1) instance RunModel SimpleCounter (ReaderT (IORef Int) IO) where perform _ IncSimple _ = do ref <- ask lift $ atomicModifyIORef' ref (\count -> (succ count, count)) -- A very simple model with a single action whose postcondition fails in a -- predictable way. This model is useful for testing the runtime. newtype FailingCounter = FailingCounter {failingCount :: Int} deriving (Eq, Show, Generic) deriving instance Eq (Action FailingCounter a) deriving instance Show (Action FailingCounter a) instance HasVariables (Action FailingCounter a) where getAllVariables _ = mempty instance StateModel FailingCounter where data Action FailingCounter a where Inc' :: Action FailingCounter Int arbitraryAction _ _ = pure $ Some Inc' initialState = FailingCounter 0 nextState FailingCounter{failingCount} Inc' _ = FailingCounter (failingCount + 1) instance RunModel FailingCounter (ReaderT (IORef Int) IO) where perform _ Inc' _ = do ref <- ask lift $ atomicModifyIORef' ref (\count -> (succ count, count)) postcondition (_, FailingCounter{failingCount}) _ _ _ = pure $ failingCount < 4 -- A generic but simple counter model data Counter = Counter Int deriving (Show, Generic) deriving instance Show (Action Counter a) deriving instance Eq (Action Counter a) instance HasVariables (Action Counter a) where getAllVariables _ = mempty instance StateModel Counter where data Action Counter a where Inc :: Action Counter () Reset :: Action Counter Int initialState = Counter 0 arbitraryAction _ _ = frequency [(5, pure $ Some Inc), (1, pure $ Some Reset)] nextState (Counter n) Inc _ = Counter (n + 1) nextState _ Reset _ = Counter 0 instance RunModel Counter (ReaderT (IORef Int) IO) where perform _ Inc _ = do ref <- ask lift $ modifyIORef ref succ perform _ Reset _ = do ref <- ask lift $ do n <- readIORef ref writeIORef ref 0 pure n postcondition (Counter n, _) Reset _ res = pure $ n == res postcondition _ _ _ _ = pure True