packages feed

lsm-tree-1.0.0.0: test/Test/Util/QLS.hs

{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE TypeFamilies          #-}

-- | Utilities for the @quickcheck-lockstep@ package
--
-- TODO: it might be nice to upstream these utilities to @quickcheck-lockstep@
-- at some point.
module Test.Util.QLS (
    IOProperty (..)
  , runActionsBracket
  ) where

import           Prelude hiding (init)

import           Control.Monad (void)
import           Control.Monad.Class.MonadST
import           Control.Monad.Class.MonadThrow
import           Control.Monad.IOSim (IOSim, runSimTraceST, traceResult)
import           Control.Monad.Primitive (RealWorld)
import qualified Control.Monad.ST.Lazy as ST
import           Data.Typeable
import qualified Test.QuickCheck as QC
import           Test.QuickCheck (Property, Testable)
import           Test.QuickCheck.Monadic
import qualified Test.QuickCheck.StateModel as StateModel
import           Test.QuickCheck.StateModel hiding (runActions)
import           Test.QuickCheck.StateModel.Lockstep

class IOProperty m where
  ioProperty :: m Property -> Property

instance IOProperty IO where
  ioProperty = QC.ioProperty

instance IOProperty (IOSim RealWorld) where
  ioProperty x = QC.ioProperty $ do
      trac <- ST.stToIO $ runSimTraceST x
      case traceResult False trac of
        Left e  -> pure $ QC.counterexample (show e) False
        Right p -> pure p

runActionsBracket ::
     ( RunLockstep state m
     , e ~ Error (Lockstep state) m
     , forall a. IsPerformResult e a
     , Testable prop
     , MonadMask n
     , MonadST n
     , IOProperty n
     )
  => Proxy state
  -> n st -- ^ Initialisation
  -> (st -> n prop) -- ^ Cleanup
  -> (m Property -> st -> n Property) -- ^ Runner
  -> Actions (Lockstep state)
  -> Property
runActionsBracket _ init cleanup runner actions =
    monadicBracketIO init cleanup runner $
      void $ StateModel.runActions actions

ioPropertyBracket ::
     (Testable a, Testable b, MonadMask n, MonadST n, IOProperty n)
  => n st
  -> (st -> n b)
  -> (m a -> st -> n a)
  -> m a
  -> Property
ioPropertyBracket init cleanup runner prop =
    ioProperty $ mask $ \restore -> do
      st <- init
      a <- restore (runner prop st) `onException` cleanup st
      b <- cleanup st
      pure $ a QC..&&. b

monadicBracketIO :: forall st a b m n.
     (Monad m, Testable a, Testable b, MonadMask n, MonadST n, IOProperty n)
  => n st
  -> (st -> n b)
  -> (m Property -> st -> n Property)
  -> PropertyM m a
  -> Property
monadicBracketIO init cleanup runner =
    monadic (ioPropertyBracket init cleanup runner)