module Test.HUnitPlus.Execution(
performTestCase,
performTest,
performTestSuite,
performTestSuites
) where
import Control.Monad (unless, foldM)
import Distribution.TestSuite
import Data.Map(Map)
import Prelude hiding (elem)
import System.TimeIt
import Test.HUnitPlus.Base
import Test.HUnitPlus.Filter
import Test.HUnitPlus.Reporting
import qualified Data.Set as Set
import qualified Data.Map as Map
performTestCase :: Reporter us
-> State
-> us
-> TestInstance
-> IO (State, us)
performTestCase rep @ Reporter { reporterStartCase = reportStartCase,
reporterError = reportError,
reporterEndCase = reportEndCase }
ss @ State { stCounts = c @ Counts { cTried = tried,
cCases = cases },
stName = oldname, stOptions = optmap,
stOptionDescs = descs } initialUs
initTi @ TestInstance { name = testname,
options = testdescs,
setOption = setopt } =
let
alldescs = testdescs ++ descs
ssWithName = ss { stName = testname, stCounts = c { cTried = tried + 1,
cCases = cases + 1 } }
applyOptions (us, ti) OptionDescr { optionName = optname,
optionDefault = def } =
let
setresult :: Either String TestInstance
setresult =
case Map.lookup optname optmap of
Just optval -> setopt optname optval
Nothing -> case def of
Just optval -> setopt optname optval
Nothing -> Right ti
in case setresult of
Left errmsg ->
do
newUs <- reportError errmsg ssWithName us
return $! (newUs, ti)
Right newTi -> return $! (us, newTi)
in do
(usOpts, TestInstance { run = runTest }) <-
foldM applyOptions (initialUs, initTi) alldescs
usStarted <- reportStartCase ssWithName usOpts
(time, ssFinal, usFinal) <- executeTest rep ssWithName usStarted runTest
usEnded <- reportEndCase time ssFinal usFinal
return $ (ssFinal { stName = oldname }, usEnded)
skipTestCase :: Reporter us
-> State
-> us
-> TestInstance
-> IO (State, us)
skipTestCase Reporter { reporterSkipCase = reportSkipCase }
ss @ State { stCounts = c @ Counts { cSkipped = skipped,
cCases = cases },
stName = oldname } us
TestInstance { name = testname } =
let
ss' = ss { stCounts = c { cSkipped = skipped + 1, cCases = cases + 1 },
stName = testname }
in do
us' <- reportSkipCase ss' us
return $! (ss' { stName = oldname }, us')
performTest :: Reporter us
-> Selector
-> State
-> us
-> Test
-> IO (State, us)
performTest rep initSelector initState initialUs initialTest =
let
performTest' Selector { selectorInners = inners, selectorTags = currtags }
ss us Group { groupTests = testlist, groupName = gname } =
let
selector' =
case Map.lookup gname inners of
Nothing -> Selector { selectorInners = Map.empty,
selectorTags = currtags }
Just inner @ Selector { selectorTags = innertags } ->
inner { selectorTags = combineTags currtags innertags }
oldpath = stPath ss
ssWithPath = ss { stPath = Label gname : oldpath }
foldfun (ss', us') t = performTest' selector' ss' us' t
in do
(ssAfter, usAfter) <- foldM foldfun (ssWithPath, us) testlist
return $! (ssAfter { stPath = oldpath }, usAfter)
performTest' Selector { selectorInners = inners, selectorTags = currtags }
ss us (Test t @ TestInstance { name = testname,
tags = testtags }) =
let
finaltags =
case Map.lookup testname inners of
Nothing -> currtags
Just Selector { selectorTags = innertags } ->
combineTags currtags innertags
canExecute =
case finaltags of
Nothing -> False
Just set
| set == Set.empty -> True
| otherwise -> any (\tag -> Set.member tag set) testtags
in
if canExecute
then performTestCase rep ss us t
else skipTestCase rep ss us t
performTest' selector ss @ State { stOptionDescs = descs }
us (ExtraOptions newopts inner) =
performTest' selector ss { stOptionDescs = descs ++ newopts } us inner
in do
(ss', us') <- performTest' initSelector initState initialUs initialTest
unless (null (stPath ss')) $ error "performTest: Final path is nonnull"
return $! (ss', us')
performTestSuite :: Reporter us
-> Map String Selector
-> us
-> TestSuite
-> IO (Counts, us)
performTestSuite rep @ Reporter { reporterStartSuite = reportStartSuite,
reporterEndSuite = reportEndSuite }
filters initialUs
TestSuite { suiteName = sname, suiteTests = testlist,
suiteOptions = suiteOpts } =
case Map.lookup sname filters of
Just selector ->
let
initState = State { stCounts = zeroCounts, stName = sname,
stPath = [], stOptions = Map.fromList suiteOpts,
stOptionDescs = [] }
foldfun (c, us) testcase = performTest rep selector c us testcase
in do
startedUs <- reportStartSuite initState initialUs
(time, (finishedState, finishedUs)) <-
timeItT (foldM foldfun (initState, startedUs) testlist)
endedUs <- reportEndSuite time finishedState finishedUs
return $! (stCounts finishedState, endedUs)
_ ->
return $! (Counts { cCases = 0, cTried = 0, cErrors = 0, cFailures = 0,
cAsserts = 0, cSkipped = 0 }, initialUs)
performTestSuites :: Reporter us
-> Map String Selector
-> [TestSuite]
-> IO (Counts, us)
performTestSuites rep @ Reporter { reporterStart = reportStart,
reporterEnd = reportEnd }
filters suites =
let
initialCounts = Counts { cCases = 0, cTried = 0, cErrors = 0,
cFailures = 0, cAsserts = 0, cSkipped = 0 }
combineCounts Counts { cCases = cases1, cTried = tried1,
cErrors = errors1, cFailures = failures1,
cAsserts = asserts1, cSkipped = skipped1 }
Counts { cCases = cases2, cTried = tried2,
cErrors = errors2, cFailures = failures2,
cAsserts = asserts2, cSkipped = skipped2 } =
Counts { cCases = cases1 + cases2, cTried = tried1 + tried2,
cErrors = errors1 + errors2, cFailures = failures1 + failures2,
cAsserts = asserts1 + asserts2, cSkipped = skipped1 + skipped2 }
foldfun (accumCounts, accumUs) suite =
do
(suiteCounts, suiteUs) <- performTestSuite rep filters accumUs suite
return $! (combineCounts accumCounts suiteCounts, suiteUs)
in do
initialUs <- reportStart
(time, (finishedCounts, finishedUs)) <-
timeItT (foldM foldfun (initialCounts, initialUs) suites)
endedUs <- reportEnd time finishedCounts finishedUs
return $! (finishedCounts, endedUs)