Blame src/Test/HUnit/Lang.hs

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))]