|
Packit |
bc3140 |
{-# LANGUAGE DeriveDataTypeable #-}
|
|
Packit |
bc3140 |
{-# LANGUAGE ConstraintKinds #-}
|
|
Packit |
bc3140 |
{-# LANGUAGE FlexibleContexts #-}
|
|
Packit |
bc3140 |
|
|
Packit |
bc3140 |
module Test.HUnit.Lang (
|
|
Packit |
bc3140 |
Assertion,
|
|
Packit |
bc3140 |
assertFailure,
|
|
Packit |
bc3140 |
assertEqual,
|
|
Packit |
bc3140 |
|
|
Packit |
bc3140 |
Result (..),
|
|
Packit |
bc3140 |
performTestCase,
|
|
Packit |
bc3140 |
-- * Internals
|
|
Packit |
bc3140 |
-- |
|
|
Packit |
bc3140 |
-- /Note:/ This is not part of the public API! It is exposed so that you can
|
|
Packit |
bc3140 |
-- tinker with the internals of HUnit, but do not expect it to be stable!
|
|
Packit |
bc3140 |
HUnitFailure (..),
|
|
Packit |
bc3140 |
FailureReason (..),
|
|
Packit |
bc3140 |
formatFailureReason
|
|
Packit |
bc3140 |
) where
|
|
Packit |
bc3140 |
|
|
Packit |
bc3140 |
import Control.DeepSeq
|
|
Packit |
bc3140 |
import Control.Exception as E
|
|
Packit |
bc3140 |
import Control.Monad
|
|
Packit |
bc3140 |
import Data.List
|
|
Packit |
bc3140 |
import Data.Typeable
|
|
Packit |
bc3140 |
import Data.CallStack
|
|
Packit |
bc3140 |
|
|
Packit |
bc3140 |
-- | When an assertion is evaluated, it will output a message if and only if the
|
|
Packit |
bc3140 |
-- assertion fails.
|
|
Packit |
bc3140 |
--
|
|
Packit |
bc3140 |
-- Test cases are composed of a sequence of one or more assertions.
|
|
Packit |
bc3140 |
type Assertion = IO ()
|
|
Packit |
bc3140 |
|
|
Packit |
bc3140 |
data HUnitFailure = HUnitFailure (Maybe SrcLoc) FailureReason
|
|
Packit |
bc3140 |
deriving (Eq, Show, Typeable)
|
|
Packit |
bc3140 |
|
|
Packit |
bc3140 |
instance Exception HUnitFailure
|
|
Packit |
bc3140 |
|
|
Packit |
bc3140 |
data FailureReason = Reason String | ExpectedButGot (Maybe String) String String
|
|
Packit |
bc3140 |
deriving (Eq, Show, Typeable)
|
|
Packit |
bc3140 |
|
|
Packit |
bc3140 |
location :: HasCallStack => Maybe SrcLoc
|
|
Packit |
bc3140 |
location = case reverse callStack of
|
|
Packit |
bc3140 |
(_, loc) : _ -> Just loc
|
|
Packit |
bc3140 |
[] -> Nothing
|
|
Packit |
bc3140 |
|
|
Packit |
bc3140 |
-- | Unconditionally signals that a failure has occured. All
|
|
Packit |
bc3140 |
-- other assertions can be expressed with the form:
|
|
Packit |
bc3140 |
--
|
|
Packit |
bc3140 |
-- @
|
|
Packit |
bc3140 |
-- if conditionIsMet
|
|
Packit |
bc3140 |
-- then IO ()
|
|
Packit |
bc3140 |
-- else assertFailure msg
|
|
Packit |
bc3140 |
-- @
|
|
Packit |
bc3140 |
assertFailure ::
|
|
Packit |
bc3140 |
HasCallStack =>
|
|
Packit |
bc3140 |
String -- ^ A message that is displayed with the assertion failure
|
|
Packit |
bc3140 |
-> IO a
|
|
Packit |
bc3140 |
assertFailure msg = msg `deepseq` E.throwIO (HUnitFailure location $ Reason msg)
|
|
Packit |
bc3140 |
|
|
Packit |
bc3140 |
-- | Asserts that the specified actual value is equal to the expected value.
|
|
Packit |
bc3140 |
-- The output message will contain the prefix, the expected value, and the
|
|
Packit |
bc3140 |
-- actual value.
|
|
Packit |
bc3140 |
--
|
|
Packit |
bc3140 |
-- If the prefix is the empty string (i.e., @\"\"@), then the prefix is omitted
|
|
Packit |
bc3140 |
-- and only the expected and actual values are output.
|
|
Packit |
bc3140 |
assertEqual :: (HasCallStack, Eq a, Show a)
|
|
Packit |
bc3140 |
=> String -- ^ The message prefix
|
|
Packit |
bc3140 |
-> a -- ^ The expected value
|
|
Packit |
bc3140 |
-> a -- ^ The actual value
|
|
Packit |
bc3140 |
-> Assertion
|
|
Packit |
bc3140 |
assertEqual preface expected actual =
|
|
Packit |
bc3140 |
unless (actual == expected) $ do
|
|
Packit |
bc3140 |
(prefaceMsg `deepseq` expectedMsg `deepseq` actualMsg `deepseq` E.throwIO (HUnitFailure location $ ExpectedButGot prefaceMsg expectedMsg actualMsg))
|
|
Packit |
bc3140 |
where
|
|
Packit |
bc3140 |
prefaceMsg
|
|
Packit |
bc3140 |
| null preface = Nothing
|
|
Packit |
bc3140 |
| otherwise = Just preface
|
|
Packit |
bc3140 |
expectedMsg = show expected
|
|
Packit |
bc3140 |
actualMsg = show actual
|
|
Packit |
bc3140 |
|
|
Packit |
bc3140 |
formatFailureReason :: FailureReason -> String
|
|
Packit |
bc3140 |
formatFailureReason (Reason reason) = reason
|
|
Packit |
bc3140 |
formatFailureReason (ExpectedButGot preface expected actual) = intercalate "\n" . maybe id (:) preface $ ["expected: " ++ expected, " but got: " ++ actual]
|
|
Packit |
bc3140 |
|
|
Packit |
bc3140 |
data Result = Success | Failure (Maybe SrcLoc) String | Error (Maybe SrcLoc) String
|
|
Packit |
bc3140 |
deriving (Eq, Show)
|
|
Packit |
bc3140 |
|
|
Packit |
bc3140 |
-- | Performs a single test case.
|
|
Packit |
bc3140 |
performTestCase :: Assertion -- ^ an assertion to be made during the test case run
|
|
Packit |
bc3140 |
-> IO Result
|
|
Packit |
bc3140 |
performTestCase action =
|
|
Packit |
bc3140 |
(action >> return Success)
|
|
Packit |
bc3140 |
`E.catches`
|
|
Packit |
bc3140 |
[E.Handler (\(HUnitFailure loc reason) -> return $ Failure loc (formatFailureReason reason)),
|
|
Packit |
bc3140 |
|
|
Packit |
bc3140 |
-- Re-throw AsyncException, otherwise execution will not terminate on
|
|
Packit |
bc3140 |
-- SIGINT (ctrl-c). Currently, all AsyncExceptions are being thrown
|
|
Packit |
bc3140 |
-- because it's thought that none of them will be encountered during
|
|
Packit |
bc3140 |
-- normal HUnit operation. If you encounter an example where this
|
|
Packit |
bc3140 |
-- is not the case, please email the maintainer.
|
|
Packit |
bc3140 |
E.Handler (\e -> throw (e :: E.AsyncException)),
|
|
Packit |
bc3140 |
|
|
Packit |
bc3140 |
E.Handler (\e -> return $ Error Nothing $ show (e :: E.SomeException))]
|