grapesy-1.0.0: interop/Interop/Util/Exceptions.hs
module Interop.Util.Exceptions (
TestSkipped(..)
, TestUnimplemented(..)
-- * Test failures
, TestFailure(..)
, assertFailure
, assertUnrecognized
, assertBool
, assertEqual
, assertThrows
, assertTerminatesWithinSeconds
-- * Re-exports
, HasCallStack
, throwIO
) where
import Control.Exception
import Data.List (intercalate)
import GHC.Stack
import System.Timeout
data TestSkipped = TestSkipped String
deriving stock (Show)
deriving anyclass (Exception)
data TestUnimplemented = TestUnimplemented
deriving stock (Show)
instance Exception TestUnimplemented where
displayException TestUnimplemented = "test unimplemented"
{-------------------------------------------------------------------------------
Test failures
The output of the test messages is relatively minimal: we show only a
callstack, so that we know /which/ test failed, and any runtime values.
-------------------------------------------------------------------------------}
data TestFailure =
TestFailure {
failureCallStack :: CallStack
, failureMessage :: String
}
deriving stock (Show)
instance Exception TestFailure where
displayException (TestFailure cs msg) = intercalate "\n" [
msg
, prettyCallStack cs
]
assertFailure :: HasCallStack => String -> IO x
assertFailure = throwIO . TestFailure callStack
assertUnrecognized :: (HasCallStack, Show a) => a -> IO x
assertUnrecognized x = assertFailure $ "Unrecognized: " ++ show x
assertBool :: HasCallStack => Bool -> IO ()
assertBool True = return ()
assertBool False = assertFailure "Predicate failed"
assertEqual :: (HasCallStack, Eq a, Show a) => a -> a -> IO ()
assertEqual expected actual
| expected == actual
= return ()
| otherwise
= assertFailure $ "Expected: " ++ show expected ++ ", actual: " ++ show actual
assertThrows :: (HasCallStack, Exception e) => (e -> IO ()) -> IO a -> IO ()
assertThrows p io = do
ma <- try io
case ma of
Right _ -> assertFailure "Expected exception"
Left err -> p err
assertTerminatesWithinSeconds :: Int -> IO () -> IO ()
assertTerminatesWithinSeconds s io = do
result <- timeout (s * 1_000_000) io
case result of
Nothing -> assertFailure "Timeout"
Just () -> return ()