{-# LANGUAGE ScopedTypeVariables #-}

module Main where

import Prelude hiding (catch)

import Control.Monad.Exception
import Control.Monad.Trans.Error
import Control.Monad.IO.Class
import Data.IORef
import System.Exit (exitFailure, exitSuccess)
import Test.HUnit

main :: IO ()
main = do
    count <- runTestTT tests
    case failures count of
      0 -> exitSuccess
      _ -> exitFailure


tests :: Test
tests = TestList [mkTest (conl ++ " " ++ whatl) con what | (conl, con) <- cons, (whatl, what) <- whats]
  where
    whats :: [(String, ErrorT String IO ())]
    whats = [("return",     return ()),
             ("error",      error "error"),
             ("throwError", throwError "throwError")]

    cons :: [(String, ErrorT String IO () -> ErrorT String IO () -> ErrorT String IO ())]
    cons = [("finally",  \what sequel -> what `finally` sequel),
            ("bracket_", \what sequel -> bracket_ (return ()) sequel what)]

mkTest :: String
       -> (ErrorT String IO () -> ErrorT String IO () -> ErrorT String IO ())
       -> ErrorT String IO ()
       -> Test
mkTest label con what =
    label ~: tst
  where
    tst = do
        ref <- newIORef "sequel not called"
        let sequel = liftIO $ writeIORef ref expected
        _ <- runErrorT (con what sequel) `catch` \(e :: SomeException) -> return (Left (show e))
        actual <- readIORef ref
        return $ assertEqual "" expected actual

    expected :: String
    expected = "sequel called"
