HTF-0.12.0.0: Test/Framework/TestInterface.hs
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
--
-- Copyright (c) 2009-2012 Stefan Wehr - http://www.stefanwehr.de
--
-- This library is free software; you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public
-- License as published by the Free Software Foundation; either
-- version 2.1 of the License, or (at your option) any later version.
--
-- This library is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public
-- License along with this library; if not, write to the Free Software
-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
--
{- |
This module defines the API for HTF plugins.
-}
module Test.Framework.TestInterface (
Assertion, TestResult(..), FullTestResult(..), HTFFailureException(..), failHTF, subAssertHTF
, mkFullTestResult
) where
import Test.Framework.Location
import Test.Framework.Colors
import Control.Monad.Trans.Control
import Data.Typeable
import qualified Control.Exception as Exc
import qualified Control.Exception.Lifted as ExcLifted
{- | An assertion is just an 'IO' action. Internally, the body of any test
in HTF is of type 'Assertion'. If a test specification of a certain plugin
has a type different from 'Assertion', the plugin's preprocessor pass must
inject wrapper code to convert the test specification into an assertion.
Assertions may use 'failHTF' to signal a 'TestResult' different from
'Pass'. If the assertion finishes successfully, the tests passes
implicitly.
Please note: the assertion must not swallow any exceptions! Otherwise,
timeouts and other things might not work as expected.
-}
type Assertion = IO ()
-- | The summary result of a test.
data TestResult = Pass | Pending | Fail | Error
deriving (Show, Read, Eq)
-- | The full result of a test, as used by HTF plugins.
data FullTestResult
= FullTestResult
{ ftr_location :: Maybe Location -- ^ The location of a possible failure
, ftr_callingLocations :: [(Maybe String, Location)] -- ^ The "stack" to the location of a possible failure
, ftr_message :: Maybe ColorString -- ^ An error message
, ftr_result :: Maybe TestResult -- ^ The outcome of the test, 'Nothing' means timeout
} deriving (Eq, Show, Read)
-- | Auxiliary function for contructing a 'FullTestResult'.
mkFullTestResult :: TestResult -> Maybe String -> FullTestResult
mkFullTestResult r msg =
FullTestResult
{ ftr_location = Nothing
, ftr_callingLocations = []
, ftr_message = fmap noColor msg
, ftr_result = Just r
}
-- Internal exception type for propagating exceptions.
data HTFFailureException
= HTFFailure FullTestResult
deriving (Show, Typeable)
instance Exc.Exception HTFFailureException
{- |
Terminate a HTF test, usually to signal a failure. The result of the test
is given in the 'FullTestResult' argument.
-}
failHTF :: MonadBaseControl IO m => FullTestResult -> m a
-- Important: force the string argument, otherwise an error embedded
-- lazily inside the string might escape.
failHTF r = length (show r) `seq` ExcLifted.throwIO (HTFFailure r)
{- |
Opens a new assertion stack frame to allow for sensible location information.
-}
subAssertHTF :: MonadBaseControl IO m => Location -> Maybe String -> m a -> m a
subAssertHTF loc mMsg action =
action `ExcLifted.catch`
(\(HTFFailure res) ->
let newRes = res { ftr_callingLocations = (mMsg, loc) : ftr_callingLocations res }
in failHTF newRes)