move test reports to own file

This commit is contained in:
Ronny Pfannschmidt 2018-06-26 21:17:36 +02:00
parent 6b239263da
commit cc6eb9f83c
4 changed files with 239 additions and 200 deletions

192
src/_pytest/reports.py Normal file
View File

@ -0,0 +1,192 @@
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" % (
d["id"], d["sysplatform"], ver, d["executable"]
)
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,
user_properties=(),
**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
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>" % (
self.nodeid, self.when, self.outcome
)
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>" % (
self.nodeid, len(self.result), self.outcome
)
class CollectErrorRepr(TerminalRepr):
def __init__(self, msg):
self.longrepr = msg
def toterminal(self, out):
out.line(self.longrepr, red=True)

View File

@ -7,9 +7,11 @@ import sys
from time import time from time import time
import py import py
from _pytest._code.code import TerminalRepr, ExceptionInfo from _pytest._code.code import ExceptionInfo
from _pytest.outcomes import skip, Skipped, TEST_OUTCOME from _pytest.outcomes import skip, Skipped, TEST_OUTCOME
from .reports import TestReport, CollectReport, CollectErrorRepr
# #
# pytest plugin hooks # pytest plugin hooks
@ -215,97 +217,44 @@ class CallInfo(object):
return "<CallInfo when=%r %s>" % (self.when, status) return "<CallInfo when=%r %s>" % (self.when, status)
def getslaveinfoline(node): def pytest_runtest_makereport(item, call):
try: when = call.when
return node._slaveinfocache duration = call.stop - call.start
except AttributeError: keywords = {x: 1 for x in item.keywords}
d = node.slaveinfo excinfo = call.excinfo
ver = "%s.%s.%s" % d["version_info"][:3] sections = []
node._slaveinfocache = s = "[%s] %s -- Python %s %s" % ( if not call.excinfo:
d["id"], outcome = "passed"
d["sysplatform"], longrepr = None
ver, else:
d["executable"], if not isinstance(excinfo, ExceptionInfo):
) outcome = "failed"
return s longrepr = excinfo
elif excinfo.errisinstance(skip.Exception):
outcome = "skipped"
class BaseReport(object): r = excinfo._getreprcrash()
def __init__(self, **kw): longrepr = (str(r.path), r.lineno, r.message)
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: else:
try: outcome = "failed"
out.line(longrepr) if call.when == "call":
except UnicodeEncodeError: longrepr = item.repr_failure(excinfo)
out.line("<unprintable longrepr>") else: # exception in setup or teardown
longrepr = item._repr_failure_py(
def get_sections(self, prefix): excinfo, style=item.config.option.tbstyle
for name, content in self.sections: )
if name.startswith(prefix): for rwhen, key, content in item._report_sections:
yield prefix, content sections.append(("Captured %s %s" % (key, rwhen), content))
return TestReport(
@property item.nodeid,
def longreprtext(self): item.location,
""" keywords,
Read-only property that returns the full string representation outcome,
of ``longrepr``. longrepr,
when,
.. versionadded:: 3.0 sections,
""" duration,
tw = py.io.TerminalWriter(stringio=True) user_properties=item.user_properties,
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]
def pytest_runtest_makereport(item, call): def pytest_runtest_makereport(item, call):
@ -348,78 +297,6 @@ def pytest_runtest_makereport(item, call):
) )
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,
user_properties=(),
**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
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>" % (
self.nodeid,
self.when,
self.outcome,
)
class TeardownErrorReport(BaseReport):
outcome = "failed"
when = "teardown"
def __init__(self, longrepr, **extra):
self.longrepr = longrepr
self.sections = []
self.__dict__.update(extra)
def pytest_make_collect_report(collector): def pytest_make_collect_report(collector):
call = CallInfo(lambda: list(collector.collect()), "collect") call = CallInfo(lambda: list(collector.collect()), "collect")
longrepr = None longrepr = None
@ -446,35 +323,6 @@ def pytest_make_collect_report(collector):
return rep return rep
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>" % (
self.nodeid,
len(self.result),
self.outcome,
)
class CollectErrorRepr(TerminalRepr):
def __init__(self, msg):
self.longrepr = msg
def toterminal(self, out):
out.line(self.longrepr, red=True)
class SetupState(object): class SetupState(object):
""" shared state for setting up/tearing down test items or collectors. """ """ shared state for setting up/tearing down test items or collectors. """

View File

@ -7,6 +7,8 @@ import os
from _pytest.junitxml import LogXML from _pytest.junitxml import LogXML
import pytest import pytest
from _pytest.reports import BaseReport
def runandparse(testdir, *args): def runandparse(testdir, *args):
resultpath = testdir.tmpdir.join("junit.xml") resultpath = testdir.tmpdir.join("junit.xml")
@ -940,7 +942,6 @@ def test_unicode_issue368(testdir):
path = testdir.tmpdir.join("test.xml") path = testdir.tmpdir.join("test.xml")
log = LogXML(str(path), None) log = LogXML(str(path), None)
ustr = py.builtin._totext("ВНИ!", "utf-8") ustr = py.builtin._totext("ВНИ!", "utf-8")
from _pytest.runner import BaseReport
class Report(BaseReport): class Report(BaseReport):
longrepr = ustr longrepr = ustr
@ -1137,7 +1138,6 @@ def test_fancy_items_regression(testdir):
def test_global_properties(testdir): def test_global_properties(testdir):
path = testdir.tmpdir.join("test_global_properties.xml") path = testdir.tmpdir.join("test_global_properties.xml")
log = LogXML(str(path), None) log = LogXML(str(path), None)
from _pytest.runner import BaseReport
class Report(BaseReport): class Report(BaseReport):
sections = [] sections = []
@ -1173,7 +1173,6 @@ def test_url_property(testdir):
test_url = "http://www.github.com/pytest-dev" test_url = "http://www.github.com/pytest-dev"
path = testdir.tmpdir.join("test_url_property.xml") path = testdir.tmpdir.join("test_url_property.xml")
log = LogXML(str(path), None) log = LogXML(str(path), None)
from _pytest.runner import BaseReport
class Report(BaseReport): class Report(BaseReport):
longrepr = "FooBarBaz" longrepr = "FooBarBaz"

View File

@ -8,7 +8,7 @@ import py
import pytest import pytest
import sys import sys
import types import types
from _pytest import runner, main, outcomes from _pytest import runner, main, outcomes, reports
class TestSetupState(object): class TestSetupState(object):
@ -459,10 +459,10 @@ class TestSessionReports(object):
reporttypes = [ reporttypes = [
runner.BaseReport, reports.BaseReport,
runner.TestReport, reports.TestReport,
runner.TeardownErrorReport, reports.TeardownErrorReport,
runner.CollectReport, reports.CollectReport,
] ]