diff --git a/.gitignore b/.gitignore index 3a5c919..f783efa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,41 +1,11 @@ -*.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 +.idea/ +__pycache__/ +.cache/ +**/*.user .coverage -.tox -nosetests.xml -coverage.xml +venv/ +*egg-info/* +htmlcov/ htmlcov/ - -# Translations -*.mo - -# Mr Developer -.mr.developer.cfg -.project -.pydevproject - -# Pycharm -.idea/** +dist +reports/ diff --git a/Makefile b/Makefile deleted file mode 100644 index 121ce12..0000000 --- a/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -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 --git a/junit_xml/__init__.py b/junit_xml/__init__.py index 0cfef29..a92cfc9 100644 --- a/junit_xml/__init__.py +++ b/junit_xml/__init__.py @@ -1,22 +1,4 @@ -#!/usr/bin/env python -# -*- coding: UTF-8 -*- -import warnings -from collections import defaultdict -import sys -import re -import xml.etree.ElementTree as ET -import xml.dom.minidom - -from six import u, iteritems, PY2 - -try: - # Python 2 - unichr -except NameError: # pragma: nocover - # Python 3 - unichr = chr - -""" +"""junit xml module Based on the understanding of what Jenkins can parse for JUnit XML files. @@ -54,258 +36,36 @@ """ +import sys +import re +from typing import List +from xml.etree import ElementTree +import xml.dom.minidom -def decode(var, encoding): - """ - If not already unicode, decode it. - """ - if PY2: - if isinstance(var, unicode): # noqa: F821 - ret = var - elif isinstance(var, str): - if encoding: - ret = var.decode(encoding) - else: - ret = unicode(var) # noqa: F821 - else: - ret = unicode(var) # noqa: F821 - else: - ret = str(var) - return ret - - -class TestSuite(object): - """ - Suite of test cases. - 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, - ): - self.name = name - if not test_cases: - test_cases = [] - try: - iter(test_cases) - except TypeError: - raise TypeError("test_cases must be a list of test cases") - self.test_cases = test_cases - self.timestamp = timestamp - self.hostname = hostname - self.id = id - self.package = package - self.file = file - self.log = log - self.url = url - self.stdout = stdout - self.stderr = stderr - self.properties = properties - - def build_xml_doc(self, encoding=None): - """ - Builds the XML document for the JUnit test suite. - Produces clean unicode strings and decodes non-unicode with the help of encoding. - @param encoding: Used to decode encoded strings. - @return: XML document with unicode string elements - """ - - # build the test suite element - test_suite_attributes = dict() - 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["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) - if self.id: - test_suite_attributes["id"] = decode(self.id, encoding) - if self.package: - test_suite_attributes["package"] = decode(self.package, encoding) - if self.timestamp: - 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) - - # add any properties - 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)} - ET.SubElement(props_element, "property", attrs) - - # add test suite stdout - if self.stdout: - stdout_element = ET.SubElement(xml_element, "system-out") - stdout_element.text = decode(self.stdout, encoding) - - # add test suite stderr - if self.stderr: - stderr_element = ET.SubElement(xml_element, "system-err") - stderr_element.text = decode(self.stderr, encoding) - - # test cases - for case in self.test_cases: - test_case_attributes = dict() - 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 - if case.elapsed_sec: - test_case_attributes["time"] = "%f" % case.elapsed_sec - if case.timestamp: - test_case_attributes["timestamp"] = decode(case.timestamp, encoding) - if case.classname: - test_case_attributes["classname"] = decode(case.classname, encoding) - if case.status: - test_case_attributes["status"] = decode(case.status, encoding) - if case.category: - test_case_attributes["class"] = decode(case.category, encoding) - if case.file: - test_case_attributes["file"] = decode(case.file, encoding) - if case.line: - test_case_attributes["line"] = decode(case.line, encoding) - if case.log: - test_case_attributes["log"] = decode(case.log, encoding) - if case.url: - test_case_attributes["url"] = decode(case.url, encoding) - - test_case_element = ET.SubElement(xml_element, "testcase", test_case_attributes) - - # failures - 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 - 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 - for skipped in case.skipped: - attrs = {"type": "skipped"} - if skipped["message"]: - attrs["message"] = decode(skipped["message"], encoding) - skipped_element = ET.Element("skipped", attrs) - if skipped["output"]: - skipped_element.text = decode(skipped["output"], encoding) - test_case_element.append(skipped_element) - - # test stdout - if case.stdout: - stdout_element = ET.Element("system-out") - stdout_element.text = decode(case.stdout, encoding) - test_case_element.append(stdout_element) - - # test stderr - if case.stderr: - stderr_element = ET.Element("system-err") - stderr_element.text = decode(case.stderr, encoding) - test_case_element.append(stderr_element) - - return xml_element - - @staticmethod - def to_xml_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 - """ - 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. - """ - 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) +from junit_xml.root_element import TestSuite, build_xml_root -def to_xml_report_string(test_suites, prettyprint=True, encoding=None): +def to_xml_report_string(test_suites: List[TestSuite], prettyprint=True, encoding=None): """ Returns the string representation of the JUnit XML document. + @param test_suites: input for xml string generation + @param prettyprint: flag to request formatted output, default=True @param encoding: The encoding of the input. - @return: unicode string + @return: xml unicode string """ + xml_element = build_xml_root(test_suites) - 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) + xml_string = ElementTree.tostring(xml_element, encoding=encoding) # is encoded now - xml_string = _clean_illegal_xml_chars(xml_string.decode(encoding or "utf-8")) + 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 + # 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) @@ -313,20 +73,12 @@ def to_xml_report_string(test_suites, prettyprint=True, encoding=None): 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) - - -def _clean_illegal_xml_chars(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 + @see: http://stackoverflow.com/questions/1707890/ + fast-way-to-filter-illegal-xml-unicode-chars-in-python """ illegal_unichrs = [ @@ -355,114 +107,10 @@ def _clean_illegal_xml_chars(string_to_clean): (0x10FFFE, 0x10FFFF), ] - illegal_ranges = ["%s-%s" % (unichr(low), unichr(high)) for (low, high) in illegal_unichrs if low < sys.maxunicode] + illegal_ranges = [ + f"{chr(low)}-{chr(high)}" for (low, high) in illegal_unichrs if low < sys.maxunicode + ] - illegal_xml_re = re.compile(u("[%s]") % u("").join(illegal_ranges)) + joined = "".join(illegal_ranges) + illegal_xml_re = re.compile(f"[{joined}]") 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, - url=None, - allow_multiple_subelements=False, - ): - self.name = name - self.assertions = assertions - self.elapsed_sec = elapsed_sec - self.timestamp = timestamp - self.classname = classname - self.status = status - self.category = category - self.file = file - self.line = line - self.log = log - self.url = url - self.stdout = stdout - self.stderr = stderr - - self.is_enabled = True - 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""" - 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""" - 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""" - 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 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 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 len(self.skipped) > 0 diff --git a/pyproject.toml b/pyproject.toml index 55ec8d7..833f042 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,9 @@ +[build-system] +requires = [ + "setuptools>=42", + "wheel" +] +build-backend = "setuptools.build_meta" + [tool.black] -line-length = 120 +line-length = 100 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index ffe2fce..8ce6ae1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,7 @@ -six +pylint==2.13.8 +pytest~=7.1.2 +flake8-black +pytest-sugar==0.9.4 +pytest-cov==3.0.0 +pytest-flake8 +icecream==2.1.2 diff --git a/requirements_dev.txt b/requirements_dev.txt deleted file mode 100644 index 69269df..0000000 --- a/requirements_dev.txt +++ /dev/null @@ -1,4 +0,0 @@ --r requirements.txt -flake8-black -pytest-sugar -pytest-flake8 diff --git a/setup.cfg b/setup.cfg index 3fcc1e7..f3b3562 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,3 @@ [flake8] max-line-length = 120 -exclude = .git,.tox,build,dist,venv - -[wheel] -universal = 1 +exclude = .git,.tox,build,dist,venv \ No newline at end of file diff --git a/setup.py b/setup.py index 8aba068..ce6b93d 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ -#!/usr/bin/env python -from setuptools import setup, find_packages import os +from setuptools import setup, find_packages + def read(fname): return open(os.path.join(os.path.dirname(__file__), fname)).read() @@ -9,14 +9,14 @@ def read(fname): setup( name="junit-xml", - author="Brian Beyer", - author_email="brian@kyr.us", - url="https://github.com/kyrus/python-junit-xml", + author="Tikani", + author_email="", + url="https://github.com/gitiJumi/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", + version="2.2", classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", @@ -28,5 +28,5 @@ def read(fname): "Topic :: Software Development :: Build Tools", "Topic :: Software Development :: Testing", ], - install_requires=["six"], + python_requires=">=3.8", ) diff --git a/tests/asserts.py b/tests/asserts.py deleted file mode 100644 index 82c48b4..0000000 --- a/tests/asserts.py +++ /dev/null @@ -1,83 +0,0 @@ -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 --git a/tests/serializer.py b/tests/serializer.py index 8a06200..5b5d24e 100644 --- a/tests/serializer.py +++ b/tests/serializer.py @@ -1,52 +1,32 @@ -import codecs -import os -import tempfile +import logging from xml.dom import minidom -from six import PY2 +from junit_xml import to_xml_report_string -from junit_xml import to_xml_report_file, to_xml_report_string +logger = logging.getLogger("Serializer") -def serialize_and_read(test_suites, to_file=False, prettyprint=False, encoding=None): +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) + + +def read_testsuites_root(test_suites, prettyprint=False): """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: + returning => (test suite element, list of test case elements)""" + if not isinstance(test_suites, list): 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) + xml_string = to_xml_report_string(test_suites, prettyprint=prettyprint, encoding="utf-8") + logger.debug("Serialized XML to string:\n%s", xml_string) + + xmldoc = minidom.parseString(xml_string) 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 + return xmldoc.getElementsByTagName("testsuites") diff --git a/tests/test_test_case.py b/tests/test_test_case.py index b9817de..b3d5b24 100644 --- a/tests/test_test_case.py +++ b/tests/test_test_case.py @@ -1,324 +1,193 @@ # -*- 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"), - ) + +import pytest + +from junit_xml.root_element import TestSuite as Suite +from junit_xml.single_entry import TestCase as Case +from .serializer import read_testsuites_root + +sample_testsuite_name = "Banane" + +sample_testcase_name = "Test1" +sample_classname = "some.class.name" +sample_time = 123.345 + +sample_failure_msg = "failure message: something bad happened" +sample_failure_output = "I failed!" + +sample_error_message = "error message" +sample_error_output = "I errored!" + +sample_skipped_message = "skipped message" +sample_skipped_output = "I skipped!" + + +def read_testcases(test_suites, prettyprint=False): + suites = read_testsuites_root(test_suites, prettyprint)[0] + return suites.getElementsByTagName("testsuite")[0].getElementsByTagName("testcase") + + +@pytest.mark.parametrize("_input, expected_output", + [ + (Case(sample_testcase_name), + {"name": sample_testcase_name}), + + (Case(name=sample_testcase_name, classname=sample_classname), + {"name": sample_testcase_name, "classname": sample_classname}), + + (Case(name=sample_testcase_name, classname=sample_classname, + time=sample_time), + {"name": sample_testcase_name, "classname": sample_classname, + "time": "%.3f" % sample_time}) + ]) +def test_report_passing_test(_input, expected_output): + test_case_element = read_testcases(Suite(sample_testsuite_name, [_input])) + + for _key, _value in expected_output.items(): + assert test_case_element[0].attributes[_key].value == _value, \ + f"key: {_key} value: {_value} vs. {test_case_element[0].attributes[_key].value}" + + for act_key in test_case_element[0].attributes.keys(): + assert act_key in expected_output.keys() + + _failures = test_case_element[0].getElementsByTagName("failure") + _errors = test_case_element[0].getElementsByTagName("error") + _skipped = test_case_element[0].getElementsByTagName("skipped") + + assert not _failures + assert not _errors + assert not _skipped + + +@pytest.mark.parametrize("_input_error_message,_input_error_output,_default_type,_error_type", + [(sample_error_message, sample_error_output, True, "error"), + (sample_error_message, sample_error_output, False, "com.example.Error") + ]) +def test_error_message_and_output_default_error_type(_input_error_message, _input_error_output, + _default_type, _error_type): + tc = Case(sample_testcase_name) + if _default_type: + tc.add_error_info(_input_error_message, _input_error_output) + else: + tc.add_error_info(_input_error_message, _input_error_output, _error_type) + + tcs = read_testcases(Suite(sample_testsuite_name, [tc])) + + _errors = tcs[0].getElementsByTagName("error") + assert _errors[0].attributes["message"].value == _input_error_message + assert _errors[0].attributes["type"].value == _error_type + assert _errors[0].firstChild.nodeValue.strip() == _input_error_output + + +def test_skipped_message_only(): + tc = Case(sample_testcase_name) + + tc.add_skipped_info(sample_skipped_message) + + tcs = read_testcases(Suite(sample_testsuite_name, [tc])) + + _skipped = tcs[0].getElementsByTagName("skipped") + assert _skipped[0].attributes["message"].value == sample_skipped_message + assert _skipped[0].attributes["type"].value == "skipped" + assert not _skipped[0].firstChild + + +def test_skipped_message_and_output(): + tc = Case(sample_testcase_name) + tc.add_skipped_info(sample_skipped_message, sample_skipped_output) + + tcs = read_testcases(Suite(sample_testsuite_name, [tc])) + + _skipped = tcs[0].getElementsByTagName("skipped") + + assert _skipped[0].attributes["message"].value == sample_skipped_message + assert _skipped[0].attributes["type"].value == "skipped" + assert _skipped[0].firstChild.nodeValue.strip() == sample_skipped_output + + +def test_mixed_skipped_and_err_output(): + tc = Case(sample_testcase_name) + tc.add_skipped_info(output=sample_skipped_output) + tc.add_error_info(output=sample_error_output) + + tcs = read_testcases(Suite(sample_testsuite_name, [tc])) + + _skipped = tcs[0].getElementsByTagName("skipped") + assert _skipped[0].firstChild.nodeValue.strip() == sample_skipped_output + assert _skipped[0].attributes["type"].value == "skipped" + + _errors = tcs[0].getElementsByTagName("error") + assert _errors[0].firstChild.nodeValue.strip() == sample_error_output + assert _errors[0].attributes["type"].value == "error" 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"}, - ], - ) + tc = Case(sample_testcase_name, allow_multiple_subelements=True) + tc.add_error_info(sample_error_message, sample_error_output) + + tcs = read_testcases(Suite(sample_testsuite_name, [tc])) + + _errors = tcs[0].getElementsByTagName("error") + assert _errors[0].attributes["message"].value == sample_error_message + assert _errors[0].attributes["type"].value == "error" + assert _errors[0].firstChild.nodeValue.strip() == sample_error_output + + error_entry = ("Second error message", "Second error output") + tc.add_error_info(error_entry[0], error_entry[1]) + + tcs = read_testcases(Suite(sample_testsuite_name, [tc])) + + _errors = tcs[0].getElementsByTagName("error") + assert len(_errors) == 2 + assert _errors[1].attributes["message"].value == error_entry[0] + assert _errors[1].attributes["type"].value == "error" + assert _errors[1].firstChild.nodeValue.strip() == error_entry[1] 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"}, - ], - ) + tc = Case(sample_testcase_name, allow_multiple_subelements=True) + tc.add_failure_info(sample_failure_msg, sample_failure_output) + + tcs = read_testcases(Suite(sample_testsuite_name, [tc])) + + _failures = tcs[0].getElementsByTagName("failure") + assert _failures[0].attributes["message"].value == sample_failure_msg + assert _failures[0].attributes["type"].value == "failure" + assert _failures[0].firstChild.nodeValue.strip() == sample_failure_output + + second_failure_entry = ("Second failure message", "Second failure output") + + tc.add_failure_info(second_failure_entry[0], second_failure_entry[1]) + + tcs = read_testcases(Suite(sample_testsuite_name, [tc])) + + _failures = tcs[0].getElementsByTagName("failure") + assert len(_failures) == 2 + assert _failures[1].attributes["message"].value == second_failure_entry[0] + assert _failures[1].attributes["type"].value == "failure" + assert _failures[1].firstChild.nodeValue.strip() == second_failure_entry[1] 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"}, - ], - ) + testcase_under_test = Case(sample_testcase_name, allow_multiple_subelements=True) + testcase_under_test.add_skipped_info(sample_skipped_message, sample_skipped_output) + + tcs = read_testcases(Suite(sample_testsuite_name, [testcase_under_test])) + + _skipped = tcs[0].getElementsByTagName("skipped") + assert _skipped[0].firstChild.nodeValue.strip() == sample_skipped_output + assert _skipped[0].attributes["message"].value == sample_skipped_message + + sample_skip_2 = ("Second skipped message", "Second skipped output") + + testcase_under_test.add_skipped_info(sample_skip_2[0], sample_skip_2[1]) + + tcs = read_testcases(Suite(sample_testsuite_name, [testcase_under_test])) + + _skipped = tcs[0].getElementsByTagName("skipped") + assert len(_skipped) == 2 + assert _skipped[1].firstChild.nodeValue.strip() == sample_skip_2[1] + assert _skipped[1].attributes["message"].value == sample_skip_2[0] diff --git a/tests/test_test_suite.py b/tests/test_test_suite.py index a6591fe..fee5ed8 100644 --- a/tests/test_test_suite.py +++ b/tests/test_test_suite.py @@ -1,241 +1,167 @@ # -*- coding: UTF-8 -*- -from __future__ import with_statement -import textwrap -import warnings +from unittest import mock 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 +from junit_xml import to_xml_report_string +from junit_xml.root_element import TestSuite +from junit_xml.single_entry import TestCase +from .serializer import read_testsuites_root +sample_testsuite_name = "suite" -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" - +sample_hostname = "localhost" +sample_id = 42 -def test_single_suite_no_test_cases(): - properties = {"foo": "bar"} - package = "mypackage" - timestamp = 1398382805 +sample_property_entry = ("foo", "bar") +sample_package = "sample.mypackage" - 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") +sample_timestamp = 1398382805 +suite_name_1 = "suite1" +testcase_name_1 = "Test1" -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 +suite_name_2 = "suite2" +testcase_name_2 = "Test2" - 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") +sample_testcase_name = "SomeTest" +generated_timestamp = "2022-05-12T09:19:42" -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 read_first_testsuite(test_suites, prettyprint=False): + res = read_testsuites_root(test_suites, prettyprint)[0].getElementsByTagName("testsuite")[0] + return res -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_testsuite_wrong_testcase_type(): + with pytest.raises(TypeError) as exc_info: + TestSuite("test", TestCase("Test1")) + assert str(exc_info.value) == "test_cases must be a list of test cases" -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"}) +def test_single_testsuite_no_testcases(): + ts = read_first_testsuite( + TestSuite( + name=sample_testsuite_name, + test_cases=[], + hostname=sample_hostname, + properties={sample_property_entry[0]: sample_property_entry[1]}, + package=sample_package, + timestamp=sample_timestamp, + ), + prettyprint=True, + ) - assert suites[1][0].attributes["name"].value == "suite2" - verify_test_case(suites[1][1][0], {"name": "Test2"}) + assert ts.tagName == "testsuite" + assert ts.attributes["name"].value == sample_testsuite_name + assert ts.attributes["hostname"].value == sample_hostname + + assert ts.attributes["package"].value == sample_package + assert ts.attributes["timestamp"].value == str(sample_timestamp) + assert ts.childNodes[0].childNodes[0].attributes["name"].value == sample_property_entry[0] + assert ts.childNodes[0].childNodes[0].attributes["value"].value == sample_property_entry[1] + + +exp_output_pretty_p = '\n' +exp_output_pretty_p += '\n' +exp_output_pretty_p += f'\t\t\n' +exp_output_pretty_p += '\t\n' +exp_output_pretty_p += '\n' + +exp_output_no_pretty = '\n' +expected_xml_string += '\t\t\n' +expected_xml_string += '\t\n' +expected_xml_string += '\t\n' +expected_xml_string += '\t\t\n' +expected_xml_string += '\t\n' +expected_xml_string += '\n' + + +@pytest.mark.parametrize("pretty_print, expected_output", + [(True, exp_output_pretty_p), + (False, exp_output_no_pretty)]) +@mock.patch("junit_xml.root_element.get_new_id", create=True) +@mock.patch("junit_xml.root_element.get_current_timestamp", create=True) +def test_single_suite_with_prettyprint(time_provider_mock: mock.Mock, id_provider: mock.Mock, + pretty_print, expected_output): + time_provider_mock.return_value = generated_timestamp + id_provider.return_value = "42" + + input_testsuite = TestSuite(sample_testsuite_name, [TestCase(sample_testcase_name)]) + + act_output = to_xml_report_string([input_testsuite], prettyprint=pretty_print, encoding="utf-8") + + assert act_output == expected_output + + +@mock.patch("junit_xml.root_element.get_new_id", create=True) +@mock.patch("junit_xml.root_element.get_current_timestamp", create=True) +def test_to_xml_string(time_provider_mock: mock.Mock, id_provider: mock.Mock): + time_provider_mock.return_value = generated_timestamp + id_provider.return_value = "1" -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")]), + test_suites = [ + TestSuite(name="suite1", test_cases=[TestCase(name="Test1")]), + TestSuite(name="suite2", test_cases=[TestCase(name="Test2")]), ] - suites = serialize_and_read(tss) - - assert suites[0][0].attributes["name"].value == "suite1" - assert suites[0][0].attributes["time"].value == "246.69" + xml_string = to_xml_report_string(test_suites) - 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" + assert xml_string == expected_xml_string def test_attribute_disable(): - tc = Case("Disabled-Test") + tc = TestCase("Disabled-Test") tc.is_enabled = False - tss = [Suite("suite1", [tc])] - suites = serialize_and_read(tss) - - assert suites[0][0].attributes["disabled"].value == "1" + tss = [TestSuite(sample_testsuite_name, [tc])] + # res = to_xml_report_string(tss) + # print(f"\n{res}") + suite = read_testsuites_root(tss)[0].getElementsByTagName("testsuite") - -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!" + assert suite[0].attributes["disabled"].value == "1" 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!" + suite = read_first_testsuite( + TestSuite(name=sample_testsuite_name, stdout="I am stdout!", + stderr="I am stderr!", test_cases=[TestCase(name=sample_testcase_name)]) + ) + assert suite.getElementsByTagName("system-err")[0].firstChild.data == "I am stderr!" + assert suite.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") + suite = read_first_testsuite( + TestSuite(name=sample_testsuite_name, test_cases=[TestCase(name=sample_testcase_name)])) + assert not suite.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" + suite = read_first_testsuite( + TestSuite(name=sample_testsuite_name, + test_cases=[TestCase(name=sample_testcase_name, assertions=5)])) - # @todo: add more tests for the other attributes and properties + assert suite.getElementsByTagName("testcase")[0].attributes["assertions"].value == "5" -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")]) +def test_to_xml_string_wrong_input_type(): + test_suites = TestSuite(sample_testsuite_name, [TestCase(sample_testcase_name)]) 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 --git a/tox.ini b/tox.ini index 3fdb993..6ebd1c6 100644 --- a/tox.ini +++ b/tox.ini @@ -1,39 +1,58 @@ [tox] -envlist = py27, pypy, py35, py36, py37, py38, cover, flake8 +envlist = dev-pylint,dev,black-check +#isolated_build = true sitepackages = False [testenv] +basepython = python3 +# "usedevelop = true" is short cut to "python setup.py develop" +#usedevelop = true deps = - pytest - pytest-sugar - six + black: black + dev: -rrequirements.txt + +[testenv:dev-pylint] +whitelist_externals = cat commands = - py.test \ - --junitxml={envlogdir}/junit-{envname}.xml \ - {posargs} + pylint --exit-zero --output=pylint.log junit_xml/ + cat pylint.log -[testenv:cover] -deps = - pytest - pytest-sugar - pytest-cov - six +[testenv:dev] +commands = + pytest -s --junitxml=reports/summary.xml --cov=junit_xml \ + --cov-report=term-missing --cov-report=xml --cov-report=html -W ignore {posargs} + coverage xml + +[testenv:black-format] +changedir = {toxinidir}/junit_xml +commands = black --line-length 100 . + +[testenv:black-check] +changedir = {toxinidir}/junit_xml +commands = black --check --diff --line-length 100 . + +[testenv:dev-flake8] commands = - py.test \ - --cov=junit_xml \ - --cov-report=term-missing \ - --cov-report=xml \ - --cov-report=html \ - {posargs} - -[testenv:flake8] + pytest --flake8 {posargs} + +# Release tooling +[testenv:do-wheel] +skip_install = true deps = - flake8-black - pytest - pytest-sugar - pytest-flake8 - six + wheel + setuptools + build commands = - py.test \ - --flake8 \ - {posargs} + python -m build . + +[tool:pytest] +testpaths = tests +norecursedirs = .git .tox build dist venv + +[coverage:paths] +source = + src + */site-packages + +[tool:black] +line-length = 100 \ No newline at end of file