...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package bzltestutil
16
17 import (
18 "encoding/json"
19 "encoding/xml"
20 "fmt"
21 "io"
22 "path"
23 "sort"
24 "strings"
25 "time"
26 )
27
28 type xmlTestSuites struct {
29 XMLName xml.Name `xml:"testsuites"`
30 Suites []xmlTestSuite `xml:"testsuite"`
31 }
32
33 type xmlTestSuite struct {
34 XMLName xml.Name `xml:"testsuite"`
35 TestCases []xmlTestCase `xml:"testcase"`
36 Errors int `xml:"errors,attr"`
37 Failures int `xml:"failures,attr"`
38 Skipped int `xml:"skipped,attr"`
39 Tests int `xml:"tests,attr"`
40 Time string `xml:"time,attr"`
41 Name string `xml:"name,attr"`
42 }
43
44 type xmlTestCase struct {
45 XMLName xml.Name `xml:"testcase"`
46 Classname string `xml:"classname,attr"`
47 Name string `xml:"name,attr"`
48 Time string `xml:"time,attr"`
49 Failure *xmlMessage `xml:"failure,omitempty"`
50 Error *xmlMessage `xml:"error,omitempty"`
51 Skipped *xmlMessage `xml:"skipped,omitempty"`
52 }
53
54 type xmlMessage struct {
55 Message string `xml:"message,attr"`
56 Type string `xml:"type,attr"`
57 Contents string `xml:",chardata"`
58 }
59
60
61 type jsonEvent struct {
62 Time *time.Time
63 Action string
64 Package string
65 Test string
66 Elapsed *float64
67 Output string
68 }
69
70 type testCase struct {
71 state string
72 output strings.Builder
73 duration *float64
74 }
75
76
77
78 func json2xml(r io.Reader, pkgName string) ([]byte, error) {
79 var pkgDuration *float64
80 testcases := make(map[string]*testCase)
81 testCaseByName := func(name string) *testCase {
82 if name == "" {
83 return nil
84 }
85 if _, ok := testcases[name]; !ok {
86 testcases[name] = &testCase{}
87 }
88 return testcases[name]
89 }
90
91 dec := json.NewDecoder(r)
92 for {
93 var e jsonEvent
94 if err := dec.Decode(&e); err == io.EOF {
95 break
96 } else if err != nil {
97 return nil, fmt.Errorf("error decoding test2json output: %s", err)
98 }
99 switch s := e.Action; s {
100 case "run":
101 if c := testCaseByName(e.Test); c != nil {
102 c.state = s
103 }
104 case "output":
105 if c := testCaseByName(e.Test); c != nil {
106 c.output.WriteString(e.Output)
107 }
108 case "skip":
109 if c := testCaseByName(e.Test); c != nil {
110 c.output.WriteString(e.Output)
111 c.state = s
112 c.duration = e.Elapsed
113 }
114 case "fail":
115 if c := testCaseByName(e.Test); c != nil {
116 c.state = s
117 c.duration = e.Elapsed
118 } else {
119 pkgDuration = e.Elapsed
120 }
121 case "pass":
122 if c := testCaseByName(e.Test); c != nil {
123 c.duration = e.Elapsed
124 c.state = s
125 } else {
126 pkgDuration = e.Elapsed
127 }
128 }
129 }
130
131 return xml.MarshalIndent(toXML(pkgName, pkgDuration, testcases), "", "\t")
132 }
133
134 func toXML(pkgName string, pkgDuration *float64, testcases map[string]*testCase) *xmlTestSuites {
135 cases := make([]string, 0, len(testcases))
136 for k := range testcases {
137 cases = append(cases, k)
138 }
139 sort.Strings(cases)
140 suite := xmlTestSuite{
141 Name: pkgName,
142 }
143 if pkgDuration != nil {
144 suite.Time = fmt.Sprintf("%.3f", *pkgDuration)
145 }
146 for _, name := range cases {
147 c := testcases[name]
148 suite.Tests++
149 newCase := xmlTestCase{
150 Name: name,
151 Classname: path.Base(pkgName),
152 }
153 if c.duration != nil {
154 newCase.Time = fmt.Sprintf("%.3f", *c.duration)
155 }
156 switch c.state {
157 case "skip":
158 suite.Skipped++
159 newCase.Skipped = &xmlMessage{
160 Message: "Skipped",
161 Contents: c.output.String(),
162 }
163 case "fail":
164 suite.Failures++
165 newCase.Failure = &xmlMessage{
166 Message: "Failed",
167 Contents: c.output.String(),
168 }
169 case "pass":
170 break
171 default:
172 suite.Errors++
173 newCase.Error = &xmlMessage{
174 Message: "No pass/skip/fail event found for test",
175 Contents: c.output.String(),
176 }
177 }
178 suite.TestCases = append(suite.TestCases, newCase)
179 }
180 return &xmlTestSuites{Suites: []xmlTestSuite{suite}}
181 }
182
View as plain text