module Test.HUnitPlus.Text(
PutText(..),
putTextToHandle,
putTextToShowS,
showCounts,
textReporter,
runTestText,
runSuiteText,
runSuitesText,
terminalReporter,
runTestTT,
runSuiteTT,
runSuitesTT
) where
import Distribution.TestSuite
import Test.HUnitPlus.Base
import Test.HUnitPlus.Execution
import Test.HUnitPlus.Filter
import Test.HUnitPlus.Reporting
import Control.Monad (when)
import System.IO (Handle, stderr, hPutStr, hPutStrLn)
import Text.Printf(printf)
import qualified Data.Map as Map
data PutText st = PutText (String -> st -> IO st) st
putTextToHandle :: Handle -> PutText ()
putTextToHandle handle = PutText (\line () -> hPutStr handle line) ()
putTextToShowS :: PutText ShowS
putTextToShowS =
PutText (\line func -> return (\rest -> func (line ++ rest))) id
textReporter :: PutText us
-> Bool
-> Reporter us
textReporter (PutText put initUs) verbose =
let
reportProblem p0 p1 msg ss us =
let
kind = if null path then p0 else p1
path = showPath (stPath ss)
line = "### " ++ kind ++ path ++ ": " ++ msg ++ "\n"
in
put line us
reportOutput p0 p1 msg ss us =
let
kind = if null path then p0 else p1
path = showPath (stPath ss)
line = "### " ++ kind ++ path ++ ": " ++ msg ++ "\n"
in
if verbose then put line us
else return us
reportStartCase ss us =
let
path = showPath (stPath ss)
line = if null path then "Test case starting\n"
else "Test case " ++ path ++ " starting\n"
in
if verbose then put line us
else return us
reportEndCase time ss us =
let
path = showPath (stPath ss)
timestr = printf "%.6f" time
line = if null path then "Test completed in " ++ timestr ++ " sec\n"
else "Test " ++ path ++ " completed in " ++ timestr ++ " sec\n"
in
if verbose then put line us
else return us
in
defaultReporter {
reporterStart = return initUs,
reporterStartCase = reportStartCase,
reporterEndCase = reportEndCase,
reporterSystemOut = reportOutput "STDOUT " "STDOUT from ",
reporterSystemErr = reportOutput "STDERR" "STDERR from ",
reporterError = reportProblem "Error " "Error in ",
reporterFailure = reportProblem "Failure" "Failure in "
}
runTestText :: PutText us
-> Bool
-> Test
-> IO (Counts, us)
runTestText puttext @ (PutText put us0) verbose t =
let
initState = State { stCounts = zeroCounts, stName = "",
stPath = [], stOptions = Map.empty,
stOptionDescs = [] }
reporter = textReporter puttext verbose
in do
(ss1, us1) <- ((performTest $! reporter) allSelector $! initState) us0 t
us2 <- put (showCounts (stCounts ss1) ++ "\n") us1
return (stCounts ss1, us2)
runSuiteText :: PutText us
-> Bool
-> TestSuite
-> IO (Counts, us)
runSuiteText puttext @ (PutText put us0) verbose
suite @ TestSuite { suiteName = sname } =
let
selectorMap = Map.singleton sname allSelector
reporter = textReporter puttext verbose
in do
(counts, us1) <- ((performTestSuite $! reporter) $!selectorMap) us0 suite
us2 <- put (showCounts counts ++ "\n") us1
return (counts, us2)
runSuitesText :: PutText us
-> Bool
-> [TestSuite]
-> IO (Counts, us)
runSuitesText puttext @ (PutText put _) verbose suites =
let
suiteNames = map suiteName suites
selectorMap = foldl (\suitemap sname ->
Map.insert sname allSelector suitemap)
Map.empty suiteNames
reporter = textReporter puttext verbose
in do
(counts, us1) <- ((performTestSuites $! reporter) $! selectorMap) suites
us2 <- put (showCounts counts ++ "\n") us1
return (counts, us2)
showCounts :: Counts -> String
showCounts Counts { cCases = cases, cTried = tried,
cErrors = errors, cFailures = failures,
cAsserts = asserts, cSkipped = skipped } =
"Cases: " ++ show cases ++ " Tried: " ++ show tried ++
" Errors: " ++ show errors ++ " Failures: " ++ show failures ++
" Assertions: " ++ show asserts ++ " Skipped: " ++ show skipped
termPut :: String -> Bool -> Int -> IO Int
termPut line pers (1) = do when pers (hPutStrLn stderr line); return (1)
termPut line True cnt = do hPutStrLn stderr (erase cnt ++ line); return 0
termPut line False _ = do hPutStr stderr ('\r' : line); return (length line)
erase :: Int -> String
erase cnt = if cnt == 0 then "" else "\r" ++ replicate cnt ' ' ++ "\r"
terminalReporter :: Reporter Int
terminalReporter =
let
reportProblem p0 p1 msg ss us =
let
line = "### " ++ kind ++ path ++ '\n' : msg
path = showPath (stPath ss)
kind = if null path then p0 else p1
in
termPut line True us
in
defaultReporter {
reporterStart = return 0,
reporterEndCase =
(\_ ss us -> termPut (showCounts (stCounts ss)) False us),
reporterError = reportProblem "Error:" "Error in: ",
reporterFailure = reportProblem "Failure:" "Failure in: "
}
runTestTT :: Test -> IO Counts
runTestTT t =
let
initState = State { stCounts = zeroCounts, stName = "",
stPath = [], stOptions = Map.empty,
stOptionDescs = [] }
in do
(ss1, us1) <- (performTest terminalReporter allSelector $! initState) 0 t
0 <- termPut (showCounts (stCounts ss1)) True us1
return (stCounts ss1)
runSuiteTT :: TestSuite -> IO Counts
runSuiteTT suite @ TestSuite { suiteName = sname } =
let
selectorMap = Map.singleton sname allSelector
in do
(counts, us) <- (performTestSuite terminalReporter $! selectorMap) 0 suite
0 <- termPut (showCounts counts ++ "\n") True us
return counts
runSuitesTT :: [TestSuite] -> IO Counts
runSuitesTT suites =
let
suiteNames = map suiteName suites
selectorMap = foldl (\suitemap sname ->
Map.insert sname allSelector suitemap)
Map.empty suiteNames
in do
(counts, us) <- (performTestSuites terminalReporter $! selectorMap) suites
0 <- termPut (showCounts counts ++ "\n") True us
return counts