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

Commit 7313998

Browse files
committed
First commit.
1 parent 5b4d6d0 commit 7313998

File tree

13 files changed

+331
-116
lines changed

13 files changed

+331
-116
lines changed

.gitattributes

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,2 @@
11
# Auto detect text files and perform LF normalization
2-
* text=auto
3-
4-
# Custom for Visual Studio
5-
*.cs diff=csharp
6-
7-
# Standard to msysgit
8-
*.doc diff=astextplain
9-
*.DOC diff=astextplain
10-
*.docx diff=astextplain
11-
*.DOCX diff=astextplain
12-
*.dot diff=astextplain
13-
*.DOT diff=astextplain
14-
*.pdf diff=astextplain
15-
*.PDF diff=astextplain
16-
*.rtf diff=astextplain
17-
*.RTF diff=astextplain
2+
* text=auto

.gitignore

Lines changed: 4 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,5 @@
1-
# Byte-compiled / optimized / DLL files
2-
__pycache__/
3-
*.py[cod]
4-
5-
# C extensions
6-
*.so
7-
8-
# Distribution / packaging
9-
.Python
10-
env/
1+
*.egg-info
2+
.idea
113
build/
12-
develop-eggs/
13-
dist/
14-
downloads/
15-
eggs/
16-
lib/
17-
lib64/
18-
parts/
19-
sdist/
20-
var/
21-
*.egg-info/
22-
.installed.cfg
23-
*.egg
24-
25-
# PyInstaller
26-
# Usually these files are written by a python script from a template
27-
# before PyInstaller builds the exe, so as to inject date/other infos into it.
28-
*.manifest
29-
*.spec
30-
31-
# Installer logs
32-
pip-log.txt
33-
pip-delete-this-directory.txt
34-
35-
# Unit test / coverage reports
36-
htmlcov/
37-
.tox/
38-
.coverage
39-
.cache
40-
nosetests.xml
41-
coverage.xml
42-
43-
# Translations
44-
*.mo
45-
*.pot
46-
47-
# Django stuff:
48-
*.log
49-
50-
# Sphinx documentation
51-
docs/_build/
52-
53-
# PyBuilder
54-
target/
55-
56-
# =========================
57-
# Operating System Files
58-
# =========================
59-
60-
# OSX
61-
# =========================
62-
63-
.DS_Store
64-
.AppleDouble
65-
.LSOverride
66-
67-
# Thumbnails
68-
._*
69-
70-
# Files that might appear on external disk
71-
.Spotlight-V100
72-
.Trashes
73-
74-
# Directories potentially created on remote AFP share
75-
.AppleDB
76-
.AppleDesktop
77-
Network Trash Folder
78-
Temporary Items
79-
.apdisk
80-
81-
# Windows
82-
# =========================
83-
84-
# Windows image file caches
85-
Thumbs.db
86-
ehthumbs.db
87-
88-
# Folder config file
89-
Desktop.ini
90-
91-
# Recycle Bin used on file shares
92-
$RECYCLE.BIN/
93-
94-
# Windows Installer files
95-
*.cab
96-
*.msi
97-
*.msm
98-
*.msp
99-
100-
# Windows shortcuts
101-
*.lnk
4+
dist
5+
__pycache__

README.rst

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
cppcheck JUnit Converter
2+
========================
3+
4+
.. image:: https://badge.fury.io/py/cppcheck-junit.png
5+
:target: http://badge.fury.io/py/cppcheck-junit
6+
7+
Tool that converts ``cppcheck`` output to JUnit XML format. Use on your CI servers to get more
8+
helpful feedback.
9+
10+
Installation
11+
------------
12+
13+
You can install, upgrade, and uninstall ``cppcheck-junit`` with these commands:
14+
15+
.. code:: shell-session
16+
17+
$ pip install cppcheck-junit
18+
$ pip install --upgrade cppcheck-junit
19+
$ pip uninstall cppcheck-junit
20+
21+
Usage
22+
-----
23+
Enable XML version 2 output, enable all message, and redirect ``cppcheck`` ``stderr`` to a file:
24+
25+
.. code:: shell-session
26+
27+
$ cppcheck --xml-version=2 --enable=all . 2> cppcheck-result.xml
28+
29+
Convert it to JUnit XML format:
30+
31+
.. code:: shell-session
32+
33+
$ cppcheck_junit cppcheck-result.xml cppcheck-junit.xml
34+
35+
Releases
36+
--------
37+
38+
0.1.0 - 2015-11-15
39+
^^^^^^^^^^^^^^^^^^
40+
41+
First release.

cppcheck_junit.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#! /usr/bin/env python3
2+
3+
"""Converts Cppcheck XML version 2 output to JUnit XML format."""
4+
5+
import argparse
6+
import collections
7+
import os
8+
import sys
9+
from xml.etree import ElementTree
10+
11+
__version__ = '0.1.0'
12+
13+
EXIT_SUCCESS = 0
14+
EXIT_FAILURE = -1
15+
16+
17+
class CppcheckError(object):
18+
def __init__(self, file, line, message, severity, error_id, verbose):
19+
"""Constructor.
20+
21+
Args:
22+
file (str): File error originated on.
23+
line (int): Line error originated on.
24+
message (str): Error message.
25+
severity (str): Severity of the error.
26+
error_id (str): Unique identifier for the error.
27+
verbose (str): Verbose error message.
28+
"""
29+
self.file = file
30+
self.line = line
31+
self.message = message
32+
self.severity = severity
33+
self.error_id = error_id
34+
self.verbose = verbose
35+
36+
37+
def parse_arguments():
38+
parser = argparse.ArgumentParser(description='Converts Cppcheck XML version 2 to JUnit XML '
39+
'format.')
40+
parser.add_argument('input_file', type=str, help='Cppcheck XML version 2 stderr file.')
41+
parser.add_argument('output_file', type=str, help='JUnit XML output file.')
42+
return parser.parse_args()
43+
44+
45+
def parse_cppcheck(file_name):
46+
"""Parses a Cppcheck XML version 2 file.
47+
48+
Args:
49+
file_name (str): Cppcheck XML file.
50+
51+
Returns:
52+
Dict[str, List[CppcheckError]]: Parsed errors grouped by file name.
53+
54+
Raises:
55+
ValueError: If unsupported Cppcheck XML version.
56+
"""
57+
root = ElementTree.parse(file_name).getroot() # type: ElementTree.Element
58+
59+
if not int(root.get('version')) == 2:
60+
raise ValueError('Parser only supports Cppcheck XML version 2. Use --xml-version=2')
61+
62+
error_root = root.find('errors')
63+
64+
errors = collections.defaultdict(list)
65+
for error_element in error_root:
66+
location_element = error_element.find('location') # type: ElementTree.Element
67+
error = CppcheckError(file=location_element.get('file'),
68+
line=int(location_element.get('line')),
69+
message=error_element.get('msg'),
70+
severity=error_element.get('severity'),
71+
error_id=error_element.get('id'),
72+
verbose=error_element.get('verbose'))
73+
errors[error.file].append(error)
74+
75+
return errors
76+
77+
78+
def generate_test_suite(errors, output_file):
79+
"""Writes a JUnit test file from parsed Cppcheck errors.
80+
81+
Args:
82+
errors (Dict[str, List[CppcheckError]]):
83+
output_file (str): File path to create JUnit XML file.
84+
85+
Returns:
86+
Nothing.
87+
"""
88+
test_suite = ElementTree.Element('testsuite')
89+
test_suite.attrib['errors'] = str(len(errors))
90+
test_suite.attrib['failures'] = str(0)
91+
test_suite.attrib['name'] = 'Cppcheck errors'
92+
test_suite.attrib['tests'] = str(len(errors))
93+
test_suite.attrib['time'] = str(1)
94+
95+
for file_name, errors in errors.items():
96+
test_case = ElementTree.SubElement(test_suite,
97+
'testcase',
98+
name=os.path.relpath(file_name))
99+
for error in errors:
100+
ElementTree.SubElement(test_case,
101+
'error',
102+
file=os.path.relpath(error.file),
103+
line=str(error.line),
104+
message='{}: {}'.format(error.line, error.message))
105+
106+
tree = ElementTree.ElementTree(test_suite)
107+
tree.write(output_file, encoding='utf-8', xml_declaration=True)
108+
109+
110+
def main():
111+
"""Main function.
112+
113+
Returns:
114+
int: Exit code.
115+
"""
116+
args = parse_arguments()
117+
118+
try:
119+
errors = parse_cppcheck(args.input_file)
120+
except FileNotFoundError as e:
121+
print(str(e))
122+
return EXIT_FAILURE
123+
124+
if len(errors) > 0:
125+
generate_test_suite(errors, args.output_file)
126+
127+
return EXIT_SUCCESS
128+
129+
if __name__ == '__main__':
130+
sys.exit(main())

setup.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[wheel]
2+
universal = 1

setup.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from setuptools import setup
2+
3+
import cppcheck_junit
4+
5+
6+
setup(
7+
name='cppcheck-junit',
8+
version=cppcheck_junit.__version__,
9+
10+
description='Converts Cppcheck XML output to JUnit format.',
11+
long_description=open('README.rst').read(),
12+
keywords='Cppcheck C++ JUnit',
13+
14+
author='John Hagen',
15+
author_email='[email protected]',
16+
url='https://github.com/johnthagen/cppcheck-junit',
17+
license='MIT',
18+
19+
py_modules=['cppcheck_junit'],
20+
zip_safe=False,
21+
22+
classifiers=[
23+
'Development Status :: 3 - Alpha',
24+
'Environment :: Console',
25+
'Intended Audience :: Developers',
26+
'License :: OSI Approved :: MIT License',
27+
'Operating System :: OS Independent',
28+
'Programming Language :: Python',
29+
'Programming Language :: Python :: 2',
30+
'Programming Language :: Python :: 2.7',
31+
'Programming Language :: Python :: 3',
32+
'Programming Language :: Python :: 3.3',
33+
'Programming Language :: Python :: 3.4',
34+
'Programming Language :: Python :: 3.5',
35+
'Topic :: Software Development :: Libraries :: Python Modules',
36+
'Topic :: Software Development :: Quality Assurance',
37+
],
38+
39+
scripts=['cppcheck_junit.py'],
40+
41+
entry_points={
42+
'console_scripts': [
43+
'cppcheck_junit = cppcheck_junit:main',
44+
],
45+
}
46+
)

0 commit comments

Comments
 (0)