Created
September 18, 2014 21:01
-
-
Save unsetbit/7d5d5b9cbc4e53514fef to your computer and use it in GitHub Desktop.
Cover 2 cover
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import sys | |
import xml.etree.ElementTree as ET | |
import re | |
import os.path | |
# from https://github.com/rix0rrr/cover2cover | |
# branch-rate="0.0" complexity="0.0" line-rate="1.0" | |
# branch="true" hits="1" number="86" | |
def find_lines(j_package, filename): | |
"""Return all <line> elements for a given source file in a package.""" | |
xpath = "sourcefile[@name='" + os.path.basename(filename) + "']/line" | |
return list(j_package.findall(xpath)) | |
def method_lines(jmethod, jmethods, jlines): | |
"""Filter the lines from the given set of jlines that apply to the given jmethod.""" | |
start_line = int(jmethod.attrib['line']) | |
larger = list(int(jm.attrib['line']) for jm in jmethods if int(jm.attrib['line']) > start_line) | |
end_line = min(larger) if len(larger) else 99999999 | |
for jline in jlines: | |
if start_line <= int(jline.attrib['nr']) < end_line: | |
yield jline | |
def convert_lines(j_lines, into): | |
"""Convert the JaCoCo <line> elements into Cobertura <line> elements, add them under the given element.""" | |
c_lines = ET.SubElement(into, 'lines') | |
for jline in j_lines: | |
mb = int(jline.attrib['mb']) | |
cb = int(jline.attrib['cb']) | |
ci = int(jline.attrib['ci']) | |
cline = ET.SubElement(c_lines, 'line') | |
cline.set('number', jline.attrib['nr']) | |
cline.set('hits', '1' if ci > 0 else '0') # Probably not true but no way to know from JaCoCo XML file | |
if mb + cb > 0: | |
percentage = str(int(100 * (float(cb) / (float(cb) + float(mb))))) + '%' | |
cline.set('branch', 'true') | |
cline.set('condition-coverage', percentage + ' (' + str(cb) + '/' + str(cb + mb) + ')') | |
cond = ET.SubElement(ET.SubElement(cline, 'conditions'), 'condition') | |
cond.set('number', '0') | |
cond.set('type', 'jump') | |
cond.set('coverage', percentage) | |
else: | |
cline.set('branch', 'false') | |
def guess_filename(path_to_class): | |
m = re.match('([^$]*)', path_to_class) | |
return (m.group(1) if m else path_to_class) + '.java' | |
def add_counters(source, target): | |
target.set('line-rate', counter(source, 'LINE')) | |
target.set('branch-rate', counter(source, 'BRANCH')) | |
target.set('complexity', counter(source, 'COMPLEXITY', sum)) | |
def fraction(covered, missed): | |
return covered / (covered + missed) | |
def sum(covered, missed): | |
return covered + missed | |
def counter(source, type, operation=fraction): | |
c = source.find('counter[@type="' + type + '"]') | |
if c is not None: | |
covered = float(c.attrib['covered']) | |
missed = float(c.attrib['missed']) | |
return str(operation(covered, missed)) | |
else: | |
return '0.0' | |
def convert_method(j_method, j_lines): | |
c_method = ET.Element('method') | |
c_method.set('name', j_method.attrib['name']) | |
c_method.set('signature', j_method.attrib['desc']) | |
add_counters(j_method, c_method) | |
convert_lines(j_lines, c_method) | |
return c_method | |
def convert_class(j_class, j_package): | |
c_class = ET.Element('class') | |
c_class.set('name', j_class.attrib['name'].replace('/', '.')) | |
c_class.set('filename', guess_filename(j_class.attrib['name'])) | |
all_j_lines = list(find_lines(j_package, c_class.attrib['filename'])) | |
c_methods = ET.SubElement(c_class, 'methods') | |
all_j_methods = list(j_class.findall('method')) | |
for j_method in all_j_methods: | |
j_method_lines = method_lines(j_method, all_j_methods, all_j_lines) | |
c_methods.append(convert_method(j_method, j_method_lines)) | |
add_counters(j_class, c_class) | |
convert_lines(all_j_lines, c_class) | |
return c_class | |
def convert_package(j_package): | |
c_package = ET.Element('package') | |
c_package.attrib['name'] = j_package.attrib['name'].replace('/', '.') | |
c_classes = ET.SubElement(c_package, 'classes') | |
for j_class in j_package.findall('class'): | |
c_classes.append(convert_class(j_class, j_package)) | |
add_counters(j_package, c_package) | |
return c_package | |
def convert_root(source, target, source_root): | |
target.set('timestamp', str(int(source.find('sessioninfo').attrib['start']) / 1000)) | |
sources = ET.SubElement(target, 'sources') | |
ET.SubElement(sources, 'source').text = source_root | |
packages = ET.SubElement(target, 'packages') | |
for package in source.findall('package'): | |
packages.append(convert_package(package)) | |
add_counters(source, target) | |
def jacoco2cobertura(filename, source_root): | |
if filename == '-': | |
root = ET.fromstring(sys.stdin.read()) | |
else: | |
tree = ET.parse(filename) | |
root = tree.getroot() | |
into = ET.Element('coverage') | |
convert_root(root, into, source_root) | |
print '<?xml version="1.0" ?>' | |
print ET.tostring(into) | |
if __name__ == '__main__': | |
if len(sys.argv) < 2: | |
print "Usage: cover2cover.py FILENAME [SOURCE_ROOT]" | |
sys.exit(1) | |
filename = sys.argv[1] | |
source_root = sys.argv[2] if 2 < len(sys.argv) else '.' | |
jacoco2cobertura(filename, source_root) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment