2018-06-27 03:17:36 +08:00
|
|
|
import py
|
|
|
|
from _pytest._code.code import TerminalRepr
|
|
|
|
|
|
|
|
|
|
|
|
def getslaveinfoline(node):
|
|
|
|
try:
|
|
|
|
return node._slaveinfocache
|
|
|
|
except AttributeError:
|
|
|
|
d = node.slaveinfo
|
|
|
|
ver = "%s.%s.%s" % d["version_info"][:3]
|
|
|
|
node._slaveinfocache = s = "[%s] %s -- Python %s %s" % (
|
2018-06-27 04:10:26 +08:00
|
|
|
d["id"],
|
|
|
|
d["sysplatform"],
|
|
|
|
ver,
|
|
|
|
d["executable"],
|
2018-06-27 03:17:36 +08:00
|
|
|
)
|
|
|
|
return s
|
|
|
|
|
|
|
|
|
|
|
|
class BaseReport(object):
|
|
|
|
def __init__(self, **kw):
|
|
|
|
self.__dict__.update(kw)
|
|
|
|
|
|
|
|
def toterminal(self, out):
|
|
|
|
if hasattr(self, "node"):
|
|
|
|
out.line(getslaveinfoline(self.node))
|
|
|
|
|
|
|
|
longrepr = self.longrepr
|
|
|
|
if longrepr is None:
|
|
|
|
return
|
|
|
|
|
|
|
|
if hasattr(longrepr, "toterminal"):
|
|
|
|
longrepr.toterminal(out)
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
out.line(longrepr)
|
|
|
|
except UnicodeEncodeError:
|
|
|
|
out.line("<unprintable longrepr>")
|
|
|
|
|
|
|
|
def get_sections(self, prefix):
|
|
|
|
for name, content in self.sections:
|
|
|
|
if name.startswith(prefix):
|
|
|
|
yield prefix, content
|
|
|
|
|
|
|
|
@property
|
|
|
|
def longreprtext(self):
|
|
|
|
"""
|
|
|
|
Read-only property that returns the full string representation
|
|
|
|
of ``longrepr``.
|
|
|
|
|
|
|
|
.. versionadded:: 3.0
|
|
|
|
"""
|
|
|
|
tw = py.io.TerminalWriter(stringio=True)
|
|
|
|
tw.hasmarkup = False
|
|
|
|
self.toterminal(tw)
|
|
|
|
exc = tw.stringio.getvalue()
|
|
|
|
return exc.strip()
|
|
|
|
|
|
|
|
@property
|
|
|
|
def caplog(self):
|
|
|
|
"""Return captured log lines, if log capturing is enabled
|
|
|
|
|
|
|
|
.. versionadded:: 3.5
|
|
|
|
"""
|
|
|
|
return "\n".join(
|
|
|
|
content for (prefix, content) in self.get_sections("Captured log")
|
|
|
|
)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def capstdout(self):
|
|
|
|
"""Return captured text from stdout, if capturing is enabled
|
|
|
|
|
|
|
|
.. versionadded:: 3.0
|
|
|
|
"""
|
|
|
|
return "".join(
|
|
|
|
content for (prefix, content) in self.get_sections("Captured stdout")
|
|
|
|
)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def capstderr(self):
|
|
|
|
"""Return captured text from stderr, if capturing is enabled
|
|
|
|
|
|
|
|
.. versionadded:: 3.0
|
|
|
|
"""
|
|
|
|
return "".join(
|
|
|
|
content for (prefix, content) in self.get_sections("Captured stderr")
|
|
|
|
)
|
|
|
|
|
|
|
|
passed = property(lambda x: x.outcome == "passed")
|
|
|
|
failed = property(lambda x: x.outcome == "failed")
|
|
|
|
skipped = property(lambda x: x.outcome == "skipped")
|
|
|
|
|
|
|
|
@property
|
|
|
|
def fspath(self):
|
|
|
|
return self.nodeid.split("::")[0]
|
|
|
|
|
|
|
|
|
|
|
|
class TestReport(BaseReport):
|
|
|
|
""" Basic test report object (also used for setup and teardown calls if
|
|
|
|
they fail).
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
nodeid,
|
|
|
|
location,
|
|
|
|
keywords,
|
|
|
|
outcome,
|
|
|
|
longrepr,
|
|
|
|
when,
|
|
|
|
sections=(),
|
|
|
|
duration=0,
|
2018-09-26 20:02:14 +08:00
|
|
|
user_properties=None,
|
2018-06-27 03:17:36 +08:00
|
|
|
**extra
|
|
|
|
):
|
|
|
|
#: normalized collection node id
|
|
|
|
self.nodeid = nodeid
|
|
|
|
|
|
|
|
#: a (filesystempath, lineno, domaininfo) tuple indicating the
|
|
|
|
#: actual location of a test item - it might be different from the
|
|
|
|
#: collected one e.g. if a method is inherited from a different module.
|
|
|
|
self.location = location
|
|
|
|
|
|
|
|
#: a name -> value dictionary containing all keywords and
|
|
|
|
#: markers associated with a test invocation.
|
|
|
|
self.keywords = keywords
|
|
|
|
|
|
|
|
#: test outcome, always one of "passed", "failed", "skipped".
|
|
|
|
self.outcome = outcome
|
|
|
|
|
|
|
|
#: None or a failure representation.
|
|
|
|
self.longrepr = longrepr
|
|
|
|
|
|
|
|
#: one of 'setup', 'call', 'teardown' to indicate runtest phase.
|
|
|
|
self.when = when
|
|
|
|
|
|
|
|
#: user properties is a list of tuples (name, value) that holds user
|
|
|
|
#: defined properties of the test
|
2018-09-26 20:02:14 +08:00
|
|
|
if user_properties is None:
|
|
|
|
user_properties = []
|
|
|
|
elif not isinstance(user_properties, list):
|
|
|
|
# This used to be a tuple; so cast for backwards-compatibility
|
|
|
|
user_properties = list(user_properties)
|
2018-06-27 03:17:36 +08:00
|
|
|
self.user_properties = user_properties
|
|
|
|
|
|
|
|
#: list of pairs ``(str, str)`` of extra information which needs to
|
|
|
|
#: marshallable. Used by pytest to add captured text
|
|
|
|
#: from ``stdout`` and ``stderr``, but may be used by other plugins
|
|
|
|
#: to add arbitrary information to reports.
|
|
|
|
self.sections = list(sections)
|
|
|
|
|
|
|
|
#: time it took to run just the test
|
|
|
|
self.duration = duration
|
|
|
|
|
|
|
|
self.__dict__.update(extra)
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return "<TestReport %r when=%r outcome=%r>" % (
|
2018-06-27 04:10:26 +08:00
|
|
|
self.nodeid,
|
|
|
|
self.when,
|
|
|
|
self.outcome,
|
2018-06-27 03:17:36 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class TeardownErrorReport(BaseReport):
|
|
|
|
outcome = "failed"
|
|
|
|
when = "teardown"
|
|
|
|
|
|
|
|
def __init__(self, longrepr, **extra):
|
|
|
|
self.longrepr = longrepr
|
|
|
|
self.sections = []
|
|
|
|
self.__dict__.update(extra)
|
|
|
|
|
|
|
|
|
|
|
|
class CollectReport(BaseReport):
|
|
|
|
def __init__(self, nodeid, outcome, longrepr, result, sections=(), **extra):
|
|
|
|
self.nodeid = nodeid
|
|
|
|
self.outcome = outcome
|
|
|
|
self.longrepr = longrepr
|
|
|
|
self.result = result or []
|
|
|
|
self.sections = list(sections)
|
|
|
|
self.__dict__.update(extra)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def location(self):
|
|
|
|
return (self.fspath, None, self.fspath)
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return "<CollectReport %r lenresult=%s outcome=%r>" % (
|
2018-06-27 04:10:26 +08:00
|
|
|
self.nodeid,
|
|
|
|
len(self.result),
|
|
|
|
self.outcome,
|
2018-06-27 03:17:36 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class CollectErrorRepr(TerminalRepr):
|
|
|
|
def __init__(self, msg):
|
|
|
|
self.longrepr = msg
|
|
|
|
|
|
|
|
def toterminal(self, out):
|
|
|
|
out.line(self.longrepr, red=True)
|