Skip to content

Commit f5338b0

Browse files
authoredOct 16, 2022
Validate JUnit output against Jenkins JUnit XSD (#134)
* Validate JUnit output against Jenkins JUnix XSD * Add missing Jenkins JUnit XSD * Add time to TestCase for #127
1 parent f68d6ec commit f5338b0

File tree

6 files changed

+142
-17
lines changed

6 files changed

+142
-17
lines changed
 

‎Dockerfile.bats

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
FROM bats/bats:v1.2.1
2-
RUN apk --no-cache add ca-certificates parallel
2+
RUN apk --no-cache add ca-certificates parallel libxml2-utils
33
COPY dist/kubeconform_linux_amd64/kubeconform /code/bin/
44
COPY acceptance.bats acceptance-nonetwork.bats /code/
55
COPY fixtures /code/fixtures

‎Makefile

+3
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,6 @@ release:
4040
update-deps:
4141
go get -u ./...
4242
go mod tidy
43+
44+
update-junit-xsd:
45+
curl https://raw.githubusercontent.com/junit-team/junit5/main/platform-tests/src/test/resources/jenkins-junit.xsd > fixtures/junit.xsd

‎acceptance.bats

+7
Original file line numberDiff line numberDiff line change
@@ -316,3 +316,10 @@ resetCacheFolder() {
316316
[ "$status" -eq 0 ]
317317
[ "$output" = 'Summary: 100000 resources found parsing stdin - Valid: 100000, Invalid: 0, Errors: 0, Skipped: 0' ]
318318
}
319+
320+
@test "JUnit output can be validated against the Junit schema definition" {
321+
run bash -c "bin/kubeconform -output junit -summary fixtures/valid.yaml > output.xml"
322+
[ "$status" -eq 0 ]
323+
run xmllint --noout --schema fixtures/junit.xsd output.xml
324+
[ "$status" -eq 0 ]
325+
}

‎fixtures/junit.xsd

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<!--
3+
Source: https://svn.jenkins-ci.org/trunk/hudson/dtkit/dtkit-format/dtkit-junit-model/src/main/resources/com/thalesgroup/dtkit/junit/model/xsd/junit-4.xsd
4+
5+
This file available under the terms of the MIT License as follows:
6+
7+
*******************************************************************************
8+
* Copyright (c) 2010 Thales Corporate Services SAS *
9+
* *
10+
* Permission is hereby granted, free of charge, to any person obtaining a copy *
11+
* of this software and associated documentation files (the "Software"), to deal*
12+
* in the Software without restriction, including without limitation the rights *
13+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell *
14+
* copies of the Software, and to permit persons to whom the Software is *
15+
* furnished to do so, subject to the following conditions: *
16+
* *
17+
* The above copyright notice and this permission notice shall be included in *
18+
* all copies or substantial portions of the Software. *
19+
* *
20+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
21+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
22+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
23+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
24+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,*
25+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN *
26+
* THE SOFTWARE. *
27+
********************************************************************************
28+
-->
29+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
30+
31+
<xs:element name="failure">
32+
<xs:complexType mixed="true">
33+
<xs:attribute name="type" type="xs:string" use="optional"/>
34+
<xs:attribute name="message" type="xs:string" use="optional"/>
35+
</xs:complexType>
36+
</xs:element>
37+
38+
<xs:element name="error">
39+
<xs:complexType mixed="true">
40+
<xs:attribute name="type" type="xs:string" use="optional"/>
41+
<xs:attribute name="message" type="xs:string" use="optional"/>
42+
</xs:complexType>
43+
</xs:element>
44+
45+
<xs:element name="properties">
46+
<xs:complexType>
47+
<xs:sequence>
48+
<xs:element ref="property" maxOccurs="unbounded"/>
49+
</xs:sequence>
50+
</xs:complexType>
51+
</xs:element>
52+
53+
<xs:element name="property">
54+
<xs:complexType>
55+
<xs:attribute name="name" type="xs:string" use="required"/>
56+
<xs:attribute name="value" type="xs:string" use="required"/>
57+
</xs:complexType>
58+
</xs:element>
59+
60+
<xs:element name="skipped" type="xs:string"/>
61+
<xs:element name="system-err" type="xs:string"/>
62+
<xs:element name="system-out" type="xs:string"/>
63+
64+
<xs:element name="testcase">
65+
<xs:complexType>
66+
<xs:sequence>
67+
<xs:element ref="skipped" minOccurs="0" maxOccurs="1"/>
68+
<xs:element ref="error" minOccurs="0" maxOccurs="unbounded"/>
69+
<xs:element ref="failure" minOccurs="0" maxOccurs="unbounded"/>
70+
<xs:element ref="system-out" minOccurs="0" maxOccurs="unbounded"/>
71+
<xs:element ref="system-err" minOccurs="0" maxOccurs="unbounded"/>
72+
</xs:sequence>
73+
<xs:attribute name="name" type="xs:string" use="required"/>
74+
<xs:attribute name="assertions" type="xs:string" use="optional"/>
75+
<xs:attribute name="time" type="xs:string" use="optional"/>
76+
<xs:attribute name="classname" type="xs:string" use="optional"/>
77+
<xs:attribute name="status" type="xs:string" use="optional"/>
78+
</xs:complexType>
79+
</xs:element>
80+
81+
<xs:element name="testsuite">
82+
<xs:complexType>
83+
<xs:sequence>
84+
<xs:element ref="properties" minOccurs="0" maxOccurs="1"/>
85+
<xs:element ref="testcase" minOccurs="0" maxOccurs="unbounded"/>
86+
<xs:element ref="system-out" minOccurs="0" maxOccurs="1"/>
87+
<xs:element ref="system-err" minOccurs="0" maxOccurs="1"/>
88+
</xs:sequence>
89+
<xs:attribute name="name" type="xs:string" use="required"/>
90+
<xs:attribute name="tests" type="xs:string" use="required"/>
91+
<xs:attribute name="failures" type="xs:string" use="optional"/>
92+
<xs:attribute name="errors" type="xs:string" use="optional"/>
93+
<xs:attribute name="time" type="xs:string" use="optional"/>
94+
<xs:attribute name="disabled" type="xs:string" use="optional"/>
95+
<xs:attribute name="skipped" type="xs:string" use="optional"/>
96+
<xs:attribute name="timestamp" type="xs:string" use="optional"/>
97+
<xs:attribute name="hostname" type="xs:string" use="optional"/>
98+
<xs:attribute name="id" type="xs:string" use="optional"/>
99+
<xs:attribute name="package" type="xs:string" use="optional"/>
100+
</xs:complexType>
101+
</xs:element>
102+
103+
<xs:element name="testsuites">
104+
<xs:complexType>
105+
<xs:sequence>
106+
<xs:element ref="testsuite" minOccurs="0" maxOccurs="unbounded"/>
107+
</xs:sequence>
108+
<xs:attribute name="name" type="xs:string" use="optional"/>
109+
<xs:attribute name="time" type="xs:string" use="optional"/>
110+
<xs:attribute name="tests" type="xs:string" use="optional"/>
111+
<xs:attribute name="failures" type="xs:string" use="optional"/>
112+
<xs:attribute name="disabled" type="xs:string" use="optional"/>
113+
<xs:attribute name="errors" type="xs:string" use="optional"/>
114+
</xs:complexType>
115+
</xs:element>
116+
117+
118+
</xs:schema>

‎pkg/output/junit.go

+11-12
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,22 @@ type Property struct {
3333
}
3434

3535
type TestSuite struct {
36-
XMLName xml.Name `xml:"testsuite"`
37-
Properties []*Property `xml:"properties>property,omitempty"`
38-
Cases []TestCase `xml:"testcase"`
39-
Name string `xml:"name,attr"`
40-
Id int `xml:"id,attr"`
41-
Tests int `xml:"tests,attr"`
42-
Failures int `xml:"failures,attr"`
43-
Errors int `xml:"errors,attr"`
44-
Disabled int `xml:"disabled,attr"`
45-
Skipped int `xml:"skipped,attr"`
36+
XMLName xml.Name `xml:"testsuite"`
37+
Cases []TestCase `xml:"testcase"`
38+
Name string `xml:"name,attr"`
39+
Id int `xml:"id,attr"`
40+
Tests int `xml:"tests,attr"`
41+
Failures int `xml:"failures,attr"`
42+
Errors int `xml:"errors,attr"`
43+
Disabled int `xml:"disabled,attr"`
44+
Skipped int `xml:"skipped,attr"`
4645
}
4746

4847
type TestCase struct {
4948
XMLName xml.Name `xml:"testcase"`
5049
Name string `xml:"name,attr"`
5150
ClassName string `xml:"classname,attr"`
51+
Time int `xml:"time,attr"` // Optional, but for Buildkite support https://github.com/yannh/kubeconform/issues/127
5252
Skipped *TestCaseSkipped `xml:"skipped,omitempty"`
5353
Error *TestCaseError `xml:"error,omitempty"`
5454
Failure []TestCaseError `xml:"failure,omitempty"`
@@ -100,8 +100,7 @@ func (o *junito) Write(result validator.Result) error {
100100
Name: result.Resource.Path,
101101
Id: o.id,
102102
Tests: 0, Failures: 0, Errors: 0, Disabled: 0, Skipped: 0,
103-
Cases: make([]TestCase, 0),
104-
Properties: make([]*Property, 0),
103+
Cases: make([]TestCase, 0),
105104
}
106105
o.suites[result.Resource.Path] = suite
107106
}

‎pkg/output/junit_test.go

+2-4
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@ metadata:
4848
},
4949
"<testsuites name=\"kubeconform\" time=\"\" tests=\"1\" failures=\"0\" disabled=\"0\" errors=\"0\">\n" +
5050
" <testsuite name=\"deployment.yml\" id=\"1\" tests=\"1\" failures=\"0\" errors=\"0\" disabled=\"0\" skipped=\"0\">\n" +
51-
" <properties></properties>\n" +
52-
" <testcase name=\"my-app\" classname=\"Deployment@apps/v1\"></testcase>\n" +
51+
" <testcase name=\"my-app\" classname=\"Deployment@apps/v1\" time=\"\"></testcase>\n" +
5352
" </testsuite>\n" +
5453
"</testsuites>\n",
5554
},
@@ -82,8 +81,7 @@ metadata:
8281
},
8382
"<testsuites name=\"kubeconform\" time=\"\" tests=\"1\" failures=\"0\" disabled=\"0\" errors=\"0\">\n" +
8483
" <testsuite name=\"deployment.yml\" id=\"1\" tests=\"1\" failures=\"0\" errors=\"0\" disabled=\"0\" skipped=\"0\">\n" +
85-
" <properties></properties>\n" +
86-
" <testcase name=\"my-app\" classname=\"Deployment@apps/v1\"></testcase>\n" +
84+
" <testcase name=\"my-app\" classname=\"Deployment@apps/v1\" time=\"\"></testcase>\n" +
8785
" </testsuite>\n" +
8886
"</testsuites>\n",
8987
},

0 commit comments

Comments
 (0)
Please sign in to comment.