Skip to content
This repository was archived by the owner on Jan 1, 2026. It is now read-only.

Commit e33a07a

Browse files
committed
Brings generated JUnit XML inline with spec
Added code to test cases to check that all required attributes for the testsuite, testcase, and error tags are present in the XML generated by this module. Also had to add some new attributes to make new unittests pass.
1 parent ef9dd3d commit e33a07a

File tree

2 files changed

+71
-25
lines changed

2 files changed

+71
-25
lines changed

cppcheck_junit.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,13 @@ def generate_test_suite(errors):
123123
for file_name, errors in errors.items():
124124
test_case = ElementTree.SubElement(test_suite,
125125
'testcase',
126-
name=os.path.relpath(file_name) if file_name else '')
126+
name=os.path.relpath(file_name) if file_name else '',
127+
classname='',
128+
time=str(1))
127129
for error in errors:
128130
ElementTree.SubElement(test_case,
129131
'error',
132+
type='',
130133
file=os.path.relpath(error.file) if error.file else '',
131134
line=str(error.line),
132135
message='{}: ({}) {}'.format(error.line,
@@ -149,7 +152,9 @@ def generate_single_success_test_suite():
149152
test_suite.attrib['time'] = str(1)
150153
ElementTree.SubElement(test_suite,
151154
'testcase',
152-
name='Cppcheck success')
155+
name='Cppcheck success',
156+
classname='',
157+
time=str(1))
153158
return ElementTree.ElementTree(test_suite)
154159

155160

test.py

Lines changed: 64 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ def test_malformed(self): # type: () -> None
102102

103103

104104
class GenerateTestSuiteTestCase(unittest.TestCase):
105+
# Expected attributes from JUnit XSD
106+
# ref: https://raw.githubusercontent.com/windyroad/JUnit-Schema/master/JUnit.xsd
107+
# @TODO: Better thing to do here would be to curl or otherwise access the
108+
# spec above instead of hardcoding pieces of it here
109+
junit_testsuite_attributes = ['name', 'timestamp', 'hostname', 'tests', 'failures', 'errors', 'time']
110+
junit_testcase_attributes = ['name', 'classname', 'time']
111+
junit_error_attributes = ['type']
112+
105113
def test_single(self): # type: () -> None
106114
errors = {'file_name':
107115
[CppcheckError('file_name',
@@ -111,18 +119,27 @@ def test_single(self): # type: () -> None
111119
'error_id',
112120
'verbose error message')]}
113121
tree = generate_test_suite(errors)
114-
root = tree.getroot()
115-
self.assertEqual(root.get('errors'), str(1))
116-
self.assertEqual(root.get('failures'), str(0))
117-
self.assertEqual(root.get('tests'), str(1))
118-
119-
test_case_element = root.find('testcase')
120-
self.assertEqual(test_case_element.get('name'), 'file_name')
121-
122-
error_element = test_case_element.find('error')
122+
testsuite_element = tree.getroot()
123+
self.assertEqual(testsuite_element.get('errors'), str(1))
124+
self.assertEqual(testsuite_element.get('failures'), str(0))
125+
self.assertEqual(testsuite_element.get('tests'), str(1))
126+
# Check that testsuite element is compliant with the spec
127+
for required_attribute in self.junit_testsuite_attributes:
128+
self.assertTrue(required_attribute in testsuite_element.attrib.keys())
129+
130+
testcase_element = testsuite_element.find('testcase')
131+
self.assertEqual(testcase_element.get('name'), 'file_name')
132+
# Check that test_case is compliant with the spec
133+
for required_attribute in self.junit_testcase_attributes:
134+
self.assertTrue(required_attribute in testcase_element.attrib.keys())
135+
136+
error_element = testcase_element.find('error')
123137
self.assertEqual(error_element.get('file'), 'file_name')
124138
self.assertEqual(error_element.get('line'), str(4))
125139
self.assertEqual(error_element.get('message'), '4: (severity) error message')
140+
# Check that error element is compliant with the spec
141+
for required_attribute in self.junit_error_attributes:
142+
self.assertTrue(required_attribute in error_element.attrib.keys())
126143

127144
def test_missing_file(self): # type: () -> None
128145
errors = {'':
@@ -141,32 +158,56 @@ def test_missing_file(self): # type: () -> None
141158
'that may increase the checking time. For more details, '
142159
'use --enable=information.')]}
143160
tree = generate_test_suite(errors)
144-
root = tree.getroot()
145-
self.assertEqual(root.get('errors'), str(1))
146-
self.assertEqual(root.get('failures'), str(0))
147-
self.assertEqual(root.get('tests'), str(1))
148-
149-
test_case_element = root.find('testcase')
150-
self.assertEqual(test_case_element.get('name'), '')
151-
152-
error_element = test_case_element.find('error')
161+
testsuite_element = tree.getroot()
162+
self.assertEqual(testsuite_element.get('errors'), str(1))
163+
self.assertEqual(testsuite_element.get('failures'), str(0))
164+
self.assertEqual(testsuite_element.get('tests'), str(1))
165+
# Check that testsuite element is compliant with the spec
166+
for required_attribute in self.junit_testsuite_attributes:
167+
self.assertTrue(required_attribute in testsuite_element.attrib.keys())
168+
169+
testcase_element = testsuite_element.find('testcase')
170+
self.assertEqual(testcase_element.get('name'), '')
171+
# Check that test_case is compliant with the spec
172+
for required_attribute in self.junit_testcase_attributes:
173+
self.assertTrue(required_attribute in testcase_element.attrib.keys())
174+
175+
error_element = testcase_element.find('error')
153176
self.assertEqual(error_element.get('file'), '')
154177
self.assertEqual(error_element.get('line'), str(0))
155178
self.assertEqual(error_element.get('message'),
156179
'0: (information) Too many #ifdef configurations - cppcheck only checks '
157180
'12 configurations. Use --force to check all '
158181
'configurations. For more details, use '
159182
'--enable=information.')
183+
# Check that error element is compliant with the spec
184+
for required_attribute in self.junit_error_attributes:
185+
self.assertTrue(required_attribute in error_element.attrib.keys())
160186

161187

162188
class GenerateSingleSuccessTestSuite(unittest.TestCase):
189+
# Expected attributes from JUnit XSD
190+
# ref: https://raw.githubusercontent.com/windyroad/JUnit-Schema/master/JUnit.xsd
191+
# @TODO: Better thing to do here would be to curl or otherwise access the
192+
# spec above instead of hardcoding pieces of it here
193+
junit_testsuite_attributes = ['name', 'timestamp', 'hostname', 'tests', 'failures', 'errors', 'time']
194+
junit_testcase_attributes = ['name', 'classname', 'time']
195+
163196
def test(self): # type: () -> None
164197
tree = generate_single_success_test_suite()
165-
root = tree.getroot()
166-
self.assertEqual(root.get('tests'), str(1))
167-
168-
test_case_element = root.find('testcase')
169-
self.assertEqual(test_case_element.get('name'), 'Cppcheck success')
198+
testsuite_element = tree.getroot()
199+
self.assertEqual(testsuite_element.get('tests'), str(1))
200+
self.assertEqual(testsuite_element.get('errors'), str(0))
201+
self.assertEqual(testsuite_element.get('failures'), str(0))
202+
# Check that testsuite element is compliant with the spec
203+
for required_attribute in self.junit_testsuite_attributes:
204+
self.assertTrue(required_attribute in testsuite_element.attrib.keys())
205+
206+
testcase_element = testsuite_element.find('testcase')
207+
self.assertEqual(testcase_element.get('name'), 'Cppcheck success')
208+
# Check that test_case is compliant with the spec
209+
for required_attribute in self.junit_testcase_attributes:
210+
self.assertTrue(required_attribute in testcase_element.attrib.keys())
170211

171212

172213
class ParseArgumentsTestCase(unittest.TestCase):

0 commit comments

Comments
 (0)