Files
dokku-redis/tests/shellcheck-to-junit
Jose Diaz-Gonzalez 744e65760b feat: Real docker-based testing
This pull request switches testing to use an actual docker daemon, vs mocking everything out.

It may also catch actual breaking issues in our tests, which is great!
2019-03-18 14:44:28 -04:00

206 lines
7.0 KiB
Python
Executable File

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
import argparse
import collections
import datetime
import re
import socket
import sys
from xml.etree import ElementTree
def CDATA(text=None):
element = ElementTree.Element('![CDATA[')
element.text = text
return element
def _serialize_xml(write, elem, qnames, namespaces,short_empty_elements, **kwargs):
if elem.tag == '![CDATA[':
write("\n<{}{}]]>\n".format(elem.tag, elem.text))
if elem.tail:
write(ElementTree._escape_cdata(elem.tail))
else:
return ElementTree._original_serialize_xml(write, elem, qnames, namespaces,short_empty_elements, **kwargs)
ElementTree._original_serialize_xml = ElementTree._serialize_xml
ElementTree._serialize_xml = ElementTree._serialize['xml'] = _serialize_xml
def read_in():
lines = sys.stdin.readlines()
for i in range(len(lines)):
lines[i] = lines[i].rstrip()
return lines
def process_lines(lines):
files = {}
current_file = None
previous_line = None
line_no = None
new_issues = []
code = None
RE_VIOLATION = re.compile(r"\^-- (SC[\w]+): (.*)")
RE_VIOLATION_NEW = re.compile(r"\^[-]+\^ (SC[\w]+): (.*)")
for line in lines:
# start a new block
if line == '':
if current_file is not None:
file_data = files.get(current_file, {})
files[current_file] = file_data
issue_data = file_data.get(line_no, {})
issue_data['code'] = code
files[current_file][line_no] = issue_data
issues = issue_data.get('issues', [])
issues.extend(new_issues)
issue_data['issues'] = issues
files[current_file][line_no] = issue_data
code = None
current_file = None
line_no = None
elif line.startswith('In ./') and not previous_line:
current_file = line.split(' ')[1].replace('./', '')
line_no = line.split(' ')[3]
new_issues = []
code = None
elif code is None and len(new_issues) == 0:
code = line
else:
match = RE_VIOLATION.match(line.strip())
if not match:
match = RE_VIOLATION_NEW.match(line.strip())
if not match:
if 'https://www.shellcheck.net/wiki/SC' in line:
continue
if 'For more information:' == line:
continue
print('Error: Issue parsing line "{0}"'.format(line.strip()))
else:
new_issues.append({
'shellcheck_id': match.group(1),
'message': match.group(2),
'original_message': line
})
previous_line = line
return files
def output_junit(files, args):
timestamp = datetime.datetime.now().replace(microsecond=0).isoformat()
failures = 0
for file, data in files.items():
for line, issue_data in data.items():
code = issue_data.get('code')
for issue in issue_data.get('issues', []):
failures += 1
tests = 0
if args.files:
with open(args.files, 'r') as f:
tests = len(f.readlines())
root = ElementTree.Element("testsuite",
name="shellcheck",
tests="{0}".format(tests),
failures="{0}".format(failures),
errors="0",
skipped="0",
timestamp=timestamp,
time="0",
hostname=socket.gethostname())
properties = ElementTree.SubElement(root, "properties")
if args.exclude:
ElementTree.SubElement(properties,
"property",
name="exclude",
value=args.exclude)
if args.files:
with open(args.files, 'r') as f:
lines = f.readlines()
for i in range(len(lines)):
file = lines[i].rstrip().replace('./', '')
data = files.get(file, None)
if data:
for line, issue_data in data.items():
code = issue_data.get('code')
for issue in issue_data.get('issues', []):
testcase = ElementTree.SubElement(root,
"testcase",
classname=file,
name=file,
time="0")
shellcheck_id = issue.get('shellcheck_id')
message = 'line {0}: {1}'.format(
line, issue.get('message'))
original_message = issue.get('original_message')
e = ElementTree.Element("failure",
type=shellcheck_id,
message=message)
cdata = CDATA("\n".join([code, original_message]))
e.append(cdata)
testcase.append(e)
ElementTree.SubElement(root,
"testcase",
classname=file,
name=file,
time="0")
ElementTree.SubElement(root, "system-out")
ElementTree.SubElement(root, "system-err")
content = ElementTree.tostring(root, encoding='UTF-8', method='xml')
if args.output:
with open(args.output, 'w') as f:
try:
f.write(content)
except TypeError:
f.write(content.decode("utf-8"))
def main():
parser = argparse.ArgumentParser(
description='Process shellcheck output to junit.')
parser.add_argument('--output',
dest='output',
action='store',
default=None,
help='file to write shellcheck output')
parser.add_argument('--files',
dest='files',
action='store',
default=None,
help='a file containing a list of all files processed by shellcheck')
parser.add_argument('--exclude',
dest='exclude',
action='store',
default=None,
help='a comma-separated list of rules being excluded by shellcheck')
args = parser.parse_args()
lines = read_in()
files = process_lines(lines)
files = collections.OrderedDict(sorted(files.items()))
output_junit(files, args)
for line in lines:
print(line)
if __name__ == '__main__':
main()