packages feed

quickcheck-effectful-1.0.0: test/Main.hs

{- HLINT ignore "Redundant reverse" -}
module Main where

import Effectful
import Effectful.Error.Static (runError, throwError)
import Effectful.Prim (Prim, runPrim)
import Effectful.Prim.IORef (modifyIORef', newIORef, readIORef)
import Effectful.QuickCheck
import Effectful.Reader.Static (Reader, ask, runReader)
import Prelude

prop_reverse :: [Int] -> Bool
prop_reverse xs = (reverse . reverse) xs == xs

prop_readerScaling :: (Reader Int :> es) => Int -> Eff es Bool
prop_readerScaling x = do
    factor <- ask
    pure (x * factor == factor * x)

prop_errorRecovers :: Int -> Eff es (Property es)
prop_errorRecovers n = do
    result <- runError @String (if odd n then throwError ("odd" :: String) else pure n)
    pure $ either (const $ property (odd n)) (=== n) result

prop_iorefAccumulates :: (Prim :> es) => [Int] -> Eff es (Property es)
prop_iorefAccumulates xs = do
    ref <- newIORef (0 :: Int)
    mapM_ (\x -> modifyIORef' ref (+ x)) xs
    acc <- readIORef ref
    pure $ counterexample ("running total was " <> show acc) (acc === sum xs)

main :: IO ()
main = runEff . runPrim . runReader (7 :: Int) $ do
    -- Pure props
    quickCheck prop_reverse
    quickCheck $ withMaxSuccess 10_000 prop_reverse

    -- Effectful props
    quickCheck prop_readerScaling
    quickCheck prop_errorRecovers
    quickCheck prop_iorefAccumulates

    -- Composition
    quickCheck $ withMaxSuccess 500 prop_readerScaling
    quickCheck $ prop_readerScaling .&&. prop_iorefAccumulates
    quickCheck $ prop_reverse .||. prop_readerScaling
    quickCheck . forAll (chooseInt (0, 100)) $ \x ->
        counterexample ("x = " <> show x) (prop_readerScaling x)
    quickCheck $ \x -> x > 0 ==> prop_readerScaling x
    quickCheck $ classify True "always" . label "reader" $ prop_readerScaling
    quickCheck . conjoin $ [prop_readerScaling, prop_readerScaling]
    quickCheck $ once (prop_reverse [])