summaryrefslogtreecommitdiff
path: root/lib/spack/external/_pytest/junitxml.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/spack/external/_pytest/junitxml.py')
-rw-r--r--lib/spack/external/_pytest/junitxml.py74
1 files changed, 57 insertions, 17 deletions
diff --git a/lib/spack/external/_pytest/junitxml.py b/lib/spack/external/_pytest/junitxml.py
index 317382e637..7fb40dc354 100644
--- a/lib/spack/external/_pytest/junitxml.py
+++ b/lib/spack/external/_pytest/junitxml.py
@@ -4,9 +4,11 @@
Based on initial code from Ross Lawley.
+
+Output conforms to https://github.com/jenkinsci/xunit-plugin/blob/master/
+src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd
"""
-# Output conforms to https://github.com/jenkinsci/xunit-plugin/blob/master/
-# src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd
+from __future__ import absolute_import, division, print_function
import functools
import py
@@ -15,6 +17,7 @@ import re
import sys
import time
import pytest
+from _pytest import nodes
from _pytest.config import filename_arg
# Python 2.X and 3.X compatibility
@@ -105,6 +108,8 @@ class _NodeReporter(object):
}
if testreport.location[1] is not None:
attrs["line"] = testreport.location[1]
+ if hasattr(testreport, "url"):
+ attrs["url"] = testreport.url
self.attrs = attrs
def to_xml(self):
@@ -119,7 +124,7 @@ class _NodeReporter(object):
node = kind(data, message=message)
self.append(node)
- def _write_captured_output(self, report):
+ def write_captured_output(self, report):
for capname in ('out', 'err'):
content = getattr(report, 'capstd' + capname)
if content:
@@ -128,7 +133,6 @@ class _NodeReporter(object):
def append_pass(self, report):
self.add_stats('passed')
- self._write_captured_output(report)
def append_failure(self, report):
# msg = str(report.longrepr.reprtraceback.extraline)
@@ -147,7 +151,6 @@ class _NodeReporter(object):
fail = Junit.failure(message=message)
fail.append(bin_xml_escape(report.longrepr))
self.append(fail)
- self._write_captured_output(report)
def append_collect_error(self, report):
# msg = str(report.longrepr.reprtraceback.extraline)
@@ -165,7 +168,6 @@ class _NodeReporter(object):
msg = "test setup failure"
self._add_simple(
Junit.error, msg, report.longrepr)
- self._write_captured_output(report)
def append_skipped(self, report):
if hasattr(report, "wasxfail"):
@@ -180,7 +182,7 @@ class _NodeReporter(object):
Junit.skipped("%s:%s: %s" % (filename, lineno, skipreason),
type="pytest.skip",
message=skipreason))
- self._write_captured_output(report)
+ self.write_captured_output(report)
def finalize(self):
data = self.to_xml().unicode(indent=0)
@@ -225,13 +227,14 @@ def pytest_addoption(parser):
metavar="str",
default=None,
help="prepend prefix to classnames in junit-xml output")
+ parser.addini("junit_suite_name", "Test suite name for JUnit report", default="pytest")
def pytest_configure(config):
xmlpath = config.option.xmlpath
# prevent opening xmllog on slave nodes (xdist)
if xmlpath and not hasattr(config, 'slaveinput'):
- config._xml = LogXML(xmlpath, config.option.junitprefix)
+ config._xml = LogXML(xmlpath, config.option.junitprefix, config.getini("junit_suite_name"))
config.pluginmanager.register(config._xml)
@@ -250,7 +253,7 @@ def mangle_test_address(address):
except ValueError:
pass
# convert file path to dotted path
- names[0] = names[0].replace("/", '.')
+ names[0] = names[0].replace(nodes.SEP, '.')
names[0] = _py_ext_re.sub("", names[0])
# put any params back
names[-1] += possible_open_bracket + params
@@ -258,10 +261,11 @@ def mangle_test_address(address):
class LogXML(object):
- def __init__(self, logfile, prefix):
+ def __init__(self, logfile, prefix, suite_name="pytest"):
logfile = os.path.expanduser(os.path.expandvars(logfile))
self.logfile = os.path.normpath(os.path.abspath(logfile))
self.prefix = prefix
+ self.suite_name = suite_name
self.stats = dict.fromkeys([
'error',
'passed',
@@ -271,6 +275,9 @@ class LogXML(object):
self.node_reporters = {} # nodeid -> _NodeReporter
self.node_reporters_ordered = []
self.global_properties = []
+ # List of reports that failed on call but teardown is pending.
+ self.open_reports = []
+ self.cnt_double_fail_tests = 0
def finalize(self, report):
nodeid = getattr(report, 'nodeid', report)
@@ -330,14 +337,33 @@ class LogXML(object):
-> teardown node2
-> teardown node1
"""
+ close_report = None
if report.passed:
if report.when == "call": # ignore setup/teardown
reporter = self._opentestcase(report)
reporter.append_pass(report)
elif report.failed:
+ if report.when == "teardown":
+ # The following vars are needed when xdist plugin is used
+ report_wid = getattr(report, "worker_id", None)
+ report_ii = getattr(report, "item_index", None)
+ close_report = next(
+ (rep for rep in self.open_reports
+ if (rep.nodeid == report.nodeid and
+ getattr(rep, "item_index", None) == report_ii and
+ getattr(rep, "worker_id", None) == report_wid
+ )
+ ), None)
+ if close_report:
+ # We need to open new testcase in case we have failure in
+ # call and error in teardown in order to follow junit
+ # schema
+ self.finalize(close_report)
+ self.cnt_double_fail_tests += 1
reporter = self._opentestcase(report)
if report.when == "call":
reporter.append_failure(report)
+ self.open_reports.append(report)
else:
reporter.append_error(report)
elif report.skipped:
@@ -345,7 +371,20 @@ class LogXML(object):
reporter.append_skipped(report)
self.update_testcase_duration(report)
if report.when == "teardown":
+ reporter = self._opentestcase(report)
+ reporter.write_captured_output(report)
self.finalize(report)
+ report_wid = getattr(report, "worker_id", None)
+ report_ii = getattr(report, "item_index", None)
+ close_report = next(
+ (rep for rep in self.open_reports
+ if (rep.nodeid == report.nodeid and
+ getattr(rep, "item_index", None) == report_ii and
+ getattr(rep, "worker_id", None) == report_wid
+ )
+ ), None)
+ if close_report:
+ self.open_reports.remove(close_report)
def update_testcase_duration(self, report):
"""accumulates total duration for nodeid from given report and updates
@@ -378,14 +417,15 @@ class LogXML(object):
suite_stop_time = time.time()
suite_time_delta = suite_stop_time - self.suite_start_time
- numtests = self.stats['passed'] + self.stats['failure'] + self.stats['skipped'] + self.stats['error']
-
+ numtests = (self.stats['passed'] + self.stats['failure'] +
+ self.stats['skipped'] + self.stats['error'] -
+ self.cnt_double_fail_tests)
logfile.write('<?xml version="1.0" encoding="utf-8"?>')
logfile.write(Junit.testsuite(
self._get_global_properties_node(),
[x.to_xml() for x in self.node_reporters_ordered],
- name="pytest",
+ name=self.suite_name,
errors=self.stats['error'],
failures=self.stats['failure'],
skips=self.stats['skipped'],
@@ -405,9 +445,9 @@ class LogXML(object):
"""
if self.global_properties:
return Junit.properties(
- [
- Junit.property(name=name, value=value)
- for name, value in self.global_properties
- ]
+ [
+ Junit.property(name=name, value=value)
+ for name, value in self.global_properties
+ ]
)
return ''