packages feed

lazy-async-1.1.0.0: test/test.hs

module Main (main) where

import LazyAsync

import Test.Counter
import Test.Exceptions
import Test.General
import Test.Optics

import Data.Foldable (traverse_)
import Data.Function (($), (.))
import Data.Functor  (($>))
import Prelude       (Integer, (+))
import System.IO     (IO)

import Control.Exception (ArithException (DivideByZero))

import Control.Applicative       (liftA2, (<|>))
import Control.Monad             (replicateM_, return, (>>=))
import Control.Monad.Trans.Class (MonadTrans (lift))

import Hedgehog (Property, annotate, discover, (===))

main :: IO ()
main = main' $$(discover)

prop_noAutoStart :: Property
prop_noAutoStart = contExample do
    annotate "'LazyAsync' does not start automatically"
    tick <- expectTicks 0
    la <- lazyAsync tick
    pause
    lift (poll la) >>= focus _Incomplete

prop_memoize_noAutoStart :: Property
prop_memoize_noAutoStart = contExample do
    annotate "'memoize' does not start the action"
    tick <- expectTicks 0
    _ <- memoize tick
    pause

prop_start :: Property
prop_start = contExample do
    annotate "'start' prompts a 'LazyAsync' to run"
    tick <- expectTicks 1
    la <- lazyAsync tick
    start la
    pause

prop_startWait :: Property
prop_startWait = contExample do
    annotate "'startWait' prompts a 'LazyAsync' to run"
    tick <- expectTicks 1
    la <- lazyAsync tick
    lift (startWait la >>= (=== 1))

prop_start_idempotent :: Property
prop_start_idempotent = contExample do
    annotate "'start' is idempotent"
    tick <- expectTicks 1
    la <- lazyAsync tick
    replicateM_ 2 (start la)
    pause

prop_startWait_idempotent :: Property
prop_startWait_idempotent = contExample do
    annotate "'startWait' is idempotent"
    tick <- expectTicks 1
    la <- lazyAsync tick
    lift (replicateM_ 2 (startWait la >>= (=== 1)))

prop_memoize_idempotent :: Property
prop_memoize_idempotent = contExample do
    annotate "The action returned by 'memoize' is idempotent"
    tick <- expectTicks 1
    tick' <- memoize tick
    lift (replicateM_ 2 (tick' >>= (=== 1)))

prop_startWaitCatch :: Property
prop_startWaitCatch = contExample do
    annotate "'startWaitCatch' catches exceptions"
    la <- lazyAsync (throw' DivideByZero)
    lift (startWaitCatch la) >>= focus _Failure >>= exceptionIs DivideByZero

prop_startWaitCatch_idempotent :: Property
prop_startWaitCatch_idempotent = contExample do
    annotate "'startWaitCatch' is idempotent"
    tick <- expectTicks 1
    la <- lazyAsync tick
    replicateM_ 2 (lift (startWaitCatch la))

prop_apply_startWait_both :: Property
prop_apply_startWait_both = contExample do
    annotate "'startWait' on an applicative complex runs both actions"
    tick <- expectTicks 2

    outcome <- contIO do
        la1 <- lazyAsync tick
        la2 <- lazyAsync tick
        let complex = liftA2 (+) la1 la2
        lift (startWaitCatch complex)

    lift (focus _Success outcome >>= (=== 3))

prop_apply_once :: Property
prop_apply_once = contExample do
    annotate "actions included in multiple applicative complexes still can only run once"
    tick <- expectTicks 3

    contIO do
        la1 <- lazyAsync tick
        la2 <- lazyAsync tick
        la3 <- lazyAsync tick

        let complexes = [ liftA2 (,) la1 la2
                        , liftA2 (,) la3 la2
                        , liftA2 (,) la1 la3 ]

        traverse_ start complexes
        lift (traverse_ wait complexes)

prop_choose :: Property
prop_choose = contExample do
    annotate "(<|>) can tolerate the failure of either part"
    la1 <- lazyAsync (return (5 :: Integer))
    la2 <- lazyAsync (throw' DivideByZero)

    let complex1 = la1 <|> la2
        complex2 = la2 <|> la1

    lift do
        startWait complex1 >>= (=== 5)
        startWait complex2 >>= (=== 5)