-- | Unit testing support for tasty, inspired by the HUnit package
{-# LANGUAGE TypeFamilies, DeriveDataTypeable #-}
module Test.Tasty.HUnit
  ( testCase
  , testCaseInfo
  , testCaseSteps
  , module Test.Tasty.HUnit.Orig
  ) where

import Test.Tasty.Providers

import Test.Tasty.HUnit.Orig
import Test.Tasty.HUnit.Steps

import Data.Typeable
import Control.Exception

-- | Create a 'Test' for a HUnit 'Assertion'
testCase :: TestName -> Assertion -> TestTree
testCase name = singleTest name . TestCase . (fmap (const ""))

-- | Like 'testCase', except in case the test succeeds, the returned string
-- will be shown as the description. If the empty string is returned, it
-- will be ignored.
testCaseInfo :: TestName -> IO String -> TestTree
testCaseInfo name = singleTest name . TestCase

-- IO String is a computation that throws an exception upon failure or
-- returns an informational string otherwise. This allows us to unify the
-- implementation of 'testCase' and 'testCaseInfo'.
--
-- In case of testCase, we simply make the result string empty, which makes
-- tasty ignore it.
newtype TestCase = TestCase (IO String)
    deriving Typeable

instance IsTest TestCase where
  run _ (TestCase 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 assertion
    return $
      case hunitResult of
        Right info -> testPassed info
        Left (HUnitFailure message) -> testFailed message

  testOptions = return []