diff -Nru python-junit-xml-1.8/debian/changelog python-junit-xml-1.9/debian/changelog --- python-junit-xml-1.8/debian/changelog 2019-08-28 02:31:13.000000000 +0000 +++ python-junit-xml-1.9/debian/changelog 2020-07-13 13:42:16.000000000 +0000 @@ -1,3 +1,23 @@ +python-junit-xml (1.9-1) unstable; urgency=medium + + * Team upload + + [ Ondřej Nový ] + * Bump Standards-Version to 4.4.1. + + [ Antonio Terceiro ] + * New upstream version 1.9 + - Upstream did not publish a source release to pypi. The orig tarball + used is taken from their git repository at ba89b41638df8ad2011c + - Tests now pass under python 3.8 (Closes: #954517) + * Bump debhelper-compat to 13 + * Bump Standards-Version to 4.5.0; no changes needed + * debian/control: run tests with pytest. Upstream changed setup.py in a way + that relying on setup.py test no longer works. + * Add debian/tests/control to run testsuite under autopkgtest + + -- Antonio Terceiro Mon, 13 Jul 2020 10:42:16 -0300 + python-junit-xml (1.8-2) unstable; urgency=medium [ Ondřej Nový ] diff -Nru python-junit-xml-1.8/debian/control python-junit-xml-1.9/debian/control --- python-junit-xml-1.8/debian/control 2019-08-28 02:31:13.000000000 +0000 +++ python-junit-xml-1.9/debian/control 2020-07-13 13:42:16.000000000 +0000 @@ -3,8 +3,8 @@ Priority: optional Maintainer: Sandro Tosi Uploaders: Debian Python Modules Team -Build-Depends: debhelper-compat (= 9), python3, dh-python, python3-setuptools, python3-six -Standards-Version: 4.4.0 +Build-Depends: debhelper-compat (= 13), python3, dh-python, python3-setuptools, python3-six, python3-pytest +Standards-Version: 4.5.0 Homepage: https://github.com/kyrus/python-junit-xml Vcs-Git: https://salsa.debian.org/python-team/modules/python-junit-xml.git Vcs-Browser: https://salsa.debian.org/python-team/modules/python-junit-xml diff -Nru python-junit-xml-1.8/debian/rules python-junit-xml-1.9/debian/rules --- python-junit-xml-1.8/debian/rules 2019-08-28 02:31:13.000000000 +0000 +++ python-junit-xml-1.9/debian/rules 2020-07-13 13:42:16.000000000 +0000 @@ -3,6 +3,8 @@ %: dh $@ --with python3 --buildsystem=pybuild +export PYBUILD_TEST_PYTEST = 1 + override_dh_auto_install: python3 setup.py install --install-layout=deb --root=$(CURDIR)/debian/python3-junit.xml diff -Nru python-junit-xml-1.8/debian/tests/control python-junit-xml-1.9/debian/tests/control --- python-junit-xml-1.8/debian/tests/control 1970-01-01 00:00:00.000000000 +0000 +++ python-junit-xml-1.9/debian/tests/control 2020-07-13 13:42:16.000000000 +0000 @@ -0,0 +1,4 @@ +Test-Command: cp -r tests/ $AUTOPKGTEST_TMP + && cd $AUTOPKGTEST_TMP + && python3 -m pytest +Depends: @, python3-pytest diff -Nru python-junit-xml-1.8/.gitignore python-junit-xml-1.9/.gitignore --- python-junit-xml-1.8/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ python-junit-xml-1.9/.gitignore 2020-02-22 20:32:08.000000000 +0000 @@ -0,0 +1,41 @@ +*.py[cod] + +# C extensions +*.so + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +venv +sdist +develop-eggs +.installed.cfg +lib +lib64 + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox +nosetests.xml +coverage.xml +htmlcov/ + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# Pycharm +.idea/** diff -Nru python-junit-xml-1.8/junit_xml/__init__.py python-junit-xml-1.9/junit_xml/__init__.py --- python-junit-xml-1.8/junit_xml/__init__.py 2017-05-22 15:26:44.000000000 +0000 +++ python-junit-xml-1.9/junit_xml/__init__.py 2020-02-22 20:32:08.000000000 +0000 @@ -1,5 +1,6 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- +import warnings from collections import defaultdict import sys import re @@ -59,15 +60,15 @@ If not already unicode, decode it. """ if PY2: - if isinstance(var, unicode): + if isinstance(var, unicode): # noqa: F821 ret = var elif isinstance(var, str): if encoding: ret = var.decode(encoding) else: - ret = unicode(var) + ret = unicode(var) # noqa: F821 else: - ret = unicode(var) + ret = unicode(var) # noqa: F821 else: ret = str(var) return ret @@ -79,16 +80,28 @@ Can handle unicode strings or binary strings if their encoding is provided. """ - def __init__(self, name, test_cases=None, hostname=None, id=None, - package=None, timestamp=None, properties=None, file=None, - log=None, url=None, stdout=None, stderr=None): + def __init__( + self, + name, + test_cases=None, + hostname=None, + id=None, + package=None, + timestamp=None, + properties=None, + file=None, + log=None, + url=None, + stdout=None, + stderr=None, + ): self.name = name if not test_cases: test_cases = [] try: iter(test_cases) except TypeError: - raise Exception('test_cases must be a list of test cases') + raise TypeError("test_cases must be a list of test cases") self.test_cases = test_cases self.timestamp = timestamp self.hostname = hostname @@ -111,36 +124,30 @@ # build the test suite element test_suite_attributes = dict() - test_suite_attributes['name'] = decode(self.name, encoding) if any(c.assertions for c in self.test_cases): - test_suite_attributes['assertions'] = \ - str(sum([int(c.assertions) for c in self.test_cases if c.assertions])) - test_suite_attributes['disabled'] = \ - str(len([c for c in self.test_cases if not c.is_enabled])) - test_suite_attributes['failures'] = \ - str(len([c for c in self.test_cases if c.is_failure()])) - test_suite_attributes['errors'] = \ - str(len([c for c in self.test_cases if c.is_error()])) - test_suite_attributes['skipped'] = \ - str(len([c for c in self.test_cases if c.is_skipped()])) - test_suite_attributes['time'] = \ - str(sum(c.elapsed_sec for c in self.test_cases if c.elapsed_sec)) - test_suite_attributes['tests'] = str(len(self.test_cases)) + test_suite_attributes["assertions"] = str(sum([int(c.assertions) for c in self.test_cases if c.assertions])) + test_suite_attributes["disabled"] = str(len([c for c in self.test_cases if not c.is_enabled])) + test_suite_attributes["errors"] = str(len([c for c in self.test_cases if c.is_error()])) + test_suite_attributes["failures"] = str(len([c for c in self.test_cases if c.is_failure()])) + test_suite_attributes["name"] = decode(self.name, encoding) + test_suite_attributes["skipped"] = str(len([c for c in self.test_cases if c.is_skipped()])) + test_suite_attributes["tests"] = str(len(self.test_cases)) + test_suite_attributes["time"] = str(sum(c.elapsed_sec for c in self.test_cases if c.elapsed_sec)) if self.hostname: - test_suite_attributes['hostname'] = decode(self.hostname, encoding) + test_suite_attributes["hostname"] = decode(self.hostname, encoding) if self.id: - test_suite_attributes['id'] = decode(self.id, encoding) + test_suite_attributes["id"] = decode(self.id, encoding) if self.package: - test_suite_attributes['package'] = decode(self.package, encoding) + test_suite_attributes["package"] = decode(self.package, encoding) if self.timestamp: - test_suite_attributes['timestamp'] = decode(self.timestamp, encoding) - if self.timestamp: - test_suite_attributes['file'] = decode(self.file, encoding) - if self.timestamp: - test_suite_attributes['log'] = decode(self.log, encoding) - if self.timestamp: - test_suite_attributes['url'] = decode(self.url, encoding) + test_suite_attributes["timestamp"] = decode(self.timestamp, encoding) + if self.file: + test_suite_attributes["file"] = decode(self.file, encoding) + if self.log: + test_suite_attributes["log"] = decode(self.log, encoding) + if self.url: + test_suite_attributes["url"] = decode(self.url, encoding) xml_element = ET.Element("testsuite", test_suite_attributes) @@ -148,7 +155,7 @@ if self.properties: props_element = ET.SubElement(xml_element, "properties") for k, v in self.properties.items(): - attrs = {'name': decode(k, encoding), 'value': decode(v, encoding)} + attrs = {"name": decode(k, encoding), "value": decode(v, encoding)} ET.SubElement(props_element, "property", attrs) # add test suite stdout @@ -164,64 +171,65 @@ # test cases for case in self.test_cases: test_case_attributes = dict() - test_case_attributes['name'] = decode(case.name, encoding) + test_case_attributes["name"] = decode(case.name, encoding) if case.assertions: # Number of assertions in the test case - test_case_attributes['assertions'] = "%d" % case.assertions + test_case_attributes["assertions"] = "%d" % case.assertions if case.elapsed_sec: - test_case_attributes['time'] = "%f" % case.elapsed_sec + test_case_attributes["time"] = "%f" % case.elapsed_sec if case.timestamp: - test_case_attributes['timestamp'] = decode(case.timestamp, encoding) + test_case_attributes["timestamp"] = decode(case.timestamp, encoding) if case.classname: - test_case_attributes['classname'] = decode(case.classname, encoding) + test_case_attributes["classname"] = decode(case.classname, encoding) if case.status: - test_case_attributes['status'] = decode(case.status, encoding) + test_case_attributes["status"] = decode(case.status, encoding) if case.category: - test_case_attributes['class'] = decode(case.category, encoding) + test_case_attributes["class"] = decode(case.category, encoding) if case.file: - test_case_attributes['file'] = decode(case.file, encoding) + test_case_attributes["file"] = decode(case.file, encoding) if case.line: - test_case_attributes['line'] = decode(case.line, encoding) + test_case_attributes["line"] = decode(case.line, encoding) if case.log: - test_case_attributes['log'] = decode(case.log, encoding) + test_case_attributes["log"] = decode(case.log, encoding) if case.url: - test_case_attributes['url'] = decode(case.url, encoding) + test_case_attributes["url"] = decode(case.url, encoding) - test_case_element = ET.SubElement( - xml_element, "testcase", test_case_attributes) + test_case_element = ET.SubElement(xml_element, "testcase", test_case_attributes) # failures - if case.is_failure(): - attrs = {'type': 'failure'} - if case.failure_message: - attrs['message'] = decode(case.failure_message, encoding) - if case.failure_type: - attrs['type'] = decode(case.failure_type, encoding) - failure_element = ET.Element("failure", attrs) - if case.failure_output: - failure_element.text = decode(case.failure_output, encoding) - test_case_element.append(failure_element) + for failure in case.failures: + if failure["output"] or failure["message"]: + attrs = {"type": "failure"} + if failure["message"]: + attrs["message"] = decode(failure["message"], encoding) + if failure["type"]: + attrs["type"] = decode(failure["type"], encoding) + failure_element = ET.Element("failure", attrs) + if failure["output"]: + failure_element.text = decode(failure["output"], encoding) + test_case_element.append(failure_element) # errors - if case.is_error(): - attrs = {'type': 'error'} - if case.error_message: - attrs['message'] = decode(case.error_message, encoding) - if case.error_type: - attrs['type'] = decode(case.error_type, encoding) - error_element = ET.Element("error", attrs) - if case.error_output: - error_element.text = decode(case.error_output, encoding) - test_case_element.append(error_element) + for error in case.errors: + if error["message"] or error["output"]: + attrs = {"type": "error"} + if error["message"]: + attrs["message"] = decode(error["message"], encoding) + if error["type"]: + attrs["type"] = decode(error["type"], encoding) + error_element = ET.Element("error", attrs) + if error["output"]: + error_element.text = decode(error["output"], encoding) + test_case_element.append(error_element) # skippeds - if case.is_skipped(): - attrs = {'type': 'skipped'} - if case.skipped_message: - attrs['message'] = decode(case.skipped_message, encoding) + for skipped in case.skipped: + attrs = {"type": "skipped"} + if skipped["message"]: + attrs["message"] = decode(skipped["message"], encoding) skipped_element = ET.Element("skipped", attrs) - if case.skipped_output: - skipped_element.text = decode(case.skipped_output, encoding) + if skipped["output"]: + skipped_element.text = decode(skipped["output"], encoding) test_case_element.append(skipped_element) # test stdout @@ -245,84 +253,134 @@ @param encoding: The encoding of the input. @return: unicode string """ - - try: - iter(test_suites) - except TypeError: - raise Exception('test_suites must be a list of test suites') - - xml_element = ET.Element("testsuites") - attributes = defaultdict(int) - for ts in test_suites: - ts_xml = ts.build_xml_doc(encoding=encoding) - for key in ['failures', 'errors', 'tests', 'disabled']: - attributes[key] += int(ts_xml.get(key, 0)) - for key in ['time']: - attributes[key] += float(ts_xml.get(key, 0)) - xml_element.append(ts_xml) - for key, value in iteritems(attributes): - xml_element.set(key, str(value)) - - xml_string = ET.tostring(xml_element, encoding=encoding) - # is encoded now - xml_string = TestSuite._clean_illegal_xml_chars( - xml_string.decode(encoding or 'utf-8')) - # is unicode now - - if prettyprint: - # minidom.parseString() works just on correctly encoded binary strings - xml_string = xml_string.encode(encoding or 'utf-8') - xml_string = xml.dom.minidom.parseString(xml_string) - # toprettyxml() produces unicode if no encoding is being passed or binary string with an encoding - xml_string = xml_string.toprettyxml(encoding=encoding) - if encoding: - xml_string = xml_string.decode(encoding) - # is unicode now - return xml_string + warnings.warn( + "Testsuite.to_xml_string is deprecated. It will be removed in version 2.0.0. " + "Use function to_xml_report_string", + DeprecationWarning, + ) + return to_xml_report_string(test_suites, prettyprint, encoding) @staticmethod def to_file(file_descriptor, test_suites, prettyprint=True, encoding=None): """ Writes the JUnit XML document to a file. """ - xml_string = TestSuite.to_xml_string( - test_suites, prettyprint=prettyprint, encoding=encoding) - # has problems with encoded str with non-ASCII (non-default-encoding) characters! - file_descriptor.write(xml_string) + warnings.warn( + "Testsuite.to_file is deprecated. It will be removed in version 2.0.0. Use function to_xml_report_file", + DeprecationWarning, + ) + to_xml_report_file(file_descriptor, test_suites, prettyprint, encoding) - @staticmethod - def _clean_illegal_xml_chars(string_to_clean): - """ - Removes any illegal unicode characters from the given XML string. - @see: http://stackoverflow.com/questions/1707890/fast-way-to-filter-illegal-xml-unicode-chars-in-python - """ +def to_xml_report_string(test_suites, prettyprint=True, encoding=None): + """ + Returns the string representation of the JUnit XML document. + @param encoding: The encoding of the input. + @return: unicode string + """ + + try: + iter(test_suites) + except TypeError: + raise TypeError("test_suites must be a list of test suites") + + xml_element = ET.Element("testsuites") + attributes = defaultdict(int) + for ts in test_suites: + ts_xml = ts.build_xml_doc(encoding=encoding) + for key in ["disabled", "errors", "failures", "tests"]: + attributes[key] += int(ts_xml.get(key, 0)) + for key in ["time"]: + attributes[key] += float(ts_xml.get(key, 0)) + xml_element.append(ts_xml) + for key, value in iteritems(attributes): + xml_element.set(key, str(value)) + + xml_string = ET.tostring(xml_element, encoding=encoding) + # is encoded now + xml_string = _clean_illegal_xml_chars(xml_string.decode(encoding or "utf-8")) + # is unicode now + + if prettyprint: + # minidom.parseString() works just on correctly encoded binary strings + xml_string = xml_string.encode(encoding or "utf-8") + xml_string = xml.dom.minidom.parseString(xml_string) + # toprettyxml() produces unicode if no encoding is being passed or binary string with an encoding + xml_string = xml_string.toprettyxml(encoding=encoding) + if encoding: + xml_string = xml_string.decode(encoding) + # is unicode now + return xml_string + + +def to_xml_report_file(file_descriptor, test_suites, prettyprint=True, encoding=None): + """ + Writes the JUnit XML document to a file. + """ + xml_string = to_xml_report_string(test_suites, prettyprint=prettyprint, encoding=encoding) + # has problems with encoded str with non-ASCII (non-default-encoding) characters! + file_descriptor.write(xml_string) - illegal_unichrs = [ - (0x00, 0x08), (0x0B, 0x1F), (0x7F, 0x84), (0x86, 0x9F), - (0xD800, 0xDFFF), (0xFDD0, 0xFDDF), (0xFFFE, 0xFFFF), - (0x1FFFE, 0x1FFFF), (0x2FFFE, 0x2FFFF), (0x3FFFE, 0x3FFFF), - (0x4FFFE, 0x4FFFF), (0x5FFFE, 0x5FFFF), (0x6FFFE, 0x6FFFF), - (0x7FFFE, 0x7FFFF), (0x8FFFE, 0x8FFFF), (0x9FFFE, 0x9FFFF), - (0xAFFFE, 0xAFFFF), (0xBFFFE, 0xBFFFF), (0xCFFFE, 0xCFFFF), - (0xDFFFE, 0xDFFFF), (0xEFFFE, 0xEFFFF), (0xFFFFE, 0xFFFFF), - (0x10FFFE, 0x10FFFF)] - - illegal_ranges = ["%s-%s" % (unichr(low), unichr(high)) - for (low, high) in illegal_unichrs - if low < sys.maxunicode] - illegal_xml_re = re.compile(u('[%s]') % u('').join(illegal_ranges)) - return illegal_xml_re.sub('', string_to_clean) +def _clean_illegal_xml_chars(string_to_clean): + """ + Removes any illegal unicode characters from the given XML string. + + @see: http://stackoverflow.com/questions/1707890/fast-way-to-filter-illegal-xml-unicode-chars-in-python + """ + + illegal_unichrs = [ + (0x00, 0x08), + (0x0B, 0x1F), + (0x7F, 0x84), + (0x86, 0x9F), + (0xD800, 0xDFFF), + (0xFDD0, 0xFDDF), + (0xFFFE, 0xFFFF), + (0x1FFFE, 0x1FFFF), + (0x2FFFE, 0x2FFFF), + (0x3FFFE, 0x3FFFF), + (0x4FFFE, 0x4FFFF), + (0x5FFFE, 0x5FFFF), + (0x6FFFE, 0x6FFFF), + (0x7FFFE, 0x7FFFF), + (0x8FFFE, 0x8FFFF), + (0x9FFFE, 0x9FFFF), + (0xAFFFE, 0xAFFFF), + (0xBFFFE, 0xBFFFF), + (0xCFFFE, 0xCFFFF), + (0xDFFFE, 0xDFFFF), + (0xEFFFE, 0xEFFFF), + (0xFFFFE, 0xFFFFF), + (0x10FFFE, 0x10FFFF), + ] + + illegal_ranges = ["%s-%s" % (unichr(low), unichr(high)) for (low, high) in illegal_unichrs if low < sys.maxunicode] + + illegal_xml_re = re.compile(u("[%s]") % u("").join(illegal_ranges)) + return illegal_xml_re.sub("", string_to_clean) class TestCase(object): """A JUnit test case with a result and possibly some stdout or stderr""" - def __init__(self, name, classname=None, elapsed_sec=None, stdout=None, - stderr=None, assertions=None, timestamp=None, status=None, - category=None, file=None, line=None, log=None, group=None, - url=None): + def __init__( + self, + name, + classname=None, + elapsed_sec=None, + stdout=None, + stderr=None, + assertions=None, + timestamp=None, + status=None, + category=None, + file=None, + line=None, + log=None, + url=None, + allow_multiple_subelements=False, + ): self.name = name self.assertions = assertions self.elapsed_sec = elapsed_sec @@ -338,48 +396,73 @@ self.stderr = stderr self.is_enabled = True - self.error_message = None - self.error_output = None - self.error_type = None - self.failure_message = None - self.failure_output = None - self.failure_type = None - self.skipped_message = None - self.skipped_output = None + self.errors = [] + self.failures = [] + self.skipped = [] + self.allow_multiple_subalements = allow_multiple_subelements def add_error_info(self, message=None, output=None, error_type=None): """Adds an error message, output, or both to the test case""" - if message: - self.error_message = message - if output: - self.error_output = output - if error_type: - self.error_type = error_type + error = {} + error["message"] = message + error["output"] = output + error["type"] = error_type + if self.allow_multiple_subalements: + if message or output: + self.errors.append(error) + elif not len(self.errors): + self.errors.append(error) + else: + if message: + self.errors[0]["message"] = message + if output: + self.errors[0]["output"] = output + if error_type: + self.errors[0]["type"] = error_type def add_failure_info(self, message=None, output=None, failure_type=None): """Adds a failure message, output, or both to the test case""" - if message: - self.failure_message = message - if output: - self.failure_output = output - if failure_type: - self.failure_type = failure_type + failure = {} + failure["message"] = message + failure["output"] = output + failure["type"] = failure_type + if self.allow_multiple_subalements: + if message or output: + self.failures.append(failure) + elif not len(self.failures): + self.failures.append(failure) + else: + if message: + self.failures[0]["message"] = message + if output: + self.failures[0]["output"] = output + if failure_type: + self.failures[0]["type"] = failure_type def add_skipped_info(self, message=None, output=None): """Adds a skipped message, output, or both to the test case""" - if message: - self.skipped_message = message - if output: - self.skipped_output = output + skipped = {} + skipped["message"] = message + skipped["output"] = output + if self.allow_multiple_subalements: + if message or output: + self.skipped.append(skipped) + elif not len(self.skipped): + self.skipped.append(skipped) + else: + if message: + self.skipped[0]["message"] = message + if output: + self.skipped[0]["output"] = output def is_failure(self): """returns true if this test case is a failure""" - return self.failure_output or self.failure_message + return sum(1 for f in self.failures if f["message"] or f["output"]) > 0 def is_error(self): """returns true if this test case is an error""" - return self.error_output or self.error_message + return sum(1 for e in self.errors if e["message"] or e["output"]) > 0 def is_skipped(self): """returns true if this test case has been skipped""" - return self.skipped_output or self.skipped_message + return len(self.skipped) > 0 diff -Nru python-junit-xml-1.8/junit_xml.egg-info/dependency_links.txt python-junit-xml-1.9/junit_xml.egg-info/dependency_links.txt --- python-junit-xml-1.8/junit_xml.egg-info/dependency_links.txt 2017-08-30 02:29:51.000000000 +0000 +++ python-junit-xml-1.9/junit_xml.egg-info/dependency_links.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ - diff -Nru python-junit-xml-1.8/junit_xml.egg-info/PKG-INFO python-junit-xml-1.9/junit_xml.egg-info/PKG-INFO --- python-junit-xml-1.8/junit_xml.egg-info/PKG-INFO 2017-08-30 02:29:51.000000000 +0000 +++ python-junit-xml-1.9/junit_xml.egg-info/PKG-INFO 1970-01-01 00:00:00.000000000 +0000 @@ -1,109 +0,0 @@ -Metadata-Version: 1.1 -Name: junit-xml -Version: 1.8 -Summary: Creates JUnit XML test result documents that can be read by tools such as Jenkins -Home-page: https://github.com/kyrus/python-junit-xml -Author: Brian Beyer -Author-email: brian@kyr.us -License: MIT -Description: python-junit-xml - ================ - .. image:: https://travis-ci.org/kyrus/python-junit-xml.png?branch=master - - About - ----- - - A Python module for creating JUnit XML test result documents that can be - read by tools such as Jenkins. If you are ever working with test tool or - test suite written in Python and want to take advantage of Jenkins' - pretty graphs and test reporting capabilities, this module will let you - generate the XML test reports. - - *As there is no definitive Jenkins JUnit XSD that I could find, the XML - documents created by this module support a schema based on Google - searches and the Jenkins JUnit XML reader source code. File a bug if - something doesn't work like you expect it to.* - - Installation - ------------ - - Install using pip or easy_install: - - :: - - pip install junit-xml - or - easy_install junit-xml - - You can also clone the Git repository from Github and install it manually: - - :: - - git clone https://github.com/kyrus/python-junit-xml.git - python setup.py install - - Using - ----- - - Create a test suite, add a test case, and print it to the screen: - - .. code-block:: python - - from junit_xml import TestSuite, TestCase - - test_cases = [TestCase('Test1', 'some.class.name', 123.345, 'I am stdout!', 'I am stderr!')] - ts = TestSuite("my test suite", test_cases) - # pretty printing is on by default but can be disabled using prettyprint=False - print(TestSuite.to_xml_string([ts])) - - Produces the following output - - .. code-block:: xml - - - - - - - I am stdout! - - - I am stderr! - - - - - - Writing XML to a file: - - .. code-block:: python - - # you can also write the XML to a file and not pretty print it - with open('output.xml', 'w') as f: - TestSuite.to_file(f, [ts], prettyprint=False) - - See the docs and unit tests for more examples. - - NOTE: Unicode characters identified as "illegal or discouraged" are automatically - stripped from the XML string or file. - - Running the tests - ----------------- - - :: - - # activate your virtualenv - pip install tox - tox - - -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: Freely Distributable -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Topic :: Software Development :: Build Tools -Classifier: Topic :: Software Development :: Testing diff -Nru python-junit-xml-1.8/junit_xml.egg-info/requires.txt python-junit-xml-1.9/junit_xml.egg-info/requires.txt --- python-junit-xml-1.8/junit_xml.egg-info/requires.txt 2017-08-30 02:29:51.000000000 +0000 +++ python-junit-xml-1.9/junit_xml.egg-info/requires.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -six diff -Nru python-junit-xml-1.8/junit_xml.egg-info/SOURCES.txt python-junit-xml-1.9/junit_xml.egg-info/SOURCES.txt --- python-junit-xml-1.8/junit_xml.egg-info/SOURCES.txt 2017-08-30 02:29:51.000000000 +0000 +++ python-junit-xml-1.9/junit_xml.egg-info/SOURCES.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -LICENSE.txt -MANIFEST.in -README.rst -setup.py -test_junit_xml.py -tox.ini -junit_xml/__init__.py -junit_xml.egg-info/PKG-INFO -junit_xml.egg-info/SOURCES.txt -junit_xml.egg-info/dependency_links.txt -junit_xml.egg-info/requires.txt -junit_xml.egg-info/top_level.txt \ No newline at end of file diff -Nru python-junit-xml-1.8/junit_xml.egg-info/top_level.txt python-junit-xml-1.9/junit_xml.egg-info/top_level.txt --- python-junit-xml-1.8/junit_xml.egg-info/top_level.txt 2017-08-30 02:29:51.000000000 +0000 +++ python-junit-xml-1.9/junit_xml.egg-info/top_level.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -junit_xml diff -Nru python-junit-xml-1.8/Makefile python-junit-xml-1.9/Makefile --- python-junit-xml-1.8/Makefile 1970-01-01 00:00:00.000000000 +0000 +++ python-junit-xml-1.9/Makefile 2020-02-22 20:32:08.000000000 +0000 @@ -0,0 +1,17 @@ +ACTIVATE=venv/bin/activate + +venv: $(ACTIVATE) +$(ACTIVATE): requirements.txt requirements_dev.txt + test -d venv || virtualenv venv + . $(ACTIVATE); pip install -r requirements_dev.txt + +.PHONY : dist +dist: + python setup.py sdist bdist_wheel + +.PHONY : clean +clean: + find . -name "*.pyc" -delete + find . -name "__pycache__" -delete + rm -rf build + rm -rf dist diff -Nru python-junit-xml-1.8/MANIFEST.in python-junit-xml-1.9/MANIFEST.in --- python-junit-xml-1.8/MANIFEST.in 2016-09-23 15:22:03.000000000 +0000 +++ python-junit-xml-1.9/MANIFEST.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -include LICENSE.txt -include README.rst -include setup.py -include test_junit_xml.py -include tox.ini diff -Nru python-junit-xml-1.8/PKG-INFO python-junit-xml-1.9/PKG-INFO --- python-junit-xml-1.8/PKG-INFO 2017-08-30 02:29:51.000000000 +0000 +++ python-junit-xml-1.9/PKG-INFO 1970-01-01 00:00:00.000000000 +0000 @@ -1,109 +0,0 @@ -Metadata-Version: 1.1 -Name: junit-xml -Version: 1.8 -Summary: Creates JUnit XML test result documents that can be read by tools such as Jenkins -Home-page: https://github.com/kyrus/python-junit-xml -Author: Brian Beyer -Author-email: brian@kyr.us -License: MIT -Description: python-junit-xml - ================ - .. image:: https://travis-ci.org/kyrus/python-junit-xml.png?branch=master - - About - ----- - - A Python module for creating JUnit XML test result documents that can be - read by tools such as Jenkins. If you are ever working with test tool or - test suite written in Python and want to take advantage of Jenkins' - pretty graphs and test reporting capabilities, this module will let you - generate the XML test reports. - - *As there is no definitive Jenkins JUnit XSD that I could find, the XML - documents created by this module support a schema based on Google - searches and the Jenkins JUnit XML reader source code. File a bug if - something doesn't work like you expect it to.* - - Installation - ------------ - - Install using pip or easy_install: - - :: - - pip install junit-xml - or - easy_install junit-xml - - You can also clone the Git repository from Github and install it manually: - - :: - - git clone https://github.com/kyrus/python-junit-xml.git - python setup.py install - - Using - ----- - - Create a test suite, add a test case, and print it to the screen: - - .. code-block:: python - - from junit_xml import TestSuite, TestCase - - test_cases = [TestCase('Test1', 'some.class.name', 123.345, 'I am stdout!', 'I am stderr!')] - ts = TestSuite("my test suite", test_cases) - # pretty printing is on by default but can be disabled using prettyprint=False - print(TestSuite.to_xml_string([ts])) - - Produces the following output - - .. code-block:: xml - - - - - - - I am stdout! - - - I am stderr! - - - - - - Writing XML to a file: - - .. code-block:: python - - # you can also write the XML to a file and not pretty print it - with open('output.xml', 'w') as f: - TestSuite.to_file(f, [ts], prettyprint=False) - - See the docs and unit tests for more examples. - - NOTE: Unicode characters identified as "illegal or discouraged" are automatically - stripped from the XML string or file. - - Running the tests - ----------------- - - :: - - # activate your virtualenv - pip install tox - tox - - -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: Freely Distributable -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Topic :: Software Development :: Build Tools -Classifier: Topic :: Software Development :: Testing diff -Nru python-junit-xml-1.8/pyproject.toml python-junit-xml-1.9/pyproject.toml --- python-junit-xml-1.8/pyproject.toml 1970-01-01 00:00:00.000000000 +0000 +++ python-junit-xml-1.9/pyproject.toml 2020-02-22 20:32:08.000000000 +0000 @@ -0,0 +1,2 @@ +[tool.black] +line-length = 120 diff -Nru python-junit-xml-1.8/pytest.ini python-junit-xml-1.9/pytest.ini --- python-junit-xml-1.8/pytest.ini 1970-01-01 00:00:00.000000000 +0000 +++ python-junit-xml-1.9/pytest.ini 2020-02-22 20:32:08.000000000 +0000 @@ -0,0 +1,2 @@ +[pytest] +norecursedirs = .git .tox build dist venv diff -Nru python-junit-xml-1.8/README.rst python-junit-xml-1.9/README.rst --- python-junit-xml-1.8/README.rst 2016-09-23 15:22:03.000000000 +0000 +++ python-junit-xml-1.9/README.rst 2020-02-22 20:32:08.000000000 +0000 @@ -6,15 +6,16 @@ ----- A Python module for creating JUnit XML test result documents that can be -read by tools such as Jenkins. If you are ever working with test tool or -test suite written in Python and want to take advantage of Jenkins' +read by tools such as Jenkins or Bamboo. If you are ever working with test tool or +test suite written in Python and want to take advantage of Jenkins' or Bamboo's pretty graphs and test reporting capabilities, this module will let you generate the XML test reports. *As there is no definitive Jenkins JUnit XSD that I could find, the XML documents created by this module support a schema based on Google searches and the Jenkins JUnit XML reader source code. File a bug if -something doesn't work like you expect it to.* +something doesn't work like you expect it to. +For Bamboo situation is the same.* Installation ------------ diff -Nru python-junit-xml-1.8/requirements_dev.txt python-junit-xml-1.9/requirements_dev.txt --- python-junit-xml-1.8/requirements_dev.txt 1970-01-01 00:00:00.000000000 +0000 +++ python-junit-xml-1.9/requirements_dev.txt 2020-02-22 20:32:08.000000000 +0000 @@ -0,0 +1,4 @@ +-r requirements.txt +flake8-black +pytest-sugar +pytest-flake8 diff -Nru python-junit-xml-1.8/requirements.txt python-junit-xml-1.9/requirements.txt --- python-junit-xml-1.8/requirements.txt 1970-01-01 00:00:00.000000000 +0000 +++ python-junit-xml-1.9/requirements.txt 2020-02-22 20:32:08.000000000 +0000 @@ -0,0 +1 @@ +six diff -Nru python-junit-xml-1.8/setup.cfg python-junit-xml-1.9/setup.cfg --- python-junit-xml-1.8/setup.cfg 2017-08-30 02:29:51.000000000 +0000 +++ python-junit-xml-1.9/setup.cfg 2020-02-22 20:32:08.000000000 +0000 @@ -1,4 +1,6 @@ -[egg_info] -tag_build = -tag_date = 0 +[flake8] +max-line-length = 120 +exclude = .git,.tox,build,dist,venv +[wheel] +universal = 1 diff -Nru python-junit-xml-1.8/setup.py python-junit-xml-1.9/setup.py --- python-junit-xml-1.8/setup.py 2017-08-30 02:29:33.000000000 +0000 +++ python-junit-xml-1.9/setup.py 2020-02-22 20:32:08.000000000 +0000 @@ -6,30 +6,27 @@ def read(fname): return open(os.path.join(os.path.dirname(__file__), fname)).read() + setup( - name='junit-xml', - author='Brian Beyer', - author_email='brian@kyr.us', - url='https://github.com/kyrus/python-junit-xml', - license='MIT', - packages=find_packages(), - test_suite='test_junit_xml', - description='Creates JUnit XML test result documents that can be read by ' - 'tools such as Jenkins', - long_description=read('README.rst'), - version='1.8', + name="junit-xml", + author="Brian Beyer", + author_email="brian@kyr.us", + url="https://github.com/kyrus/python-junit-xml", + license="MIT", + packages=find_packages(exclude=["tests"]), + description="Creates JUnit XML test result documents that can be read by tools such as Jenkins", + long_description=read("README.rst"), + version="1.9", classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'License :: Freely Distributable', - 'License :: OSI Approved :: MIT License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Topic :: Software Development :: Build Tools', - 'Topic :: Software Development :: Testing', - ], - install_requires=[ - 'six' - ] - ) + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: Freely Distributable", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Topic :: Software Development :: Build Tools", + "Topic :: Software Development :: Testing", + ], + install_requires=["six"], +) diff -Nru python-junit-xml-1.8/test_junit_xml.py python-junit-xml-1.9/test_junit_xml.py --- python-junit-xml-1.8/test_junit_xml.py 2017-05-22 15:26:44.000000000 +0000 +++ python-junit-xml-1.9/test_junit_xml.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,579 +0,0 @@ -# -*- coding: UTF-8 -*- -from __future__ import with_statement -import unittest -import os -import tempfile -import textwrap -from xml.dom import minidom -import codecs - -from six import u, PY2 - -from junit_xml import TestCase, TestSuite, decode - - -def serialize_and_read(test_suites, to_file=False, prettyprint=False, encoding=None): - """writes the test suite to an XML string and then re-reads it using minidom, - returning => (test suite element, list of test case elements)""" - try: - iter(test_suites) - except TypeError: - test_suites = [test_suites] - - if to_file: - fd, filename = tempfile.mkstemp(text=True) - os.close(fd) - with codecs.open(filename, mode='w', encoding=encoding) as f: - TestSuite.to_file(f, test_suites, prettyprint=prettyprint, encoding=encoding) - print("Serialized XML to temp file [%s]" % filename) - xmldoc = minidom.parse(filename) - os.remove(filename) - else: - xml_string = TestSuite.to_xml_string( - test_suites, prettyprint=prettyprint, encoding=encoding) - if PY2: - assert isinstance(xml_string, unicode) - print("Serialized XML to string:\n%s" % xml_string) - if encoding: - xml_string = xml_string.encode(encoding) - xmldoc = minidom.parseString(xml_string) - - def remove_blanks(node): - for x in node.childNodes: - if x.nodeType == minidom.Node.TEXT_NODE: - if x.nodeValue: - x.nodeValue = x.nodeValue.strip() - elif x.nodeType == minidom.Node.ELEMENT_NODE: - remove_blanks(x) - remove_blanks(xmldoc) - xmldoc.normalize() - - ret = [] - suites = xmldoc.getElementsByTagName("testsuites")[0] - for suite in suites.getElementsByTagName("testsuite"): - cases = suite.getElementsByTagName("testcase") - ret.append((suite, cases)) - return ret - - -class TestSuiteTests(unittest.TestCase): - def test_single_suite_single_test_case(self): - try: - (ts, tcs) = serialize_and_read( - TestSuite('test', TestCase('Test1')), to_file=True)[0] - self.fail("This should've raised an exeception") # pragma: nocover - except Exception as exc: - self.assertEqual( - str(exc), 'test_cases must be a list of test cases') - - def test_single_suite_no_test_cases(self): - properties = {'foo': 'bar'} - package = 'mypackage' - timestamp = 1398382805 - - (ts, tcs) = serialize_and_read( - TestSuite( - name='test', - test_cases=[], - hostname='localhost', - id=1, - properties=properties, - package=package, - timestamp=timestamp - ), - to_file=True, - prettyprint=True - )[0] - self.assertEqual(ts.tagName, 'testsuite') - self.assertEqual(ts.attributes['package'].value, package) - self.assertEqual(ts.attributes['timestamp'].value, str(timestamp)) - self.assertEqual( - ts.childNodes[0].childNodes[0].attributes['name'].value, - 'foo') - self.assertEqual( - ts.childNodes[0].childNodes[0].attributes['value'].value, - 'bar') - - def test_single_suite_no_test_cases_utf8(self): - properties = {'foö': 'bär'} - package = 'mypäckage' - timestamp = 1398382805 - - test_suite = TestSuite( - name='äöü', - test_cases=[], - hostname='löcalhost', - id='äöü', - properties=properties, - package=package, - timestamp=timestamp - ) - (ts, tcs) = serialize_and_read( - test_suite, - to_file=True, - prettyprint=True, - encoding='utf-8' - )[0] - self.assertEqual(ts.tagName, 'testsuite') - self.assertEqual(ts.attributes['package'].value, decode(package, 'utf-8')) - self.assertEqual(ts.attributes['timestamp'].value, str(timestamp)) - self.assertEqual( - ts.childNodes[0].childNodes[0].attributes['name'].value, - decode('foö', 'utf-8')) - self.assertEqual( - ts.childNodes[0].childNodes[0].attributes['value'].value, - decode('bär', 'utf-8')) - - def test_single_suite_no_test_cases_unicode(self): - properties = {decode('foö', 'utf-8'): decode('bär', 'utf-8')} - package = decode('mypäckage', 'utf-8') - timestamp = 1398382805 - - (ts, tcs) = serialize_and_read( - TestSuite( - name=decode('äöü', 'utf-8'), - test_cases=[], - hostname=decode('löcalhost', 'utf-8'), - id=decode('äöü', 'utf-8'), - properties=properties, - package=package, - timestamp=timestamp - ), - to_file=True, - prettyprint=True, - encoding='utf-8' - )[0] - self.assertEqual(ts.tagName, 'testsuite') - self.assertEqual(ts.attributes['package'].value, package) - self.assertEqual(ts.attributes['timestamp'].value, str(timestamp)) - self.assertEqual( - ts.childNodes[0].childNodes[0].attributes['name'].value, - decode('foö', 'utf-8')) - self.assertEqual( - ts.childNodes[0].childNodes[0].attributes['value'].value, - decode('bär', 'utf-8')) - - def test_single_suite_to_file(self): - (ts, tcs) = serialize_and_read( - TestSuite('test', [TestCase('Test1')]), to_file=True)[0] - verify_test_case(self, tcs[0], {'name': 'Test1'}) - - def test_single_suite_to_file_prettyprint(self): - (ts, tcs) = serialize_and_read(TestSuite( - 'test', [TestCase('Test1')]), to_file=True, prettyprint=True)[0] - verify_test_case(self, tcs[0], {'name': 'Test1'}) - - def test_single_suite_prettyprint(self): - (ts, tcs) = serialize_and_read( - TestSuite('test', [TestCase('Test1')]), - to_file=False, prettyprint=True)[0] - verify_test_case(self, tcs[0], {'name': 'Test1'}) - - def test_single_suite_to_file_no_prettyprint(self): - (ts, tcs) = serialize_and_read( - TestSuite('test', [TestCase('Test1')]), - to_file=True, prettyprint=False)[0] - verify_test_case(self, tcs[0], {'name': 'Test1'}) - - def test_multiple_suites_to_file(self): - tss = [TestSuite('suite1', [TestCase('Test1')]), - TestSuite('suite2', [TestCase('Test2')])] - suites = serialize_and_read(tss, to_file=True) - - self.assertEqual('suite1', suites[0][0].attributes['name'].value) - verify_test_case(self, suites[0][1][0], {'name': 'Test1'}) - - self.assertEqual('suite2', suites[1][0].attributes['name'].value) - verify_test_case(self, suites[1][1][0], {'name': 'Test2'}) - - def test_multiple_suites_to_string(self): - tss = [TestSuite('suite1', [TestCase('Test1')]), - TestSuite('suite2', [TestCase('Test2')])] - suites = serialize_and_read(tss) - - self.assertEqual('suite1', suites[0][0].attributes['name'].value) - verify_test_case(self, suites[0][1][0], {'name': 'Test1'}) - - self.assertEqual('suite2', suites[1][0].attributes['name'].value) - verify_test_case(self, suites[1][1][0], {'name': 'Test2'}) - - def test_attribute_time(self): - tss = [TestSuite('suite1', [TestCase(name='Test1', classname='some.class.name', elapsed_sec=123.345), - TestCase(name='Test2', classname='some2.class.name', elapsed_sec=123.345)]), - TestSuite('suite2', [TestCase('Test2')])] - suites = serialize_and_read(tss) - - self.assertEqual('suite1', suites[0][0].attributes['name'].value) - self.assertEqual('246.69', suites[0][0].attributes['time'].value) - - self.assertEqual('suite2', suites[1][0].attributes['name'].value) - # here the time in testsuite is "0" even there is no attribute time for - # testcase - self.assertEqual('0', suites[1][0].attributes['time'].value) - - def test_attribute_disable(self): - tc = TestCase('Disabled-Test') - tc.is_enabled = False - tss = [TestSuite('suite1', [tc])] - suites = serialize_and_read(tss) - - self.assertEqual('1', suites[0][0].attributes['disabled'].value) - - def test_stderr(self): - suites = serialize_and_read( - TestSuite(name='test', stderr='I am stderr!', - test_cases=[TestCase(name='Test1')]))[0] - self.assertEqual('I am stderr!', - suites[0].getElementsByTagName('system-err')[0].firstChild.data) - - def test_stdout_stderr(self): - suites = serialize_and_read( - TestSuite(name='test', stdout='I am stdout!', - stderr='I am stderr!', - test_cases=[TestCase(name='Test1')]))[0] - self.assertEqual('I am stderr!', - suites[0].getElementsByTagName('system-err')[0].firstChild.data) - self.assertEqual('I am stdout!', - suites[0].getElementsByTagName('system-out')[0].firstChild.data) - - def test_no_assertions(self): - suites = serialize_and_read( - TestSuite(name='test', - test_cases=[TestCase(name='Test1')]))[0] - self.assertFalse(suites[0].getElementsByTagName('testcase')[0].hasAttribute('assertions')) - - def test_assertions(self): - suites = serialize_and_read( - TestSuite(name='test', - test_cases=[TestCase(name='Test1', - assertions=5)]))[0] - self.assertEquals('5', - suites[0].getElementsByTagName('testcase')[0].attributes['assertions'].value) - - # @todo: add more tests for the other attributes and properties - - def test_to_xml_string(self): - test_suites = [TestSuite(name='suite1', test_cases=[TestCase(name='Test1')]), - TestSuite(name='suite2', test_cases=[TestCase(name='Test2')])] - xml_string = TestSuite.to_xml_string(test_suites) - if PY2: - self.assertTrue(isinstance(xml_string, unicode)) - expected_xml_string = textwrap.dedent(""" - - - \t - \t\t - \t - \t - \t\t - \t - - """.strip("\n")) # NOQA - self.assertEqual(xml_string, expected_xml_string) - - def test_to_xml_string_test_suites_not_a_list(self): - test_suites = TestSuite('suite1', [TestCase('Test1')]) - - try: - TestSuite.to_xml_string(test_suites) - except Exception as exc: - self.assertEqual( - str(exc), 'test_suites must be a list of test suites') - - -class TestCaseTests(unittest.TestCase): - def test_init(self): - (ts, tcs) = serialize_and_read( - TestSuite('test', [TestCase('Test1')]))[0] - verify_test_case(self, tcs[0], {'name': 'Test1'}) - - def test_init_classname(self): - (ts, tcs) = serialize_and_read( - TestSuite('test', - [TestCase(name='Test1', classname='some.class.name')]))[0] - verify_test_case( - self, tcs[0], {'name': 'Test1', 'classname': 'some.class.name'}) - - def test_init_classname_time(self): - (ts, tcs) = serialize_and_read( - TestSuite('test', - [TestCase(name='Test1', classname='some.class.name', - elapsed_sec=123.345)]))[0] - verify_test_case( - self, tcs[0], {'name': 'Test1', 'classname': 'some.class.name', - 'time': ("%f" % 123.345)}) - - def test_init_classname_time_timestamp(self): - (ts, tcs) = serialize_and_read( - TestSuite('test', - [TestCase(name='Test1', classname='some.class.name', - elapsed_sec=123.345, timestamp=99999)]))[0] - verify_test_case( - self, tcs[0], {'name': 'Test1', 'classname': 'some.class.name', - 'time': ("%f" % 123.345), - 'timestamp': ("%s" % 99999)}) - - def test_init_stderr(self): - (ts, tcs) = serialize_and_read( - TestSuite( - 'test', [TestCase(name='Test1', classname='some.class.name', - elapsed_sec=123.345, stderr='I am stderr!')]))[0] - verify_test_case( - self, tcs[0], - {'name': 'Test1', 'classname': 'some.class.name', - 'time': ("%f" % 123.345)}, stderr='I am stderr!') - - def test_init_stdout_stderr(self): - (ts, tcs) = serialize_and_read( - TestSuite( - 'test', [TestCase( - name='Test1', classname='some.class.name', - elapsed_sec=123.345, stdout='I am stdout!', - stderr='I am stderr!')]))[0] - verify_test_case( - self, tcs[0], - {'name': 'Test1', 'classname': 'some.class.name', - 'time': ("%f" % 123.345)}, - stdout='I am stdout!', stderr='I am stderr!') - - def test_init_disable(self): - tc = TestCase('Disabled-Test') - tc.is_enabled = False - (ts, tcs) = serialize_and_read(TestSuite('test', [tc]))[0] - verify_test_case(self, tcs[0], {'name': 'Disabled-Test'}) - - def test_init_failure_message(self): - tc = TestCase('Failure-Message') - tc.add_failure_info("failure message") - (ts, tcs) = serialize_and_read(TestSuite('test', [tc]))[0] - verify_test_case( - self, tcs[0], {'name': 'Failure-Message'}, - failure_message="failure message") - - def test_init_failure_output(self): - tc = TestCase('Failure-Output') - tc.add_failure_info(output="I failed!") - (ts, tcs) = serialize_and_read(TestSuite('test', [tc]))[0] - verify_test_case( - self, tcs[0], {'name': 'Failure-Output'}, - failure_output="I failed!") - - def test_init_failure_type(self): - tc = TestCase('Failure-Type') - tc.add_failure_info(failure_type='com.example.Error') - (ts, tcs) = serialize_and_read(TestSuite('test', [tc]))[0] - verify_test_case(self, tcs[0], {'name': 'Failure-Type'}) - - tc.add_failure_info("failure message") - (ts, tcs) = serialize_and_read(TestSuite('test', [tc]))[0] - verify_test_case( - self, tcs[0], {'name': 'Failure-Type'}, - failure_message="failure message", - failure_type='com.example.Error') - - def test_init_failure(self): - tc = TestCase('Failure-Message-and-Output') - tc.add_failure_info("failure message", "I failed!") - (ts, tcs) = serialize_and_read(TestSuite('test', [tc]))[0] - verify_test_case( - self, tcs[0], {'name': 'Failure-Message-and-Output'}, - failure_message="failure message", failure_output="I failed!", - failure_type='failure') - - def test_init_error_message(self): - tc = TestCase('Error-Message') - tc.add_error_info("error message") - (ts, tcs) = serialize_and_read(TestSuite('test', [tc]))[0] - verify_test_case( - self, tcs[0], {'name': 'Error-Message'}, - error_message="error message") - - def test_init_error_output(self): - tc = TestCase('Error-Output') - tc.add_error_info(output="I errored!") - (ts, tcs) = serialize_and_read(TestSuite('test', [tc]))[0] - verify_test_case( - self, tcs[0], {'name': 'Error-Output'}, error_output="I errored!") - - def test_init_error_type(self): - tc = TestCase('Error-Type') - tc.add_error_info(error_type='com.example.Error') - (ts, tcs) = serialize_and_read(TestSuite('test', [tc]))[0] - verify_test_case(self, tcs[0], {'name': 'Error-Type'}) - - tc.add_error_info("error message") - (ts, tcs) = serialize_and_read(TestSuite('test', [tc]))[0] - verify_test_case( - self, tcs[0], {'name': 'Error-Type'}, - error_message="error message", - error_type='com.example.Error') - - def test_init_error(self): - tc = TestCase('Error-Message-and-Output') - tc.add_error_info("error message", "I errored!") - (ts, tcs) = serialize_and_read(TestSuite('test', [tc]))[0] - verify_test_case( - self, tcs[0], {'name': 'Error-Message-and-Output'}, - error_message="error message", error_output="I errored!", - error_type="error") - - def test_init_skipped_message(self): - tc = TestCase('Skipped-Message') - tc.add_skipped_info("skipped message") - (ts, tcs) = serialize_and_read(TestSuite('test', [tc]))[0] - verify_test_case( - self, tcs[0], {'name': 'Skipped-Message'}, - skipped_message="skipped message") - - def test_init_skipped_output(self): - tc = TestCase('Skipped-Output') - tc.add_skipped_info(output="I skipped!") - (ts, tcs) = serialize_and_read(TestSuite('test', [tc]))[0] - verify_test_case( - self, tcs[0], {'name': 'Skipped-Output'}, - skipped_output="I skipped!") - - def test_init_skipped_err_output(self): - tc = TestCase('Skipped-Output') - tc.add_skipped_info(output="I skipped!") - tc.add_error_info(output="I skipped with an error!") - (ts, tcs) = serialize_and_read(TestSuite('test', [tc]))[0] - verify_test_case( - self, tcs[0], - {'name': 'Skipped-Output'}, - skipped_output="I skipped!", - error_output="I skipped with an error!") - - def test_init_skipped(self): - tc = TestCase('Skipped-Message-and-Output') - tc.add_skipped_info("skipped message", "I skipped!") - (ts, tcs) = serialize_and_read(TestSuite('test', [tc]))[0] - verify_test_case( - self, tcs[0], {'name': 'Skipped-Message-and-Output'}, - skipped_message="skipped message", skipped_output="I skipped!") - - def test_init_legal_unicode_char(self): - tc = TestCase('Failure-Message') - tc.add_failure_info( - u("failure message with legal unicode char: [\x22]")) - (ts, tcs) = serialize_and_read(TestSuite('test', [tc]))[0] - verify_test_case( - self, tcs[0], {'name': 'Failure-Message'}, failure_message=u( - "failure message with legal unicode char: [\x22]")) - - def test_init_illegal_unicode_char(self): - tc = TestCase('Failure-Message') - tc.add_failure_info( - u("failure message with illegal unicode char: [\x02]")) - (ts, tcs) = serialize_and_read(TestSuite('test', [tc]))[0] - verify_test_case( - self, tcs[0], {'name': 'Failure-Message'}, failure_message=u( - "failure message with illegal unicode char: []")) - - def test_init_utf8(self): - tc = TestCase(name='Test äöü', classname='some.class.name.äöü', - elapsed_sec=123.345, stdout='I am stdöüt!', - stderr='I am stdärr!') - tc.add_skipped_info(message='Skipped äöü', output="I skippäd!") - tc.add_error_info(message='Skipped error äöü', - output="I skippäd with an error!") - test_suite = TestSuite('Test UTF-8', [tc]) - (ts, tcs) = serialize_and_read(test_suite, encoding='utf-8')[0] - verify_test_case(self, tcs[0], {'name': decode('Test äöü', 'utf-8'), - 'classname': decode('some.class.name.äöü', 'utf-8'), - 'time': ("%f" % 123.345)}, - stdout=decode('I am stdöüt!', 'utf-8'), stderr=decode('I am stdärr!', 'utf-8'), - skipped_message=decode('Skipped äöü', 'utf-8'), - skipped_output=decode('I skippäd!', 'utf-8'), - error_message=decode('Skipped error äöü', 'utf-8'), - error_output=decode('I skippäd with an error!', 'utf-8')) - - def test_init_unicode(self): - tc = TestCase(name=decode('Test äöü', 'utf-8'), - classname=decode('some.class.name.äöü', 'utf-8'), - elapsed_sec=123.345, - stdout=decode('I am stdöüt!', 'utf-8'), - stderr=decode('I am stdärr!', 'utf-8')) - tc.add_skipped_info(message=decode('Skipped äöü', 'utf-8'), - output=decode('I skippäd!', 'utf-8')) - tc.add_error_info(message=decode('Skipped error äöü', 'utf-8'), - output=decode('I skippäd with an error!', 'utf-8')) - - (ts, tcs) = serialize_and_read(TestSuite('Test Unicode', - [tc]))[0] - verify_test_case(self, tcs[0], {'name': decode('Test äöü', 'utf-8'), - 'classname': decode('some.class.name.äöü', 'utf-8'), - 'time': ("%f" % 123.345)}, - stdout=decode('I am stdöüt!', 'utf-8'), - stderr=decode('I am stdärr!', 'utf-8'), - skipped_message=decode('Skipped äöü', 'utf-8'), - skipped_output=decode('I skippäd!', 'utf-8'), - error_message=decode('Skipped error äöü', 'utf-8'), - error_output=decode('I skippäd with an error!', 'utf-8')) - - -def verify_test_case(tc, test_case_element, expected_attributes, - error_message=None, error_output=None, error_type=None, - failure_message=None, failure_output=None, - failure_type=None, - skipped_message=None, skipped_output=None, - stdout=None, stderr=None): - for k, v in expected_attributes.items(): - tc.assertEqual(v, test_case_element.attributes[k].value) - - for k in test_case_element.attributes.keys(): - tc.assertTrue(k in expected_attributes.keys()) - - if stderr: - tc.assertEqual( - stderr, test_case_element.getElementsByTagName( - 'system-err')[0].firstChild.nodeValue.strip()) - if stdout: - tc.assertEqual( - stdout, test_case_element.getElementsByTagName( - 'system-out')[0].firstChild.nodeValue.strip()) - - errors = test_case_element.getElementsByTagName('error') - if error_message or error_output: - tc.assertTrue(len(errors) > 0) - else: - tc.assertEqual(0, len(errors)) - - if error_message: - tc.assertEqual( - error_message, errors[0].attributes['message'].value) - - if error_type and errors: - tc.assertEqual( - error_type, errors[0].attributes['type'].value) - - if error_output: - tc.assertEqual( - error_output, errors[0].firstChild.nodeValue.strip()) - - failures = test_case_element.getElementsByTagName('failure') - if failure_message or failure_output: - tc.assertTrue(len(failures) > 0) - else: - tc.assertEqual(0, len(failures)) - - if failure_message: - tc.assertEqual( - failure_message, failures[0].attributes['message'].value) - - if failure_type and failures: - tc.assertEqual( - failure_type, failures[0].attributes['type'].value) - - if failure_output: - tc.assertEqual( - failure_output, failures[0].firstChild.nodeValue.strip()) - - skipped = test_case_element.getElementsByTagName('skipped') - if skipped_message or skipped_output: - tc.assertTrue(len(skipped) > 0) - else: - tc.assertEqual(0, len(skipped)) - -if __name__ == '__main__': - unittest.main() diff -Nru python-junit-xml-1.8/tests/asserts.py python-junit-xml-1.9/tests/asserts.py --- python-junit-xml-1.8/tests/asserts.py 1970-01-01 00:00:00.000000000 +0000 +++ python-junit-xml-1.9/tests/asserts.py 2020-02-22 20:32:08.000000000 +0000 @@ -0,0 +1,83 @@ +def verify_test_case( # noqa: E302 + test_case_element, + expected_attributes, + error_message=None, + error_output=None, + error_type=None, + failure_message=None, + failure_output=None, + failure_type=None, + skipped_message=None, + skipped_output=None, + stdout=None, + stderr=None, + errors=None, + failures=None, + skipped=None, +): + for k, v in expected_attributes.items(): + assert test_case_element.attributes[k].value == v + + for k in test_case_element.attributes.keys(): + assert k in expected_attributes.keys() + + if stderr: + assert test_case_element.getElementsByTagName("system-err")[0].firstChild.nodeValue.strip() == stderr + if stdout: + assert test_case_element.getElementsByTagName("system-out")[0].firstChild.nodeValue.strip() == stdout + + _errors = test_case_element.getElementsByTagName("error") + if error_message or error_output: + assert len(_errors) > 0 + elif errors: + assert len(errors) == len(_errors) + else: + assert len(_errors) == 0 + + if error_message: + assert _errors[0].attributes["message"].value == error_message + + if error_type and _errors: + assert _errors[0].attributes["type"].value == error_type + + if error_output: + assert _errors[0].firstChild.nodeValue.strip() == error_output + + for error_exp, error_r in zip(errors or [], _errors): + assert error_r.attributes["message"].value == error_exp["message"] + assert error_r.firstChild.nodeValue.strip() == error_exp["output"] + assert error_r.attributes["type"].value == error_exp["type"] + + _failures = test_case_element.getElementsByTagName("failure") + if failure_message or failure_output: + assert len(_failures) > 0 + elif failures: + assert len(failures) == len(_failures) + else: + assert len(_failures) == 0 + + if failure_message: + assert _failures[0].attributes["message"].value == failure_message + + if failure_type and _failures: + assert _failures[0].attributes["type"].value == failure_type + + if failure_output: + assert _failures[0].firstChild.nodeValue.strip() == failure_output + + for failure_exp, failure_r in zip(failures or [], _failures): + assert failure_r.attributes["message"].value == failure_exp["message"] + assert failure_r.firstChild.nodeValue.strip() == failure_exp["output"] + assert failure_r.attributes["type"].value == failure_exp["type"] + + _skipped = test_case_element.getElementsByTagName("skipped") + if skipped_message or skipped_output: + assert len(_skipped) > 0 + elif skipped: + assert len(skipped) == len(_skipped) + else: + assert len(_skipped) == 0 + + for skipped_exp, skipped_r in zip(skipped or [], _skipped): + assert skipped_r.attributes["message"].value == skipped_exp["message"] + assert skipped_r.firstChild.nodeValue.strip() == skipped_exp["output"] diff -Nru python-junit-xml-1.8/tests/serializer.py python-junit-xml-1.9/tests/serializer.py --- python-junit-xml-1.8/tests/serializer.py 1970-01-01 00:00:00.000000000 +0000 +++ python-junit-xml-1.9/tests/serializer.py 2020-02-22 20:32:08.000000000 +0000 @@ -0,0 +1,52 @@ +import codecs +import os +import tempfile +from xml.dom import minidom + +from six import PY2 + +from junit_xml import to_xml_report_file, to_xml_report_string + + +def serialize_and_read(test_suites, to_file=False, prettyprint=False, encoding=None): + """writes the test suite to an XML string and then re-reads it using minidom, + returning => (test suite element, list of test case elements)""" + try: + iter(test_suites) + except TypeError: + test_suites = [test_suites] + + if to_file: + fd, filename = tempfile.mkstemp(text=True) + os.close(fd) + with codecs.open(filename, mode="w", encoding=encoding) as f: + to_xml_report_file(f, test_suites, prettyprint=prettyprint, encoding=encoding) + print("Serialized XML to temp file [%s]" % filename) + xmldoc = minidom.parse(filename) + os.remove(filename) + else: + xml_string = to_xml_report_string(test_suites, prettyprint=prettyprint, encoding=encoding) + if PY2: + assert isinstance(xml_string, unicode) # noqa: F821 + print("Serialized XML to string:\n%s" % xml_string) + if encoding: + xml_string = xml_string.encode(encoding) + xmldoc = minidom.parseString(xml_string) + + def remove_blanks(node): + for x in node.childNodes: + if x.nodeType == minidom.Node.TEXT_NODE: + if x.nodeValue: + x.nodeValue = x.nodeValue.strip() + elif x.nodeType == minidom.Node.ELEMENT_NODE: + remove_blanks(x) + + remove_blanks(xmldoc) + xmldoc.normalize() + + ret = [] + suites = xmldoc.getElementsByTagName("testsuites")[0] + for suite in suites.getElementsByTagName("testsuite"): + cases = suite.getElementsByTagName("testcase") + ret.append((suite, cases)) + return ret diff -Nru python-junit-xml-1.8/tests/test_test_case.py python-junit-xml-1.9/tests/test_test_case.py --- python-junit-xml-1.8/tests/test_test_case.py 1970-01-01 00:00:00.000000000 +0000 +++ python-junit-xml-1.9/tests/test_test_case.py 2020-02-22 20:32:08.000000000 +0000 @@ -0,0 +1,324 @@ +# -*- coding: UTF-8 -*- +from __future__ import with_statement + +from six import u + +from .asserts import verify_test_case +from junit_xml import TestCase as Case +from junit_xml import TestSuite as Suite +from junit_xml import decode +from .serializer import serialize_and_read + + +def test_init(): + ts, tcs = serialize_and_read(Suite("test", [Case("Test1")]))[0] + verify_test_case(tcs[0], {"name": "Test1"}) + + +def test_init_classname(): + ts, tcs = serialize_and_read(Suite("test", [Case(name="Test1", classname="some.class.name")]))[0] + verify_test_case(tcs[0], {"name": "Test1", "classname": "some.class.name"}) + + +def test_init_classname_time(): + ts, tcs = serialize_and_read(Suite("test", [Case(name="Test1", classname="some.class.name", elapsed_sec=123.345)]))[ + 0 + ] + verify_test_case(tcs[0], {"name": "Test1", "classname": "some.class.name", "time": ("%f" % 123.345)}) + + +def test_init_classname_time_timestamp(): + ts, tcs = serialize_and_read( + Suite("test", [Case(name="Test1", classname="some.class.name", elapsed_sec=123.345, timestamp=99999)]) + )[0] + verify_test_case( + tcs[0], {"name": "Test1", "classname": "some.class.name", "time": ("%f" % 123.345), "timestamp": ("%s" % 99999)} + ) + + +def test_init_stderr(): + ts, tcs = serialize_and_read( + Suite("test", [Case(name="Test1", classname="some.class.name", elapsed_sec=123.345, stderr="I am stderr!")]) + )[0] + verify_test_case( + tcs[0], {"name": "Test1", "classname": "some.class.name", "time": ("%f" % 123.345)}, stderr="I am stderr!" + ) + + +def test_init_stdout_stderr(): + ts, tcs = serialize_and_read( + Suite( + "test", + [ + Case( + name="Test1", + classname="some.class.name", + elapsed_sec=123.345, + stdout="I am stdout!", + stderr="I am stderr!", + ) + ], + ) + )[0] + verify_test_case( + tcs[0], + {"name": "Test1", "classname": "some.class.name", "time": ("%f" % 123.345)}, + stdout="I am stdout!", + stderr="I am stderr!", + ) + + +def test_init_disable(): + tc = Case("Disabled-Test") + tc.is_enabled = False + ts, tcs = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case(tcs[0], {"name": "Disabled-Test"}) + + +def test_init_failure_message(): + tc = Case("Failure-Message") + tc.add_failure_info("failure message") + ts, tcs = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case(tcs[0], {"name": "Failure-Message"}, failure_message="failure message") + + +def test_init_failure_output(): + tc = Case("Failure-Output") + tc.add_failure_info(output="I failed!") + ts, tcs = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case(tcs[0], {"name": "Failure-Output"}, failure_output="I failed!") + + +def test_init_failure_type(): + tc = Case("Failure-Type") + tc.add_failure_info(failure_type="com.example.Error") + ts, tcs = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case(tcs[0], {"name": "Failure-Type"}) + + tc.add_failure_info("failure message") + ts, tcs = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case( + tcs[0], {"name": "Failure-Type"}, failure_message="failure message", failure_type="com.example.Error" + ) + + +def test_init_failure(): + tc = Case("Failure-Message-and-Output") + tc.add_failure_info("failure message", "I failed!") + ts, tcs = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case( + tcs[0], + {"name": "Failure-Message-and-Output"}, + failure_message="failure message", + failure_output="I failed!", + failure_type="failure", + ) + + +def test_init_error_message(): + tc = Case("Error-Message") + tc.add_error_info("error message") + ts, tcs = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case(tcs[0], {"name": "Error-Message"}, error_message="error message") + + +def test_init_error_output(): + tc = Case("Error-Output") + tc.add_error_info(output="I errored!") + ts, tcs = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case(tcs[0], {"name": "Error-Output"}, error_output="I errored!") + + +def test_init_error_type(): + tc = Case("Error-Type") + tc.add_error_info(error_type="com.example.Error") + ts, tcs = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case(tcs[0], {"name": "Error-Type"}) + + tc.add_error_info("error message") + ts, tcs = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case(tcs[0], {"name": "Error-Type"}, error_message="error message", error_type="com.example.Error") + + +def test_init_error(): + tc = Case("Error-Message-and-Output") + tc.add_error_info("error message", "I errored!") + ts, tcs = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case( + tcs[0], + {"name": "Error-Message-and-Output"}, + error_message="error message", + error_output="I errored!", + error_type="error", + ) + + +def test_init_skipped_message(): + tc = Case("Skipped-Message") + tc.add_skipped_info("skipped message") + ts, tcs = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case(tcs[0], {"name": "Skipped-Message"}, skipped_message="skipped message") + + +def test_init_skipped_output(): + tc = Case("Skipped-Output") + tc.add_skipped_info(output="I skipped!") + ts, tcs = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case(tcs[0], {"name": "Skipped-Output"}, skipped_output="I skipped!") + + +def test_init_skipped_err_output(): + tc = Case("Skipped-Output") + tc.add_skipped_info(output="I skipped!") + tc.add_error_info(output="I skipped with an error!") + ts, tcs = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case( + tcs[0], {"name": "Skipped-Output"}, skipped_output="I skipped!", error_output="I skipped with an error!" + ) + + +def test_init_skipped(): + tc = Case("Skipped-Message-and-Output") + tc.add_skipped_info("skipped message", "I skipped!") + ts, tcs = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case( + tcs[0], {"name": "Skipped-Message-and-Output"}, skipped_message="skipped message", skipped_output="I skipped!" + ) + + +def test_init_legal_unicode_char(): + tc = Case("Failure-Message") + tc.add_failure_info(u("failure message with legal unicode char: [\x22]")) + ts, tcs = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case( + tcs[0], {"name": "Failure-Message"}, failure_message=u("failure message with legal unicode char: [\x22]") + ) + + +def test_init_illegal_unicode_char(): + tc = Case("Failure-Message") + tc.add_failure_info(u("failure message with illegal unicode char: [\x02]")) + ts, tcs = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case( + tcs[0], {"name": "Failure-Message"}, failure_message=u("failure message with illegal unicode char: []") + ) + + +def test_init_utf8(): + tc = Case( + name="Test äöü", + classname="some.class.name.äöü", + elapsed_sec=123.345, + stdout="I am stdöüt!", + stderr="I am stdärr!", + ) + tc.add_skipped_info(message="Skipped äöü", output="I skippäd!") + tc.add_error_info(message="Skipped error äöü", output="I skippäd with an error!") + test_suite = Suite("Test UTF-8", [tc]) + ts, tcs = serialize_and_read(test_suite, encoding="utf-8")[0] + verify_test_case( + tcs[0], + { + "name": decode("Test äöü", "utf-8"), + "classname": decode("some.class.name.äöü", "utf-8"), + "time": ("%f" % 123.345), + }, + stdout=decode("I am stdöüt!", "utf-8"), + stderr=decode("I am stdärr!", "utf-8"), + skipped_message=decode("Skipped äöü", "utf-8"), + skipped_output=decode("I skippäd!", "utf-8"), + error_message=decode("Skipped error äöü", "utf-8"), + error_output=decode("I skippäd with an error!", "utf-8"), + ) + + +def test_init_unicode(): + tc = Case( + name=decode("Test äöü", "utf-8"), + classname=decode("some.class.name.äöü", "utf-8"), + elapsed_sec=123.345, + stdout=decode("I am stdöüt!", "utf-8"), + stderr=decode("I am stdärr!", "utf-8"), + ) + tc.add_skipped_info(message=decode("Skipped äöü", "utf-8"), output=decode("I skippäd!", "utf-8")) + tc.add_error_info(message=decode("Skipped error äöü", "utf-8"), output=decode("I skippäd with an error!", "utf-8")) + + ts, tcs = serialize_and_read(Suite("Test Unicode", [tc]))[0] + verify_test_case( + tcs[0], + { + "name": decode("Test äöü", "utf-8"), + "classname": decode("some.class.name.äöü", "utf-8"), + "time": ("%f" % 123.345), + }, + stdout=decode("I am stdöüt!", "utf-8"), + stderr=decode("I am stdärr!", "utf-8"), + skipped_message=decode("Skipped äöü", "utf-8"), + skipped_output=decode("I skippäd!", "utf-8"), + error_message=decode("Skipped error äöü", "utf-8"), + error_output=decode("I skippäd with an error!", "utf-8"), + ) + + +def test_multiple_errors(): + """Tests multiple errors in one test case""" + tc = Case("Multiple error", allow_multiple_subelements=True) + tc.add_error_info("First error", "First error message") + (_, tcs) = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case( + tcs[0], + {"name": "Multiple error"}, + errors=[{"message": "First error", "output": "First error message", "type": "error"}], + ) + tc.add_error_info("Second error", "Second error message") + (_, tcs) = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case( + tcs[0], + {"name": "Multiple error"}, + errors=[ + {"message": "First error", "output": "First error message", "type": "error"}, + {"message": "Second error", "output": "Second error message", "type": "error"}, + ], + ) + + +def test_multiple_failures(): + """Tests multiple failures in one test case""" + tc = Case("Multiple failures", allow_multiple_subelements=True) + tc.add_failure_info("First failure", "First failure message") + (_, tcs) = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case( + tcs[0], + {"name": "Multiple failures"}, + failures=[{"message": "First failure", "output": "First failure message", "type": "failure"}], + ) + tc.add_failure_info("Second failure", "Second failure message") + (_, tcs) = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case( + tcs[0], + {"name": "Multiple failures"}, + failures=[ + {"message": "First failure", "output": "First failure message", "type": "failure"}, + {"message": "Second failure", "output": "Second failure message", "type": "failure"}, + ], + ) + + +def test_multiple_skipped(): + """Tests multiple skipped messages in one test case""" + tc = Case("Multiple skipped", allow_multiple_subelements=True) + tc.add_skipped_info("First skipped", "First skipped message") + (_, tcs) = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case( + tcs[0], {"name": "Multiple skipped"}, skipped=[{"message": "First skipped", "output": "First skipped message"}] + ) + tc.add_skipped_info("Second skipped", "Second skipped message") + (_, tcs) = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case( + tcs[0], + {"name": "Multiple skipped"}, + skipped=[ + {"message": "First skipped", "output": "First skipped message"}, + {"message": "Second skipped", "output": "Second skipped message"}, + ], + ) diff -Nru python-junit-xml-1.8/tests/test_test_suite.py python-junit-xml-1.9/tests/test_test_suite.py --- python-junit-xml-1.8/tests/test_test_suite.py 1970-01-01 00:00:00.000000000 +0000 +++ python-junit-xml-1.9/tests/test_test_suite.py 2020-02-22 20:32:08.000000000 +0000 @@ -0,0 +1,241 @@ +# -*- coding: UTF-8 -*- +from __future__ import with_statement + +import textwrap +import warnings + +import pytest +from six import PY2, StringIO + +from .asserts import verify_test_case +from junit_xml import TestCase as Case +from junit_xml import TestSuite as Suite +from junit_xml import decode, to_xml_report_string +from .serializer import serialize_and_read + + +def test_single_suite_single_test_case(): + with pytest.raises(TypeError) as excinfo: + serialize_and_read(Suite("test", Case("Test1")), to_file=True)[0] + assert str(excinfo.value) == "test_cases must be a list of test cases" + + +def test_single_suite_no_test_cases(): + properties = {"foo": "bar"} + package = "mypackage" + timestamp = 1398382805 + + ts, tcs = serialize_and_read( + Suite( + name="test", + test_cases=[], + hostname="localhost", + id=1, + properties=properties, + package=package, + timestamp=timestamp, + ), + to_file=True, + prettyprint=True, + )[0] + assert ts.tagName == "testsuite" + assert ts.attributes["package"].value == package + assert ts.attributes["timestamp"].value == str(timestamp) + assert ts.childNodes[0].childNodes[0].attributes["name"].value == "foo" + assert ts.childNodes[0].childNodes[0].attributes["value"].value == "bar" + + +def test_single_suite_no_test_cases_utf8(): + properties = {"foö": "bär"} + package = "mypäckage" + timestamp = 1398382805 + + test_suite = Suite( + name="äöü", + test_cases=[], + hostname="löcalhost", + id="äöü", + properties=properties, + package=package, + timestamp=timestamp, + ) + ts, tcs = serialize_and_read(test_suite, to_file=True, prettyprint=True, encoding="utf-8")[0] + assert ts.tagName == "testsuite" + assert ts.attributes["package"].value == decode(package, "utf-8") + assert ts.attributes["timestamp"].value == str(timestamp) + assert ts.childNodes[0].childNodes[0].attributes["name"].value == decode("foö", "utf-8") + assert ts.childNodes[0].childNodes[0].attributes["value"].value == decode("bär", "utf-8") + + +def test_single_suite_no_test_cases_unicode(): + properties = {decode("foö", "utf-8"): decode("bär", "utf-8")} + package = decode("mypäckage", "utf-8") + timestamp = 1398382805 + + ts, tcs = serialize_and_read( + Suite( + name=decode("äöü", "utf-8"), + test_cases=[], + hostname=decode("löcalhost", "utf-8"), + id=decode("äöü", "utf-8"), + properties=properties, + package=package, + timestamp=timestamp, + ), + to_file=True, + prettyprint=True, + encoding="utf-8", + )[0] + assert ts.tagName == "testsuite" + assert ts.attributes["package"].value == package + assert ts.attributes["timestamp"].value, str(timestamp) + assert ts.childNodes[0].childNodes[0].attributes["name"].value == decode("foö", "utf-8") + assert ts.childNodes[0].childNodes[0].attributes["value"].value == decode("bär", "utf-8") + + +def test_single_suite_to_file(): + ts, tcs = serialize_and_read(Suite("test", [Case("Test1")]), to_file=True)[0] + verify_test_case(tcs[0], {"name": "Test1"}) + + +def test_single_suite_to_file_prettyprint(): + ts, tcs = serialize_and_read(Suite("test", [Case("Test1")]), to_file=True, prettyprint=True)[0] + verify_test_case(tcs[0], {"name": "Test1"}) + + +def test_single_suite_prettyprint(): + ts, tcs = serialize_and_read(Suite("test", [Case("Test1")]), to_file=False, prettyprint=True)[0] + verify_test_case(tcs[0], {"name": "Test1"}) + + +def test_single_suite_to_file_no_prettyprint(): + ts, tcs = serialize_and_read(Suite("test", [Case("Test1")]), to_file=True, prettyprint=False)[0] + verify_test_case(tcs[0], {"name": "Test1"}) + + +def test_multiple_suites_to_file(): + tss = [Suite("suite1", [Case("Test1")]), Suite("suite2", [Case("Test2")])] + suites = serialize_and_read(tss, to_file=True) + + assert suites[0][0].attributes["name"].value == "suite1" + verify_test_case(suites[0][1][0], {"name": "Test1"}) + + assert suites[1][0].attributes["name"].value == "suite2" + verify_test_case(suites[1][1][0], {"name": "Test2"}) + + +def test_multiple_suites_to_string(): + tss = [Suite("suite1", [Case("Test1")]), Suite("suite2", [Case("Test2")])] + suites = serialize_and_read(tss) + + assert suites[0][0].attributes["name"].value == "suite1" + verify_test_case(suites[0][1][0], {"name": "Test1"}) + + assert suites[1][0].attributes["name"].value == "suite2" + verify_test_case(suites[1][1][0], {"name": "Test2"}) + + +def test_attribute_time(): + tss = [ + Suite( + "suite1", + [ + Case(name="Test1", classname="some.class.name", elapsed_sec=123.345), + Case(name="Test2", classname="some2.class.name", elapsed_sec=123.345), + ], + ), + Suite("suite2", [Case("Test2")]), + ] + suites = serialize_and_read(tss) + + assert suites[0][0].attributes["name"].value == "suite1" + assert suites[0][0].attributes["time"].value == "246.69" + + assert suites[1][0].attributes["name"].value == "suite2" + # here the time in testsuite is "0" even there is no attribute time for + # testcase + assert suites[1][0].attributes["time"].value == "0" + + +def test_attribute_disable(): + tc = Case("Disabled-Test") + tc.is_enabled = False + tss = [Suite("suite1", [tc])] + suites = serialize_and_read(tss) + + assert suites[0][0].attributes["disabled"].value == "1" + + +def test_stderr(): + suites = serialize_and_read(Suite(name="test", stderr="I am stderr!", test_cases=[Case(name="Test1")]))[0] + assert suites[0].getElementsByTagName("system-err")[0].firstChild.data == "I am stderr!" + + +def test_stdout_stderr(): + suites = serialize_and_read( + Suite(name="test", stdout="I am stdout!", stderr="I am stderr!", test_cases=[Case(name="Test1")]) + )[0] + assert suites[0].getElementsByTagName("system-err")[0].firstChild.data == "I am stderr!" + assert suites[0].getElementsByTagName("system-out")[0].firstChild.data == "I am stdout!" + + +def test_no_assertions(): + suites = serialize_and_read(Suite(name="test", test_cases=[Case(name="Test1")]))[0] + assert not suites[0].getElementsByTagName("testcase")[0].hasAttribute("assertions") + + +def test_assertions(): + suites = serialize_and_read(Suite(name="test", test_cases=[Case(name="Test1", assertions=5)]))[0] + assert suites[0].getElementsByTagName("testcase")[0].attributes["assertions"].value == "5" + + # @todo: add more tests for the other attributes and properties + + +def test_to_xml_string(): + test_suites = [ + Suite(name="suite1", test_cases=[Case(name="Test1")]), + Suite(name="suite2", test_cases=[Case(name="Test2")]), + ] + xml_string = to_xml_report_string(test_suites) + if PY2: + assert isinstance(xml_string, unicode) # noqa: F821 + expected_xml_string = textwrap.dedent( + """ + + + \t + \t\t + \t + \t + \t\t + \t + + """.strip( + "\n" + ) + ) + assert xml_string == expected_xml_string + + +def test_to_xml_string_test_suites_not_a_list(): + test_suites = Suite("suite1", [Case("Test1")]) + + with pytest.raises(TypeError) as excinfo: + to_xml_report_string(test_suites) + assert str(excinfo.value) == "test_suites must be a list of test suites" + + +def test_deprecated_to_xml_string(): + with warnings.catch_warnings(record=True) as w: + Suite.to_xml_string([]) + assert len(w) == 1 + assert issubclass(w[0].category, DeprecationWarning) + assert "Testsuite.to_xml_string is deprecated" in str(w[0].message) + + +def test_deprecated_to_file(): + with warnings.catch_warnings(record=True) as w: + Suite.to_file(StringIO(), []) + assert len(w) == 1 + assert issubclass(w[0].category, DeprecationWarning) + assert "Testsuite.to_file is deprecated" in str(w[0].message) diff -Nru python-junit-xml-1.8/tox.ini python-junit-xml-1.9/tox.ini --- python-junit-xml-1.8/tox.ini 2016-09-23 15:22:03.000000000 +0000 +++ python-junit-xml-1.9/tox.ini 2020-02-22 20:32:08.000000000 +0000 @@ -1,5 +1,5 @@ [tox] -envlist = py26, py27, pypy, py32, py33, py34, cover, flake8 +envlist = py27, pypy, py35, py36, py37, py38, cover, flake8 sitepackages = False [testenv] @@ -28,6 +28,7 @@ [testenv:flake8] deps = + flake8-black pytest pytest-sugar pytest-flake8 diff -Nru python-junit-xml-1.8/.travis.yml python-junit-xml-1.9/.travis.yml --- python-junit-xml-1.8/.travis.yml 1970-01-01 00:00:00.000000000 +0000 +++ python-junit-xml-1.9/.travis.yml 2020-02-22 20:32:08.000000000 +0000 @@ -0,0 +1,39 @@ +language: python + +matrix: + include: + - python: pypy + dist: xenial + sudo: true + env: TOXENV=pypy + - python: 3.5 + dist: xenial + sudo: true + env: TOXENV=py35 + - python: 3.7 + dist: xenial + sudo: true + env: TOXENV=py37 + - python: 3.8 + dist: xenial + sudo: true + env: TOXENV=py38 + +env: + - TOXENV=py27 + - TOXENV=py36 + - TOXENV=cover + - TOXENV=flake8 + +install: + - travis_retry pip install tox==3.13.2 + +script: + - travis_retry tox + +git: + depth: 1 + +notifications: + email: + - brian@kyr.us