packages feed

quickcheck-state-machine-0.9.0: test/Mock.hs

{-# LANGUAGE DeriveAnyClass       #-}
{-# LANGUAGE DeriveGeneric        #-}
{-# LANGUAGE DerivingStrategies   #-}
{-# LANGUAGE ExplicitNamespaces   #-}
{-# LANGUAGE FlexibleContexts     #-}
{-# LANGUAGE FlexibleInstances    #-}
{-# LANGUAGE MonoLocalBinds       #-}
{-# LANGUAGE PolyKinds            #-}
{-# LANGUAGE RecordWildCards      #-}
{-# LANGUAGE ScopedTypeVariables  #-}
{-# LANGUAGE StandaloneDeriving   #-}
{-# LANGUAGE UndecidableInstances #-}

module Mock
    ( prop_sequential_mock
    , prop_parallel_mock
    , prop_nparallel_mock
    )
    where

import           Control.Concurrent
import           GHC.Generics
                   (Generic, Generic1)
import           Prelude
import           Test.QuickCheck
import           Test.QuickCheck.Monadic
                   (monadicIO)
import           Test.StateMachine
import           Test.StateMachine.DotDrawing
import           Test.StateMachine.TreeDiff
import qualified Test.StateMachine.Types.Rank2 as Rank2

------------------------------------------------------------------------

data Command r
  = Create
  deriving stock (Eq, Generic1)
  deriving anyclass (Rank2.Functor, Rank2.Foldable, Rank2.Traversable, CommandNames)

deriving stock instance Show (Command Symbolic)
deriving stock instance Read (Command Symbolic)
deriving stock instance Show (Command Concrete)

data Response r
  = Created (Reference Int r)
  | NotCreated
  deriving stock (Eq, Generic1)
  deriving anyclass Rank2.Foldable

deriving stock instance Show (Response Symbolic)
deriving stock instance Read (Response Symbolic)
deriving stock instance Show (Response Concrete)

data Model r = Model {
      refs :: [Reference Int r]
    , c    :: Int
    }
  deriving stock (Generic, Show)

instance ToExpr (Model Symbolic)
instance ToExpr (Model Concrete)

initModel :: Model r
initModel = Model [] 0

transition :: Model r -> Command r -> Response r -> Model r
transition m@Model{..} cmd resp = case (cmd, resp, c) of
  (Create, Created ref, 0) -> Model (ref : refs) 1
  (Create, _, _)           -> m

precondition :: Model Symbolic -> Command Symbolic -> Logic
precondition _ cmd = case cmd of
    Create        -> Top

postcondition :: Model Concrete -> Command Concrete -> Response Concrete -> Logic
postcondition _ _ _ = Top

semantics :: MVar Int -> Command Concrete -> IO (Response Concrete)
semantics counter cmd = case cmd of
  Create        -> do
    c <- modifyMVar counter (\x -> return (x + 1, x))
    case c of
        0 -> return $ Created $ reference c
        _ -> return NotCreated

mock :: Model Symbolic -> Command Symbolic -> GenSym (Response Symbolic)
mock Model{..} cmd = case (cmd, c) of
  (Create, 0) -> Created   <$> genSym
  (Create, _) -> return NotCreated

generator :: Model Symbolic -> Maybe (Gen (Command Symbolic))
generator _            = Just $ frequency
    [(1, return Create)]

shrinker :: Model Symbolic -> Command Symbolic -> [Command Symbolic]
shrinker _ _ = []

sm :: IO (StateMachine Model Command IO Response)
sm = do
  counter <- newMVar 0
  pure $ StateMachine initModel transition precondition postcondition
        Nothing generator shrinker (semantics counter) mock noCleanup

smUnused :: StateMachine Model Command IO Response
smUnused = StateMachine initModel transition precondition postcondition
        Nothing generator shrinker e mock noCleanup
  where
    e = error "SUT must not be used"

prop_sequential_mock :: Property
prop_sequential_mock = forAllCommands smUnused Nothing $ \cmds -> monadicIO $ do
  (hist, _model, res) <- runCommandsWithSetup sm cmds
  prettyCommands smUnused hist (res === Ok)

prop_parallel_mock :: Property
prop_parallel_mock = forAllParallelCommands smUnused Nothing $ \cmds -> monadicIO $ do
    ret <- runParallelCommandsWithSetup sm cmds
    prettyParallelCommandsWithOpts cmds opts ret
      where opts = Just $ GraphOptions "mock-test-output.png" Png

prop_nparallel_mock :: Property
prop_nparallel_mock = forAllNParallelCommands smUnused 3 $ \cmds -> monadicIO $ do
    ret <- runNParallelCommandsWithSetup sm cmds
    prettyNParallelCommandsWithOpts cmds opts ret
      where opts = Just $ GraphOptions "mock-np-test-output.png" Png