0 | module Evince.Reporter.JUnit
10 | import Evince.Reporter
12 | escape : String -> String
13 | escape = concatMap escChar . unpack
15 | escChar : Char -> String
16 | escChar '&' = "&"
17 | escChar '<' = "<"
18 | escChar '>' = ">"
19 | escChar '"' = """
20 | escChar '\'' = "'"
21 | escChar c = singleton c
23 | splitLast : List String -> (List String, String)
24 | splitLast [] = ([], "")
25 | splitLast [x] = ([], x)
26 | splitLast (x :: xs) = let (pre, l) = splitLast xs in (x :: pre, l)
28 | classname : List String -> String
29 | classname path = concat (intersperse "." (fst (splitLast path)))
31 | testName : List String -> String
32 | testName path = snd (splitLast path)
34 | locAttrs : Maybe SrcLoc -> String
35 | locAttrs Nothing = ""
36 | locAttrs (Just loc) = " file=\"" ++ escape loc.file ++ "\" line=\"" ++ show (loc.line + 1) ++ "\""
38 | renderTestCase : TestReport -> String
39 | renderTestCase report =
40 | let cn = escape (classname report.path)
41 | name = escape (testName report.path)
42 | loc = locAttrs report.loc
43 | in case report.outcome of
45 | " <testcase name=\"" ++ name ++ "\" classname=\"" ++ cn
46 | ++ "\"" ++ loc ++ " time=\"" ++ nanosToSeconds elapsed ++ "\"/>\n"
47 | Failed info elapsed =>
48 | let msg = case failureDiff info of
49 | Just (reason, diffs) =>
50 | reason ++ "\n" ++ unlines (map renderLineDiffPlain diffs)
51 | Nothing => show info
52 | in " <testcase name=\"" ++ name ++ "\" classname=\"" ++ cn
53 | ++ "\"" ++ loc ++ " time=\"" ++ nanosToSeconds elapsed ++ "\">\n"
54 | ++ " <failure message=\"" ++ escape msg ++ "\"/>\n"
57 | " <testcase name=\"" ++ name ++ "\" classname=\"" ++ cn ++ "\"" ++ loc ++ ">\n"
59 | ++ maybe "/>\n" (\r => " message=\"" ++ escape r ++ "\"/>\n") reason
62 | countFailures : List TestReport -> Nat
63 | countFailures = foldl (\acc, r => case r.outcome of Failed _ _ => S acc;
_ => acc) 0
65 | countSkipped : List TestReport -> Nat
66 | countSkipped = foldl (\acc, r => case r.outcome of Skipped _ => S acc;
_ => acc) 0
68 | totalTime : List TestReport -> Integer
69 | totalTime = foldl (\acc, r => case r.outcome of
70 | Passed e => acc + e;
Failed _ e => acc + e;
Skipped _ => acc) 0
74 | renderXml : List TestReport -> String
76 | "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
78 | ++ " <testsuite name=\"evince\" tests=\"" ++ show (length reports)
79 | ++ "\" failures=\"" ++ show (countFailures reports)
80 | ++ "\" skipped=\"" ++ show (countSkipped reports)
81 | ++ "\" time=\"" ++ nanosToSeconds (totalTime reports) ++ "\">\n"
82 | ++ concatMap renderTestCase reports
83 | ++ " </testsuite>\n"
84 | ++ "</testsuites>\n"
86 | writeJUnitXml : String -> List TestReport -> IO ()
87 | writeJUnitXml filepath reports = do
88 | Right () <- writeFile filepath (renderXml reports)
89 | | Left err => putStrLn $
"Error writing JUnit XML: " ++ show err
95 | junitReporter : String -> IO Reporter
96 | junitReporter filepath = do
97 | ref <- newIORef {a = SnocList TestReport} [<]
98 | pure $
MkReporter $
\case
99 | TestDone report _ => modifyIORef ref (:< report)
101 | reports <- readIORef ref
102 | writeJUnitXml filepath (reports <>> [])