module Test.HUnitPlus.XML(
propertyElem,
propertiesElem,
systemOutElem,
systemErrElem,
failureElem,
errorElem,
testcaseElem,
skippedTestElem,
testSuiteElem,
testSuitesElem,
xmlReporter
) where
import Data.Map(Map)
import Data.Time
import Data.Word
import Network.HostName
import System.Locale
import Test.HUnitPlus.Reporting(Reporter(..), State(..), Counts(..),
defaultReporter, showPath)
import Text.XML.Expat.Tree
import qualified Data.Map as Map
propertyElem :: (String, String)
-> Node String String
propertyElem (name, value) = Element { eName = "property", eChildren = [],
eAttributes = [("name", name),
("value", value)] }
propertiesElem :: [(String, String)]
-> Node String String
propertiesElem props = Element { eName = "properties", eAttributes = [],
eChildren = map propertyElem props }
systemOutElem :: String
-> Node String String
systemOutElem content = Element { eName = "system-out", eAttributes = [],
eChildren = [Text content] }
systemErrElem :: String
-> Node String String
systemErrElem content = Element { eName = "system-err", eAttributes = [],
eChildren = [Text content] }
failureElem :: String
-> Node String String
failureElem message = Element { eAttributes = [("message", message)],
eName = "failure", eChildren = [] }
errorElem :: String
-> Node String String
errorElem message = Element { eAttributes = [("message", message)],
eName = "error", eChildren = [] }
testcaseElem :: String
-> String
-> Word
-> Double
-> [Node String String]
-> Node String String
testcaseElem name classname assertions time children =
Element { eName = "testcase", eChildren = children,
eAttributes = [("name", name),
("classname", classname),
("assertions", show assertions),
("time", show time)] }
skippedTestElem :: String
-> String
-> Node String String
skippedTestElem name classname =
let
skippedElem = Element { eName = "skipped", eAttributes = [],
eChildren = [] }
in
Element { eAttributes = [("name", name), ("classname", classname)],
eName = "testcase", eChildren = [skippedElem] }
testSuiteElem :: String
-> Map String String
-> Word
-> Word
-> Word
-> Word
-> String
-> UTCTime
-> Double
-> [Node String String]
-> Node String String
testSuiteElem name propmap tests failures errors skipped
hostname timestamp time content =
let
contentWithProps =
case Map.assocs propmap of
[] -> content
props -> propertiesElem props : content
timestr = formatTime defaultTimeLocale "%c" timestamp
in
Element { eName = "testsuite", eChildren = contentWithProps,
eAttributes = [("name", name),
("hostname", hostname),
("timestamp", timestr),
("time", show time),
("tests", show tests),
("failures", show failures),
("errors", show errors),
("skipped", show skipped)] }
testSuitesElem :: Double
-> [Node String String]
-> Node String String
testSuitesElem time suites =
Element { eName = "testsuites", eChildren = suites,
eAttributes = [("time", show time)] }
xmlReporter :: Reporter [[Node String String]]
xmlReporter =
let
reportStart = return [[]]
reportEnd time _ [suites] = return [[testSuitesElem time (reverse suites)]]
reportEnd _ _ _ = fail "Extra information on node stack"
reportStartSuite _ stack = return ([] : stack)
reportEndSuite time State { stName = name, stOptions = options,
stCounts = Counts { cCases = cases,
cErrors = errors,
cFailures = failures,
cSkipped = skipped } }
(events : rest : stack) =
do
hostname <- getHostName
timestamp <- getCurrentTime
return ((testSuiteElem name options cases failures errors skipped
hostname timestamp time (reverse events) :
rest) : stack)
reportEndSuite _ _ stack =
fail ("Node stack underflow in end suite.\n" ++ show stack)
reportStartCase _ stack = return ([] : stack)
reportEndCase time State { stName = name, stPath = testpath,
stCounts = Counts { cAsserts = asserts } }
(events : rest : stack) =
return ((testcaseElem name (showPath testpath)
asserts time (reverse events) : rest) : stack)
reportEndCase _ _ _ = fail "Node stack underflow in end case"
reportSkipCase State { stName = name, stPath = testpath } (rest : stack) =
return ((skippedTestElem name (showPath testpath) : rest) : stack)
reportSkipCase _ _ = fail "Node stack underflow in skip case"
reportFailure msg _ (rest : stack) =
return ((failureElem msg : rest) : stack)
reportFailure _ _ _ = fail "Node stack underflow in report failure"
reportError msg _ (rest : stack) =
return ((errorElem msg : rest) : stack)
reportError _ _ _ = fail "Node stack underflow in report error"
reportSystemOut msg _ (rest : stack) =
return ((systemOutElem msg : rest) : stack)
reportSystemOut _ _ _ = fail "Node stack underflow in system out"
reportSystemErr msg _ (rest : stack) =
return ((systemErrElem msg : rest) : stack)
reportSystemErr _ _ _ = fail "Node stack underflow in system err"
in
defaultReporter {
reporterStart = reportStart,
reporterEnd = reportEnd,
reporterStartSuite = reportStartSuite,
reporterEndSuite = reportEndSuite,
reporterStartCase = reportStartCase,
reporterEndCase = reportEndCase,
reporterSkipCase = reportSkipCase,
reporterFailure = reportFailure,
reporterError = reportError,
reporterSystemOut = reportSystemOut,
reporterSystemErr = reportSystemErr
}