packages feed

morley-0.3.0: test/Test/Tasty/HUnit.hs

-- | HUnit support for tasty.
--
-- We don't use `tasty-hunit`, because it doesn't interoperate properly with
-- other HUnit-based code.
-- Specifically, it defines its own `HUnitFailure` type and catches exceptions
-- of this type. It doesn't catch HUnit's `HUnitFailure`, so they are not
-- pretty-printed.

module Test.Tasty.HUnit
  ( testCase
  , testCaseInfo
  , testCaseSteps
  ) where

import Test.HUnit (Assertion)
import Test.HUnit.Lang (HUnitFailure(..), formatFailureReason)
import Test.Tasty (TestName, TestTree)
import Test.Tasty.Providers (IsTest(..), singleTest, testFailed, testPassed)

-- | Turn an 'Assertion' into a tasty test case
testCase :: TestName -> Assertion -> TestTree
testCase name = singleTest name . TestCaseWrapper . fmap (const "")

testCaseInfo :: TestName -> IO String -> TestTree
testCaseInfo name = singleTest name . TestCaseWrapper

-- We don't use this feature, so it's the same as simple 'testCase'.
testCaseSteps :: TestName -> ((String -> IO ()) -> Assertion) -> TestTree
testCaseSteps name f = testCase name (f $ const pass)

newtype TestCaseWrapper = TestCaseWrapper (IO String)

instance IsTest TestCaseWrapper where
  run _ (TestCaseWrapper assertion) _ = do
  -- The standard HUnit's performTestCase catches (almost) all exceptions.
  --
  -- This is bad for a few reasons:
  -- - it interferes with timeout handling
  -- - it makes exception reporting inconsistent across providers
  -- - it doesn't provide enough information for ingredients such as
  -- tasty-rerun
  --
  -- So we do it ourselves.
    hunitResult <- try @_ @HUnitFailure assertion
    return $
      case hunitResult of
        Right info -> testPassed info
        Left (HUnitFailure mbLoc reason) ->
          testFailed $ maybe id (mappend . (<> ":\n") . prettySrcLoc) mbLoc $
          formatFailureReason reason

  testOptions = return []