@@ -5,6 +5,10 @@
5module Main (main) where
5module Main (main) where
7import Control.Concurrent (threadDelay)
7import Control.Concurrent (threadDelay)
8+import Control.Exception (Exception, try)
9+import Control.Monad.Catch (throwM, MonadThrow)
10+import Control.Monad.Error.Class (throwError, MonadError)
11+import Control.Monad.Trans.Except (runExceptT, ExceptT)
8import Data.Foldable (forM_)
12import Data.Foldable (forM_)
9import Data.List (sort)
13import Data.List (sort)
10import Test.Hspec
14import Test.Hspec
@@ -32,6 +36,7 @@ main :: IO ()
32main = hspec $ do
36main = hspec $ do
33 describe "Runners" $ do
37 describe "Runners" $ do
34 -- XXX move these to property tests
38 -- XXX move these to property tests
39+ -- XXX use an IORef to store and check the side effects
35 it "simple serially" $
40 it "simple serially" $
36 (runStream . serially) (return (0 :: Int)) `shouldReturn` ()
41 (runStream . serially) (return (0 :: Int)) `shouldReturn` ()
37 it "simple serially with IO" $
42 it "simple serially with IO" $
@@ -283,6 +288,102 @@ main = hspec $ do
284 -- TBD combine all binds and all compose in one example
289 -- TBD combine all binds and all compose in one example
285 describe "Miscellaneous combined examples" mixedOps
290 describe "Miscellaneous combined examples" mixedOps
291+ describe "Simple MonadError and MonadThrow" simpleMonadError
294+ describe "Composed MonadError serially" $ composeWithMonadError serially
295+ describe "Composed MonadError coserially" $ composeWithMonadError coserially
296+ describe "Composed MonadError coparallely" $ composeWithMonadError coparallely
297+ describe "Composed MonadError parallely" $ composeWithMonadError parallely
300+ describe "Composed MonadThrow serially" $ composeWithMonadThrow serially
301+ describe "Composed MonadThrow coserially" $ composeWithMonadThrow coserially
302+ describe "Composed MonadThrow coparallely" $ composeWithMonadThrow coparallely
303+ describe "Composed MonadThrow parallely" $ composeWithMonadThrow parallely
305+-- XXX need to test that we have promptly cleaned up everything after the error
306+-- XXX We can also check the output that we are expected to get before the
309+data ExampleException = ExampleException String deriving (Eq, Show)
311+instance Exception ExampleException
313+simpleMonadError :: Spec
314+simpleMonadError = do
316+ it "simple runExceptT" $ do
317+ (runExceptT $ runStream $ return ())
318+ `shouldReturn` (Right () :: Either String ())
319+ it "simple runExceptT with error" $ do
320+ (runExceptT $ runStream $ throwError "E") `shouldReturn` Left "E"
322+ it "simple try" $ do
323+ (try $ runStream $ return ())
324+ `shouldReturn` (Right () :: Either ExampleException ())
325+ it "simple try with throw error" $ do
326+ (try $ runStream $ throwM $ ExampleException "E")
327+ `shouldReturn` (Left (ExampleException "E") :: Either ExampleException ())
329+composeWithMonadThrow
331+ , Semigroup (t IO Int)
334+ => (t IO Int -> SerialT IO Int) -> Spec
335+composeWithMonadThrow t = do
336+ it "Compose throwM, nil" $
337+ (try $ tl (throwM (ExampleException "E") <> A.nil))
338+ `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
339+ it "Compose nil, throwM" $
340+ (try $ tl (A.nil <> throwM (ExampleException "E")))
341+ `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
342+ oneLevelNestedSum "serially" serially
343+ oneLevelNestedSum "coserially" coserially
344+ oneLevelNestedSum "coparallely" coparallely
345+ oneLevelNestedSum "parallely" parallely
346+ -- XXX add two level nesting
348+ oneLevelNestedProduct "serially" serially
349+ oneLevelNestedProduct "coserially" coserially
350+ oneLevelNestedProduct "coparallely" coparallely
351+ oneLevelNestedProduct "parallely" parallely
355+ oneLevelNestedSum desc t1 =
356+ it ("One level nested sum " ++ desc) $ do
357+ let nested = (A.fromFoldable [1..10] <> throwM (ExampleException "E")
358+ <> A.fromFoldable [1..10])
359+ (try $ tl (A.nil <> t1 nested <> A.fromFoldable [1..10]))
360+ `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
362+ oneLevelNestedProduct desc t1 =
363+ it ("One level nested product" ++ desc) $ do
364+ let s1 = t $ foldMapWith (<>) return [1..4]
365+ s2 = t1 $ foldMapWith (<>) return [5..8]
370+ then throwM (ExampleException "E")
373+ `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
375+_composeWithMonadError
377+ , Semigroup (t (ExceptT String IO) Int)
378+ , MonadError String (t (ExceptT String IO))
380+ => (t (ExceptT String IO) Int -> SerialT (ExceptT String IO) Int) -> Spec
381+_composeWithMonadError t = do
382+ let tl = A.toList . t
383+ it "Compose throwError, nil" $
384+ (runExceptT $ tl (throwError "E" <> A.nil)) `shouldReturn` Left "E"
385+ it "Compose nil, error" $
386+ (runExceptT $ tl (A.nil <> throwError "E")) `shouldReturn` Left "E"
287nestTwoSerial :: Expectation
388nestTwoSerial :: Expectation
288nestTwoSerial =
389nestTwoSerial =