/
junit_formatter.cr
126 lines (106 loc) · 3.09 KB
/
junit_formatter.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
require "html"
module Spec
# :nodoc:
class JUnitFormatter < Formatter
@started_at = Time.utc
@results = [] of Spec::Result
@summary = {} of Symbol => Int32
def report(result)
current = @summary[result.kind]? || 0
@summary[result.kind] = current + 1
@results << result
end
def finish(elapsed_time, aborted)
io = @io
io.puts %(<?xml version="1.0"?>)
io << %(<testsuite tests=") << @results.size
io << %(" skipped=") << (@summary[:pending]? || 0)
io << %(" errors=") << (@summary[:error]? || 0)
io << %(" failures=") << (@summary[:fail]? || 0)
io << %(" time=") << elapsed_time.total_seconds
io << %(" timestamp=") << @started_at.to_rfc3339
io << %(" hostname=") << System.hostname
io << %(">)
io.puts
@results.each { |r| write_report(r, io) }
io << %(</testsuite>)
io.close
end
def self.file(output_path : Path)
if output_path.extension != ".xml"
output_path = output_path.join("output.xml")
end
Dir.mkdir_p(output_path.dirname)
file = File.new(output_path, "w")
JUnitFormatter.new(file)
end
private def escape_xml_attr(value)
String.build do |io|
reader = Char::Reader.new(value)
while reader.has_next?
case current_char = reader.current_char
when .control?
current_char.to_s.inspect_unquoted(io)
else
current_char.to_s(io)
end
reader.next_char
end
end
end
# -------- private utility methods
private def write_report(result, io)
io << %( <testcase file=")
HTML.escape(result.file, io)
io << %(" classname=")
HTML.escape(classname(result), io)
io << %(" name=")
HTML.escape(escape_xml_attr(result.description), io)
if elapsed = result.elapsed
io << %(" time=")
io << elapsed.total_seconds
end
if tag = inner_content_tag(result.kind)
io.puts %(">)
if (exception = result.exception) && result.kind != :pending
write_inner_content(tag, exception, io)
else
io << " <" << tag << "/>\n"
end
io.puts " </testcase>"
else
io.puts %("/>)
end
end
private def inner_content_tag(kind)
case kind
when :error then "error"
when :fail then "failure"
when :pending then "skipped"
else nil
end
end
private def write_inner_content(tag, exception, io)
io << " <" << tag
if message = exception.message
io << %( message=")
HTML.escape(message, io)
io << '"'
end
if tag == :error
io << %( type=")
io << exception.class.name
io << '"'
end
io << '>'
if backtrace = exception.backtrace?
HTML.escape(backtrace.join('\n'), io)
end
io << "</" << tag << ">\n"
end
private def classname(result)
path = Path.new result.file
path.expand.relative_to(Dir.current).parts.join('.').rchop path.extension
end
end
end