From a5fb6c6e6c419fe9da51551ddf562f0251c5414e Mon Sep 17 00:00:00 2001 From: doyou89 Date: Fri, 23 Mar 2018 14:13:13 +0900 Subject: [PATCH 1/6] use xml.etree instead of xml.dom xml.dom use too much memory, so converting big lcov file may result in memory error on memory limitated environment like jenkins. xml.etree will reduce memory usage to under 10~20% than xml.dom. But xml.etree not support prettyxml, so if you want to generate pretty xml, use xmllint. --- lcov_cobertura/lcov_cobertura.py | 75 ++++++++++++++------------------ 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/lcov_cobertura/lcov_cobertura.py b/lcov_cobertura/lcov_cobertura.py index 7aae6d1..f76db73 100755 --- a/lcov_cobertura/lcov_cobertura.py +++ b/lcov_cobertura/lcov_cobertura.py @@ -14,7 +14,7 @@ import os import time import subprocess -from xml.dom import minidom +from xml.etree import cElementTree from optparse import OptionParser from distutils.spawn import find_executable @@ -226,13 +226,8 @@ def generate_cobertura_xml(self, coverage_data): :type coverage_data: dict """ - dom_impl = minidom.getDOMImplementation() - doctype = dom_impl.createDocumentType("coverage", None, - "http://cobertura.sourceforge.net/xml/coverage-04.dtd") - document = dom_impl.createDocument(None, "coverage", doctype) - root = document.documentElement summary = coverage_data['summary'] - self._attrs(root, { + root = cElementTree.Element('coverage', { 'branch-rate': self._percent(summary['branches-total'], summary['branches-covered']), 'branches-covered': str(summary['branches-covered']), @@ -246,26 +241,23 @@ def generate_cobertura_xml(self, coverage_data): 'version': '2.0.3' }) - sources = self._el(document, 'sources', {}) - source = self._el(document, 'source', {}) - source.appendChild(document.createTextNode(self.base_dir)) - sources.appendChild(source) + sources = cElementTree.SubElement(root, 'sources') + source = cElementTree.SubElement(sources, 'source') + source.text = self.base_dir - root.appendChild(sources) - - packages_el = self._el(document, 'packages', {}) + packages_el = cElementTree.SubElement(root, 'packages') packages = coverage_data['packages'] for package_name, package_data in list(packages.items()): - package_el = self._el(document, 'package', { + package_el = cElementTree.SubElement(packages_el, 'package', { 'line-rate': package_data['line-rate'], 'branch-rate': package_data['branch-rate'], 'name': package_name, 'complexity': '0', }) - classes_el = self._el(document, 'classes', {}) + classes_el = cElementTree.SubElement(package_el, 'classes') for class_name, class_data in list(package_data['classes'].items()): - class_el = self._el(document, 'class', { + class_el = cElementTree.SubElement(classes_el, 'class', { 'branch-rate': self._percent(class_data['branches-total'], class_data['branches-covered']), 'complexity': '0', @@ -276,51 +268,48 @@ def generate_cobertura_xml(self, coverage_data): }) # Process methods - methods_el = self._el(document, 'methods', {}) + methods_el = cElementTree.SubElement(class_el, 'methods') for method_name, (line, hits) in list(class_data['methods'].items()): - method_el = self._el(document, 'method', { + method_el = cElementTree.SubElement(methods_el, 'method', { 'name': self.format(method_name), 'signature': '', 'line-rate': '1.0' if int(hits) > 0 else '0.0', 'branch-rate': '1.0' if int(hits) > 0 else '0.0', }) - method_lines_el = self._el(document, 'lines', {}) - method_line_el = self._el(document, 'line', { + method_lines_el = cElementTree.SubElement(method_el, 'lines') + cElementTree.SubElement(method_lines_el, 'line', { 'hits': hits, 'number': line, 'branch': 'false', }) - method_lines_el.appendChild(method_line_el) - method_el.appendChild(method_lines_el) - methods_el.appendChild(method_el) # Process lines - lines_el = self._el(document, 'lines', {}) + lines_el = cElementTree.SubElement(class_el, 'lines') lines = list(class_data['lines'].keys()) lines.sort() for line_number in lines: - line_el = self._el(document, 'line', { - 'branch': class_data['lines'][line_number]['branch'], - 'hits': str(class_data['lines'][line_number]['hits']), - 'number': str(line_number) - }) if class_data['lines'][line_number]['branch'] == 'true': total = int(class_data['lines'][line_number]['branches-total']) covered = int(class_data['lines'][line_number]['branches-covered']) percentage = int((covered * 100.0) / total) - line_el.setAttribute('condition-coverage', - '{0}% ({1}/{2})'.format( - percentage, covered, total)) - lines_el.appendChild(line_el) - - class_el.appendChild(methods_el) - class_el.appendChild(lines_el) - classes_el.appendChild(class_el) - package_el.appendChild(classes_el) - packages_el.appendChild(package_el) - root.appendChild(packages_el) - - return document.toprettyxml() + attr = { + 'branch': class_data['lines'][line_number]['branch'], + 'condition-coverage': '{0}% ({1}/{2})'.format(percentage, covered, total), + 'hits': str(class_data['lines'][line_number]['hits']), + 'number': str(line_number) + } + else: + attr = { + 'branch': class_data['lines'][line_number]['branch'], + 'hits': str(class_data['lines'][line_number]['hits']), + 'number': str(line_number) + } + cElementTree.SubElement(lines_el, 'line', attr) + + doctype = """""" + xml_string = cElementTree.tostring(root, encoding='UTF-8') + return xml_string.replace(' Date: Fri, 23 Mar 2018 14:40:08 +0900 Subject: [PATCH 2/6] update test case --- test/test_lcov_cobertura.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_lcov_cobertura.py b/test/test_lcov_cobertura.py index 19082ef..feb3cd0 100755 --- a/test/test_lcov_cobertura.py +++ b/test/test_lcov_cobertura.py @@ -74,7 +74,7 @@ def test_generate_cobertura_xml(self): 'lines-covered': 1, 'lines-total': 2}, 'timestamp': '1346815648000'} xml = converter.generate_cobertura_xml(parsed_lcov) - self.assertEqual(xml, '\n\n\n\t\n\t\t.\n\t\n\t\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t\n\n') + self.assertEqual(xml, ''\n\n.') def test_treat_non_integer_line_execution_count_as_zero(self): converter = LcovCobertura( From 36c089027cfdb10df4529da6d85108faa30f80a2 Mon Sep 17 00:00:00 2001 From: doyou89 Date: Fri, 23 Mar 2018 14:41:17 +0900 Subject: [PATCH 3/6] modify for test case --- lcov_cobertura/lcov_cobertura.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lcov_cobertura/lcov_cobertura.py b/lcov_cobertura/lcov_cobertura.py index f76db73..7e1b1b7 100755 --- a/lcov_cobertura/lcov_cobertura.py +++ b/lcov_cobertura/lcov_cobertura.py @@ -307,7 +307,7 @@ def generate_cobertura_xml(self, coverage_data): cElementTree.SubElement(lines_el, 'line', attr) doctype = """""" + SYSTEM "http://cobertura.sourceforge.net/xml/coverage-04.dtd">""" xml_string = cElementTree.tostring(root, encoding='UTF-8') return xml_string.replace(' Date: Sat, 24 Mar 2018 10:07:48 +0900 Subject: [PATCH 4/6] correct quote error --- test/test_lcov_cobertura.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_lcov_cobertura.py b/test/test_lcov_cobertura.py index feb3cd0..b2dace2 100755 --- a/test/test_lcov_cobertura.py +++ b/test/test_lcov_cobertura.py @@ -74,7 +74,7 @@ def test_generate_cobertura_xml(self): 'lines-covered': 1, 'lines-total': 2}, 'timestamp': '1346815648000'} xml = converter.generate_cobertura_xml(parsed_lcov) - self.assertEqual(xml, ''\n\n.') + self.assertEqual(xml, '\n\n.') def test_treat_non_integer_line_execution_count_as_zero(self): converter = LcovCobertura( From 61a91387143ecf24b0cc64901fd8c25270f7426d Mon Sep 17 00:00:00 2001 From: doyou89 Date: Sat, 24 Mar 2018 10:44:15 +0900 Subject: [PATCH 5/6] use unicode on python3 --- lcov_cobertura/lcov_cobertura.py | 43 +++++++++++++------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/lcov_cobertura/lcov_cobertura.py b/lcov_cobertura/lcov_cobertura.py index 7e1b1b7..46bfb42 100755 --- a/lcov_cobertura/lcov_cobertura.py +++ b/lcov_cobertura/lcov_cobertura.py @@ -306,37 +306,28 @@ def generate_cobertura_xml(self, coverage_data): } cElementTree.SubElement(lines_el, 'line', attr) - doctype = """""" - xml_string = cElementTree.tostring(root, encoding='UTF-8') - return xml_string.replace(' + +""" + import sys + if sys.version_info[0] == 2: + xml_string = cElementTree.tostring(document) + return header + xml_string + else: + xml_string = cElementTree.tostring(document, encoding='unicode') + return header + xml_string + def _percent(self, lines_total, lines_covered): """ Get the percentage of lines covered in the total, with formatting. From ec9644bff72a90667baf0da42bde69043f3c704a Mon Sep 17 00:00:00 2001 From: doyou89 Date: Sat, 24 Mar 2018 10:46:33 +0900 Subject: [PATCH 6/6] remove encoding --- test/test_lcov_cobertura.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_lcov_cobertura.py b/test/test_lcov_cobertura.py index b2dace2..2538d4e 100755 --- a/test/test_lcov_cobertura.py +++ b/test/test_lcov_cobertura.py @@ -74,7 +74,7 @@ def test_generate_cobertura_xml(self): 'lines-covered': 1, 'lines-total': 2}, 'timestamp': '1346815648000'} xml = converter.generate_cobertura_xml(parsed_lcov) - self.assertEqual(xml, '\n\n.') + self.assertEqual(xml, '\n\n.') def test_treat_non_integer_line_execution_count_as_zero(self): converter = LcovCobertura(