Skip to content

Commit b6728f1

Browse files
authored
Fix junit output, also ensure junit output is deterministic (#253)
1 parent a4d74ce commit b6728f1

File tree

2 files changed

+103
-29
lines changed

2 files changed

+103
-29
lines changed

pkg/output/junit.go

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ type TestCaseError struct {
6565
}
6666

6767
type junito struct {
68-
id int
69-
w io.Writer
70-
withSummary bool
71-
verbose bool
72-
suites map[string]*TestSuite // map filename to corresponding suite
73-
nValid, nInvalid, nErrors, nSkipped int
74-
startTime time.Time
68+
id int
69+
w io.Writer
70+
withSummary bool
71+
verbose bool
72+
suitesIndex map[string]int // map filename to index in suites
73+
suites []TestSuite
74+
startTime time.Time
7575
}
7676

7777
func junitOutput(w io.Writer, withSummary bool, isStdin, verbose bool) Output {
@@ -80,29 +80,28 @@ func junitOutput(w io.Writer, withSummary bool, isStdin, verbose bool) Output {
8080
w: w,
8181
withSummary: withSummary,
8282
verbose: verbose,
83-
suites: make(map[string]*TestSuite),
84-
nValid: 0,
85-
nInvalid: 0,
86-
nErrors: 0,
87-
nSkipped: 0,
83+
suites: []TestSuite{},
84+
suitesIndex: make(map[string]int),
8885
startTime: time.Now(),
8986
}
9087
}
9188

9289
// Write adds a result to the report.
9390
func (o *junito) Write(result validator.Result) error {
94-
var suite *TestSuite
95-
suite, found := o.suites[result.Resource.Path]
91+
var suite TestSuite
92+
i, found := o.suitesIndex[result.Resource.Path]
9693

9794
if !found {
9895
o.id++
99-
suite = &TestSuite{
96+
suite = TestSuite{
10097
Name: result.Resource.Path,
10198
Id: o.id,
10299
Tests: 0, Failures: 0, Errors: 0, Disabled: 0, Skipped: 0,
103100
Cases: make([]TestCase, 0),
104101
}
105-
o.suites[result.Resource.Path] = suite
102+
o.suites = append(o.suites, suite)
103+
i = len(o.suites) - 1
104+
o.suitesIndex[result.Resource.Path] = i
106105
}
107106

108107
sig, _ := result.Resource.Signature()
@@ -117,23 +116,22 @@ func (o *junito) Write(result validator.Result) error {
117116

118117
switch result.Status {
119118
case validator.Valid:
120-
o.nValid++
121119
case validator.Invalid:
122-
o.nInvalid++
120+
o.suites[i].Failures++
123121
failure := TestCaseError{Message: result.Err.Error()}
124122
testCase.Failure = append(testCase.Failure, failure)
125123
case validator.Error:
126-
o.nErrors++
124+
o.suites[i].Errors++
127125
testCase.Error = &TestCaseError{Message: result.Err.Error()}
128126
case validator.Skipped:
129127
testCase.Skipped = &TestCaseSkipped{}
130-
o.nSkipped++
128+
o.suites[i].Skipped++
131129
case validator.Empty:
132130
return nil
133131
}
134132

135-
suite.Tests++
136-
suite.Cases = append(suite.Cases, testCase)
133+
o.suites[i].Tests++
134+
o.suites[i].Cases = append(o.suites[i].Cases, testCase)
137135

138136
return nil
139137
}
@@ -142,19 +140,33 @@ func (o *junito) Write(result validator.Result) error {
142140
func (o *junito) Flush() error {
143141
runtime := time.Now().Sub(o.startTime)
144142

145-
var suites = make([]TestSuite, 0)
143+
totalValid := 0
144+
totalInvalid := 0
145+
totalErrors := 0
146+
totalSkipped := 0
147+
146148
for _, suite := range o.suites {
147-
suites = append(suites, *suite)
149+
for _, tCase := range suite.Cases {
150+
if tCase.Error != nil {
151+
totalErrors++
152+
} else if tCase.Skipped != nil {
153+
totalSkipped++
154+
} else if len(tCase.Failure) > 0 {
155+
totalInvalid++
156+
} else {
157+
totalValid++
158+
}
159+
}
148160
}
149161

150162
root := TestSuiteCollection{
151163
Name: "kubeconform",
152164
Time: runtime.Seconds(),
153-
Tests: o.nValid + o.nInvalid + o.nErrors + o.nSkipped,
154-
Failures: o.nInvalid,
155-
Errors: o.nErrors,
156-
Disabled: o.nSkipped,
157-
Suites: suites,
165+
Tests: totalValid + totalInvalid + totalErrors + totalSkipped,
166+
Failures: totalInvalid,
167+
Errors: totalErrors,
168+
Disabled: totalSkipped,
169+
Suites: o.suites,
158170
}
159171

160172
// 2-space indentation

pkg/output/junit_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package output
22

33
import (
44
"bytes"
5+
"fmt"
56
"regexp"
67
"testing"
78

@@ -85,6 +86,67 @@ metadata:
8586
" </testsuite>\n" +
8687
"</testsuites>\n",
8788
},
89+
{
90+
"one error, one invalid",
91+
true,
92+
false,
93+
false,
94+
[]validator.Result{
95+
{
96+
Resource: resource.Resource{
97+
Path: "deployment.yml",
98+
Bytes: []byte(`apiVersion: apps/v1
99+
kind: Deployment
100+
metadata:
101+
name: "my-app"
102+
`),
103+
},
104+
Status: validator.Error,
105+
Err: fmt.Errorf("error validating deployment.yml"),
106+
},
107+
{
108+
Resource: resource.Resource{
109+
Path: "deployment2.yml",
110+
Bytes: []byte(`apiVersion: apps/v1
111+
kind: Deployment
112+
metadata:
113+
name: "my-app"
114+
`),
115+
},
116+
Status: validator.Error,
117+
Err: fmt.Errorf("error validating deployment.yml"),
118+
},
119+
{
120+
Resource: resource.Resource{
121+
Path: "deployment3.yml",
122+
Bytes: []byte(`apiVersion: apps/v1
123+
kind: Deployment
124+
metadata:
125+
name: "my-app"
126+
`),
127+
},
128+
Status: validator.Invalid,
129+
Err: fmt.Errorf("deployment3.yml is invalid"),
130+
},
131+
},
132+
"<testsuites name=\"kubeconform\" time=\"\" tests=\"3\" failures=\"1\" disabled=\"0\" errors=\"2\">\n" +
133+
" <testsuite name=\"deployment.yml\" id=\"1\" tests=\"1\" failures=\"0\" errors=\"1\" disabled=\"0\" skipped=\"0\">\n" +
134+
" <testcase name=\"my-app\" classname=\"Deployment@apps/v1\" time=\"\">\n" +
135+
" <error message=\"error validating deployment.yml\" type=\"\"></error>\n" +
136+
" </testcase>\n" +
137+
" </testsuite>\n" +
138+
" <testsuite name=\"deployment2.yml\" id=\"2\" tests=\"1\" failures=\"0\" errors=\"1\" disabled=\"0\" skipped=\"0\">\n" +
139+
" <testcase name=\"my-app\" classname=\"Deployment@apps/v1\" time=\"\">\n" +
140+
" <error message=\"error validating deployment.yml\" type=\"\"></error>\n" +
141+
" </testcase>\n" +
142+
" </testsuite>\n" +
143+
" <testsuite name=\"deployment3.yml\" id=\"3\" tests=\"1\" failures=\"1\" errors=\"0\" disabled=\"0\" skipped=\"0\">\n" +
144+
" <testcase name=\"my-app\" classname=\"Deployment@apps/v1\" time=\"\">\n" +
145+
" <failure message=\"deployment3.yml is invalid\" type=\"\"></failure>\n" +
146+
" </testcase>\n" +
147+
" </testsuite>\n" +
148+
"</testsuites>\n",
149+
},
88150
} {
89151
w := new(bytes.Buffer)
90152
o := junitOutput(w, testCase.withSummary, testCase.isStdin, testCase.verbose)

0 commit comments

Comments
 (0)