freer-simple/tests/Tests.hs
2017-02-17 16:19:24 +01:00

147 lines
5.4 KiB
Haskell

{-# LANGUAGE CPP #-}
module Main where
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative
#endif
import Control.Monad.Freer
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck
import Tests.Coroutine
import Tests.Exception
import Tests.Fresh
import Tests.NonDet
import Tests.Reader
import Tests.State
import Tests.StateRW
import qualified Data.List
--------------------------------------------------------------------------------
-- Pure Tests --
--------------------------------------------------------------------------------
addInEff :: Int -> Int -> Int
addInEff x y = run ((+) <$> pure x <*> pure y)
pureTests :: TestTree
pureTests = testGroup "Pure Eff tests"
[ testProperty "Pure run just works: (+)"
(\x y -> addInEff x y == x + y)
]
--------------------------------------------------------------------------------
-- Coroutine Tests --
--------------------------------------------------------------------------------
-- | Counts number of consecutive pairs of odd elements at beginning of a list.
countOddDuoPrefix :: [Int] -> Int
countOddDuoPrefix list = count list 0
where
count (i1:i2:is) n = if even i1 && even i2 then n else count is (n+1)
count _ n = n
coroutineTests :: TestTree
coroutineTests = testGroup "Coroutine Eff tests"
[ testProperty "Counting consecutive pairs of odds"
(\list -> runTestCoroutine list == countOddDuoPrefix list)
]
--------------------------------------------------------------------------------
-- Exception Tests --
--------------------------------------------------------------------------------
exceptionTests :: TestTree
exceptionTests = testGroup "Exception Eff tests"
[ testProperty "Exc takes precedence" (\x y -> testExceptionTakesPriority x y == Left y)
, testCase "uncaught: runState (runError t)" $
ter1 @?= (Left "exc", 2)
, testCase "uncaught: runError (runState t)" $
ter2 @?= Left "exc"
, testCase "caught: runState (runError t)" $
ter3 @?= (Right "exc", 2)
, testCase "caught: runError (runState t)" $
ter4 @?= Right ("exc", 2)
, testCase "success: runReader (runErrBig t)" (ex2rr @?= Right 5)
, testCase "uncaught: runReader (runErrBig t)" $
ex2rr1 @?= Left (TooBig 7)
, testCase "uncaught: runErrBig (runReader t)" $
ex2rr2 @?= Left (TooBig 7)
]
--------------------------------------------------------------------------------
-- Fresh Effect Tests --
--------------------------------------------------------------------------------
freshTests :: TestTree
freshTests = testGroup "Fresh tests"
[ testCase "Start at 0, refresh twice, yields 1" (testFresh 10 @?= 9)
, testProperty "Freshening n times yields (n-1)" (\n -> n > 0 ==> testFresh n == (n-1))
]
--------------------------------------------------------------------------------
-- Nondeterministic Effect Tests --
--------------------------------------------------------------------------------
-- https://wiki.haskell.org/Prime_numbers
primesTo :: Int -> [Int]
primesTo m = sieve [2..m] {- (\\) is set-difference for unordered lists -}
where
sieve (x:xs) = x : sieve (xs Data.List.\\ [x,x+x..m])
sieve [] = []
nonDetTests :: TestTree
nonDetTests = testGroup "NonDet tests"
[ testProperty "Primes in 2..n generated by ifte"
(\n' -> let n = abs n' in testIfte [2..n] == primesTo n)
]
--------------------------------------------------------------------------------
-- Reader Effect Tests --
--------------------------------------------------------------------------------
readerTests :: TestTree
readerTests = testGroup "Reader tests"
[ testProperty "Reader passes along environment: n + x"
(\n x -> testReader n x == n + x)
, testProperty "Multiple readers work"
(\i n -> testMultiReader i n == ((i + 2) + fromIntegral (n + 1)))
, testProperty "Local injects into env"
(\env inc -> testLocal env inc == 2*(env+1) + inc)
]
--------------------------------------------------------------------------------
-- State[RW] Effect Tests --
--------------------------------------------------------------------------------
stateTests :: TestTree
stateTests = testGroup "State tests"
[ testProperty "get after put n yields (n,n)" (\n -> testPutGet n 0 == (n,n))
, testProperty "Final put determines stored state" $
\p1 p2 start -> testPutGetPutGetPlus p1 p2 start == (p1+p2, p2)
, testProperty "If only getting, start state determines outcome" $
\start -> testGetStart start == (start,start)
, testProperty "testPutGet: State == StateRW" $
\n -> testPutGet n 0 == testPutGetRW n 0
, testProperty "testPutGetPutGetPlus: State == StateRW" $
\p1 p2 start -> testPutGetPutGetPlus p1 p2 start == testPutGetPutGetPlusRW p1 p2 start
, testProperty "testGetStart: State == StateRW" $
\n -> testGetStart n == testGetStartRW n
, testProperty "testEvalState: evalState discards final state" $
\n -> testEvalState n == n
, testProperty "testExecState: execState returns final state" $
\n -> testExecState n == n
]
--------------------------------------------------------------------------------
-- Runner --
--------------------------------------------------------------------------------
main :: IO ()
main = defaultMain $ testGroup "Tests"
[ pureTests
, coroutineTests
, exceptionTests
, freshTests
, nonDetTests
, readerTests
, stateTests
]