on master help sign in
Split tests into modules
commit0d4109c40ec88704df42d1a94e04b82271999225 authorAndrzej Rybczak <andrzej@rybczak.net> dateSat 26 Jun 2021, 23:39 (5 years ago) parentb42e0dc
6 files changed+233−183
effectful.cabal+5 tests/AsyncTests.hs+32 tests/ErrorTests.hs+36 tests/Main.hs+6−183 tests/StateTests.hs+141 tests/Utils.hs+13
effectful.cabal+5
@@ -104,6 +104,11 @@ test-suite test
104104 type: exitcode-stdio-1.0
105105 main-is: Main.hs
106106
107+ other-modules: AsyncTests
108+ ErrorTests
109+ StateTests
110+ Utils
111+
107112benchmark bench
108113 import: language
109114
tests/AsyncTests.hsnew file+32
@@ -0,0 +1,32 @@
1+module AsyncTests (asyncTests) where
2+
3+import Test.Tasty
4+import Test.Tasty.HUnit
5+
6+import Effectful.Async
7+import Effectful.State.Dynamic
8+import Effectful.Monad
9+import Utils
10+
11+asyncTests :: TestTree
12+asyncTests = testGroup "Async"
13+ [ testCase "thread local state" test_threadLocalState
14+ ]
15+
16+test_threadLocalState :: Assertion
17+test_threadLocalState = runIOE . evalState x $ do
18+ runAsyncE . replicateConcurrently_ 2 $ do
19+ r <- goDownward 0
20+ assertEqual_ "x = n" x r
21+ where
22+ x :: Int
23+ x = 1000000
24+
25+ goDownward :: State Int :> es => Int -> Eff es Int
26+ goDownward acc = do
27+ end <- state @Int $ \case
28+ 0 -> (True, 0)
29+ n -> (False, n - 1)
30+ if end
31+ then pure acc
32+ else goDownward $ acc + 1
tests/ErrorTests.hsnew file+36
@@ -0,0 +1,36 @@
1+module ErrorTests (errorTests) where
2+
3+import Control.Monad.IO.Class
4+import Test.Tasty
5+import Test.Tasty.HUnit
6+import GHC.Stack (getCallStack)
7+
8+import Effectful.Error
9+import Effectful.Interpreter
10+import Effectful.Monad
11+
12+errorTests :: TestTree
13+errorTests = testGroup "Error"
14+ [ testCase "error from interpret" test_errorFromInterpret
15+ ]
16+
17+test_errorFromInterpret :: Assertion
18+test_errorFromInterpret = runIOE $ do
19+ result <- runError @String . runNestedErr $ do
20+ runError @String nestedErr
21+ liftIO $ case result of
22+ Left (cs, _) -> assertBool "stack trace points to the correct action" $
23+ "nestedErr" == fst (last $ getCallStack cs)
24+ Right _ -> assertFailure "error caught by the wrong (inner) handler"
25+
26+----------------------------------------
27+-- Helpers
28+
29+data NestedErr :: Effect where
30+ NestedErr :: NestedErr m ()
31+
32+nestedErr :: (HasCallStack, NestedErr :> es) => Eff es ()
33+nestedErr = send NestedErr
34+
35+runNestedErr :: Error String :> es => Eff (NestedErr : es) a -> Eff es a
36+runNestedErr = interpret $ \NestedErr -> throwError "nested error"
tests/Main.hs+6−183
@@ -1,191 +1,14 @@
11module Main (main) where
22
3−import Control.Monad.IO.Class
43import Test.Tasty
5−import Test.Tasty.HUnit
6−import qualified Control.Monad.Catch as E
7−import qualified Control.Exception.Lifted as LE
8−import GHC.Stack (getCallStack)
9−import qualified UnliftIO.Exception as UE
104
11−import Effectful.Async
12−import Effectful.Error
13−import Effectful.Interpreter
14−import Effectful.State.Dynamic
15−import Effectful.Monad
5+import AsyncTests
6+import ErrorTests
7+import StateTests
168
179main :: IO ()
1810main = defaultMain $ testGroup "effectful"
19− [ testGroup "state"
20− [ testCase "runState & execState" test_runState
21− , testCase "evalState" test_evalState
22− , testCase "stateM" test_stateM
23− , testCase "deep stack" test_deepStack
24− , testCase "exceptions" test_exceptions
25− , testCase "concurrency" test_concurrentState
26− , testCase "local effects" test_localEffects
27− , testCase "error from interpret" test_errorFromInterpret
28− ]
11+ [ asyncTests
12+ , errorTests
13+ , stateTests
2914 ]
30−
31−test_runState :: Assertion
32−test_runState = runIOE $ do
33− (end, len) <- runState (0::Int) . execState collatzStart $ collatz
34− assertEqual_ "correct end" 1 end
35− assertEqual_ "correct len" collatzLength len
36−
37−test_evalState :: Assertion
38−test_evalState = runIOE $ do
39− len <- evalState (0::Int) . evalState collatzStart $ collatz *> get @Int
40− assertEqual_ "correct len" collatzLength len
41−
42−test_stateM :: Assertion
43−test_stateM = runIOE $ do
44− (a, b) <- runState "hi" . stateM $ \s -> pure (s, s ++ "!!!")
45− assertEqual_ "correct a" "hi" a
46− assertEqual_ "correct b" "hi!!!" b
47−
48−test_deepStack :: Assertion
49−test_deepStack = runIOE $ do
50− n <- evalState () . execState (0::Int) $ do
51− evalState () . evalState () $ do
52− evalState () $ do
53− evalState () . evalState () . evalState () $ do
54− modify @Int (+1)
55− modify @Int (+2)
56− modify @Int (+4)
57− modify @Int (+8)
58− assertEqual_ "n" 15 n
59−
60−test_exceptions :: Assertion
61−test_exceptions = runIOE $ do
62− testTry "exceptions" E.try
63− testCatch "exceptions" E.catch
64− testTry "lifted-base" LE.try
65− testCatch "lifted-base" LE.catch
66− testTry "unliftio" UE.try
67− testCatch "unliftio" UE.catch
68− where
69− testTry
70− :: String
71− -> (forall a es. IOE :> es => Eff es a -> Eff es (Either Ex a))
72− -> Eff '[IOE] ()
73− testTry lib tryImpl = do
74− e <- tryImpl $ runState (0::Int) action
75− assertEqual_ (lib ++ " - exception caught") e (Left Ex)
76− s <- execState (0::Int) $ tryImpl action
77− assertEqual_ (lib ++ " - state partially updated") s 1
78−
79− testCatch
80− :: String
81− -> (forall a es. IOE :> es => Eff es a -> (Ex -> Eff es a) -> Eff es a)
82− -> Eff '[IOE] ()
83− testCatch lib catchImpl = do
84− s <- execState (0::Int) $ do
85− _ <- (evalState () action) `catchImpl` \Ex -> modify @Int (+4)
86− modify @Int (+8)
87− assertEqual_ (lib ++ " - state correctly updated") s 13
88−
89− action :: State Int :> es => Eff es ()
90− action = do
91− modify @Int (+1)
92− _ <- E.throwM Ex
93− modify @Int (+2)
94−
95−test_concurrentState :: Assertion
96−test_concurrentState = runIOE . evalState x $ do
97− runAsyncE . replicateConcurrently_ 2 $ do
98− r <- goDownward 0
99− assertEqual_ "x = n" x r
100− where
101− x :: Int
102− x = 1000000
103−
104− goDownward :: State Int :> es => Int -> Eff es Int
105− goDownward acc = do
106− end <- state @Int $ \case
107− 0 -> (True, 0)
108− n -> (False, n - 1)
109− if end
110− then pure acc
111− else goDownward $ acc + 1
112−
113−test_localEffects :: Assertion
114−test_localEffects = runIOE $ do
115− x <- runHasInt 0 $ do
116− putInt 1
117− execState () $ do
118− putInt 2
119− execState () $ do
120− putInt 4
121− getInt
122− assertEqual_ "correct x" 4 x
123−
124−test_errorFromInterpret :: Assertion
125−test_errorFromInterpret = runIOE $ do
126− result <- runError @String . runNestedErr $ do
127− runError @String nestedErr
128− liftIO $ case result of
129− Left (cs, _) -> assertBool "stack trace points to the correct action" $
130− "nestedErr" == fst (last $ getCallStack cs)
131− Right _ -> assertFailure "error caught by the wrong (inner) handler"
132−
133−----------------------------------------
134−-- Helpers
135−
136−data NestedErr :: Effect where
137− NestedErr :: NestedErr m ()
138−
139−nestedErr :: (HasCallStack, NestedErr :> es) => Eff es ()
140−nestedErr = send NestedErr
141−
142−runNestedErr :: Error String :> es => Eff (NestedErr : es) a -> Eff es a
143−runNestedErr = interpret $ \NestedErr -> throwError "nested error"
144−
145−----------------------------------------
146−
147−data HasInt :: Effect where
148− GetInt :: HasInt m Int
149− PutInt :: Int -> HasInt m ()
150−
151−getInt :: HasInt :> es => Eff es Int
152−getInt = send GetInt
153−
154−putInt :: HasInt :> es => Int -> Eff es ()
155−putInt = send . PutInt
156−
157−runHasInt :: Int -> Eff (HasInt : es) a -> Eff es a
158−runHasInt n =
159− -- reinterpret with redundant local effects
160− reinterpret (evalState () . evalState n . evalState True) $ \case
161− GetInt -> get
162− PutInt i -> put i
163−
164−----------------------------------------
165−
166−data Ex = Ex deriving (Eq, Show)
167−instance E.Exception Ex
168−
169−assertEqual_ :: (Eq a, Show a, IOE :> es) => String -> a -> a -> Eff es ()
170−assertEqual_ msg expected given = liftIO $ assertEqual msg expected given
171−
172−----------------------------------------
173−
174−collatzStart :: Integer
175−collatzStart = 9780657630
176−
177−collatzLength :: Int
178−collatzLength = 1132
179−
180−-- | Tests multiple 'State'S, 'put', 'get' and 'modify'.
181−collatz :: (State Integer :> es, State Int :> es) => Eff es ()
182−collatz = get @Integer >>= \case
183− 1 -> pure ()
184− n -> if even n
185− then do put $ n `div` 2
186− modify @Int (+1)
187− collatz
188− else do put $ 3*n + 1
189− modify @Int (+1)
190− collatz
191−{-# NOINLINE collatz #-}
tests/StateTests.hsnew file+141
@@ -0,0 +1,141 @@
1+module StateTests (stateTests) where
2+
3+import Test.Tasty
4+import Test.Tasty.HUnit
5+import qualified Control.Monad.Catch as E
6+import qualified Control.Exception.Lifted as LE
7+import qualified UnliftIO.Exception as UE
8+
9+import Effectful.Interpreter
10+import Effectful.State.Dynamic
11+import Effectful.Monad
12+import Utils
13+
14+stateTests :: TestTree
15+stateTests = testGroup "State"
16+ [ testCase "runState & execState" test_runState
17+ , testCase "evalState" test_evalState
18+ , testCase "stateM" test_stateM
19+ , testCase "deep stack" test_deepStack
20+ , testCase "exceptions" test_exceptions
21+ , testCase "local effects" test_localEffects
22+ ]
23+
24+test_runState :: Assertion
25+test_runState = runIOE $ do
26+ (end, len) <- runState (0::Int) . execState collatzStart $ collatz
27+ assertEqual_ "correct end" 1 end
28+ assertEqual_ "correct len" collatzLength len
29+
30+test_evalState :: Assertion
31+test_evalState = runIOE $ do
32+ len <- evalState (0::Int) . evalState collatzStart $ collatz *> get @Int
33+ assertEqual_ "correct len" collatzLength len
34+
35+test_stateM :: Assertion
36+test_stateM = runIOE $ do
37+ (a, b) <- runState "hi" . stateM $ \s -> pure (s, s ++ "!!!")
38+ assertEqual_ "correct a" "hi" a
39+ assertEqual_ "correct b" "hi!!!" b
40+
41+test_deepStack :: Assertion
42+test_deepStack = runIOE $ do
43+ n <- evalState () . execState (0::Int) $ do
44+ evalState () . evalState () $ do
45+ evalState () $ do
46+ evalState () . evalState () . evalState () $ do
47+ modify @Int (+1)
48+ modify @Int (+2)
49+ modify @Int (+4)
50+ modify @Int (+8)
51+ assertEqual_ "n" 15 n
52+
53+test_exceptions :: Assertion
54+test_exceptions = runIOE $ do
55+ testTry "exceptions" E.try
56+ testCatch "exceptions" E.catch
57+ testTry "lifted-base" LE.try
58+ testCatch "lifted-base" LE.catch
59+ testTry "unliftio" UE.try
60+ testCatch "unliftio" UE.catch
61+ where
62+ testTry
63+ :: String
64+ -> (forall a es. IOE :> es => Eff es a -> Eff es (Either Ex a))
65+ -> Eff '[IOE] ()
66+ testTry lib tryImpl = do
67+ e <- tryImpl $ runState (0::Int) action
68+ assertEqual_ (lib ++ " - exception caught") e (Left Ex)
69+ s <- execState (0::Int) $ tryImpl action
70+ assertEqual_ (lib ++ " - state partially updated") s 1
71+
72+ testCatch
73+ :: String
74+ -> (forall a es. IOE :> es => Eff es a -> (Ex -> Eff es a) -> Eff es a)
75+ -> Eff '[IOE] ()
76+ testCatch lib catchImpl = do
77+ s <- execState (0::Int) $ do
78+ _ <- (evalState () action) `catchImpl` \Ex -> modify @Int (+4)
79+ modify @Int (+8)
80+ assertEqual_ (lib ++ " - state correctly updated") s 13
81+
82+ action :: State Int :> es => Eff es ()
83+ action = do
84+ modify @Int (+1)
85+ _ <- E.throwM Ex
86+ modify @Int (+2)
87+
88+test_localEffects :: Assertion
89+test_localEffects = runIOE $ do
90+ x <- runHasInt 0 $ do
91+ putInt 1
92+ execState () $ do
93+ putInt 2
94+ execState () $ do
95+ putInt expected
96+ getInt
97+ assertEqual_ "correct x" expected x
98+ where
99+ expected :: Int
100+ expected = 4
101+----------------------------------------
102+-- Helpers
103+
104+data HasInt :: Effect where
105+ GetInt :: HasInt m Int
106+ PutInt :: Int -> HasInt m ()
107+
108+getInt :: HasInt :> es => Eff es Int
109+getInt = send GetInt
110+
111+putInt :: HasInt :> es => Int -> Eff es ()
112+putInt = send . PutInt
113+
114+runHasInt :: Int -> Eff (HasInt : es) a -> Eff es a
115+runHasInt n =
116+ -- reinterpret with redundant local effects
117+ reinterpret (evalState () . evalState n . evalState True) $ \case
118+ GetInt -> get
119+ PutInt i -> put i
120+
121+----------------------------------------
122+-- Helpers
123+
124+collatzStart :: Integer
125+collatzStart = 9780657630
126+
127+collatzLength :: Int
128+collatzLength = 1132
129+
130+-- | Tests multiple 'State'S, 'put', 'get' and 'modify'.
131+collatz :: (State Integer :> es, State Int :> es) => Eff es ()
132+collatz = get @Integer >>= \case
133+ 1 -> pure ()
134+ n -> if even n
135+ then do put $ n `div` 2
136+ modify @Int (+1)
137+ collatz
138+ else do put $ 3*n + 1
139+ modify @Int (+1)
140+ collatz
141+{-# NOINLINE collatz #-}
tests/Utils.hsnew file+13
@@ -0,0 +1,13 @@
1+module Utils where
2+
3+import Control.Exception
4+import Control.Monad.IO.Class
5+import Test.Tasty.HUnit
6+
7+import Effectful.Monad
8+
9+assertEqual_ :: (Eq a, Show a, IOE :> es) => String -> a -> a -> Eff es ()
10+assertEqual_ msg expected given = liftIO $ assertEqual msg expected given
11+
12+data Ex = Ex deriving (Eq, Show)
13+instance Exception Ex