Skip to content

Commit 91ae58c

Browse files
committed
validate against jenkins/xunit-plugin junit-10.xsd
- copyright notice left intact, - vendored file in path that points out to jenkins xunit-plugin. see also: http://stackoverflow.com/questions/4922867/junit-xml-format-specification-that-hudson-supports
1 parent ca8df21 commit 91ae58c

File tree

3 files changed

+159
-0
lines changed

3 files changed

+159
-0
lines changed

tests/testsuite.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,29 @@
1515
from shutil import rmtree
1616
from glob import glob
1717
from xml.dom import minidom
18+
from lxml import etree
1819
import os.path
1920

2021

22+
def _load_schema():
23+
path = os.path.join(os.path.dirname(__file__),
24+
'vendor/jenkins/xunit-plugin',
25+
'junit-10.xsd')
26+
with open(path, 'r') as schema_file:
27+
schema_doc = etree.parse(schema_file)
28+
schema = etree.XMLSchema(schema_doc)
29+
return schema
30+
raise RuntimeError('Could not load JUnit schema')
31+
32+
33+
JUnitSchema = _load_schema()
34+
35+
36+
def validate_junit_report(text):
37+
document = etree.parse(BytesIO(text))
38+
JUnitSchema.assertValid(document)
39+
40+
2141
class DoctestTest(unittest.TestCase):
2242

2343
def test_doctest_example(self):
@@ -384,6 +404,8 @@ def test_junitxml_xsd_validation_order(self):
384404
i_testcase = output.index('<testcase'.encode('utf8'))
385405
self.assertTrue(i_properties < i_testcase <
386406
i_system_out < i_system_err)
407+
# XSD validation - for good measure.
408+
validate_junit_report(output)
387409

388410
def test_junitxml_xsd_validation_empty_properties(self):
389411
suite = unittest.TestSuite()
@@ -398,6 +420,7 @@ def test_junitxml_xsd_validation_empty_properties(self):
398420
outdir.seek(0)
399421
output = outdir.read()
400422
self.assertNotIn('<properties>'.encode('utf8'), output)
423+
validate_junit_report(output)
401424

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

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ deps =
1313
coveralls
1414
djangolts: django>=1.8.8,<1.9.0
1515
djangocurr: django>=1.9.1
16+
lxml>=3.6.0
1617
commands =
1718
coverage run --append setup.py test
1819
coverage report --omit='.tox/*'

0 commit comments

Comments
 (0)