packages feed

mockcat-0.6.0.0: test/Property/LazyEvalProp.hs

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
module Property.LazyEvalProp
  ( prop_lazy_unforced_not_counted
  , prop_lazy_forced_counted
  ) where

import Test.QuickCheck
import Test.QuickCheck.Monadic (monadicIO, run, assert)
import Test.MockCat hiding (any)

-- | Unary action so we can register a concrete expected argument & return value.
--   We test that constructing (but not forcing) the application does not count.
class Monad m => LazyUnaryAction m where
  lazyUnaryAction :: Int -> m Int

makeMock [t|LazyUnaryAction|]

-- | Property: if we declare an expectation but never force (execute) the action,
--   the recorded application count remains 0.
prop_lazy_unforced_not_counted :: Property
prop_lazy_unforced_not_counted = monadicIO $ do
  run $ runMockT $ do
    _lazyUnaryAction (param (10 :: Int) |> (42 :: Int)) `applyTimesIs` 0
    -- Do NOT force the call; only build a thunk.
    let _thunk :: MockT IO Int
        _thunk = lazyUnaryAction 10
    pure ()
  assert True

-- | Property: once we actually execute (force) the action, it is counted exactly once.
prop_lazy_forced_counted :: Property
prop_lazy_forced_counted = monadicIO $ do
  run $ runMockT $ do
    _lazyUnaryAction (param (10 :: Int) |> (7 :: Int)) `applyTimesIs` 1
    v <- lazyUnaryAction 10   -- forcing the monadic action executes the mock
    v `seq` pure ()           -- ensure result is evaluated (WHNF for Int)
  assert True