introduce warning system with this API:
- node.warn() for a node-specific warning - config.warn() for a global non-node specific warning Each warning is accompanied by a "warning number" so that we can later introduce mechanisms for surpressing them. Each warning will trigger a call to pytest_report_warn(number, node, message) which is by default implemented by the TerminalReporter which introduces a new option "-rw" to show details about warnings.
This commit is contained in:
parent
b96559149c
commit
1b387bea62
|
@ -613,6 +613,11 @@ class Config(object):
|
|||
self.hook.pytest_unconfigure(config=self)
|
||||
self.pluginmanager.ensure_shutdown()
|
||||
|
||||
def warn(self, code, message):
|
||||
""" generate a warning for this test session. """
|
||||
self.hook.pytest_logwarning(code=code, message=message,
|
||||
fslocation=None, nodeid=None)
|
||||
|
||||
def pytest_cmdline_parse(self, pluginmanager, args):
|
||||
assert self == pluginmanager.config, (self, pluginmanager.config)
|
||||
self.parse(args)
|
||||
|
|
|
@ -227,6 +227,11 @@ pytest_report_teststatus.firstresult = True
|
|||
def pytest_terminal_summary(terminalreporter):
|
||||
""" add additional section in terminal summary reporting. """
|
||||
|
||||
def pytest_logwarning(message, code, nodeid, fslocation):
|
||||
""" process a warning specified by a message, a code string,
|
||||
a nodeid and fslocation (both of which may be None
|
||||
if the warning is not tied to a partilar node/location)."""
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# doctest hooks
|
||||
# -------------------------------------------------------------------------
|
||||
|
|
|
@ -263,6 +263,20 @@ class Node(object):
|
|||
return "<%s %r>" %(self.__class__.__name__,
|
||||
getattr(self, 'name', None))
|
||||
|
||||
def warn(self, code, message):
|
||||
""" generate a warning with the given code and message for this
|
||||
item. """
|
||||
assert isinstance(code, str)
|
||||
fslocation = getattr(self, "location", None)
|
||||
if fslocation is None:
|
||||
fslocation = getattr(self, "fspath", None)
|
||||
else:
|
||||
fslocation = "%s:%s" % fslocation[:2]
|
||||
|
||||
self.ihook.pytest_logwarning(code=code, message=message,
|
||||
nodeid=self.nodeid,
|
||||
fslocation=fslocation)
|
||||
|
||||
# methods for ordering nodes
|
||||
@property
|
||||
def nodeid(self):
|
||||
|
|
|
@ -75,6 +75,14 @@ def pytest_report_teststatus(report):
|
|||
letter = "f"
|
||||
return report.outcome, letter, report.outcome.upper()
|
||||
|
||||
class WarningReport:
|
||||
def __init__(self, code, message, nodeid=None, fslocation=None):
|
||||
self.code = code
|
||||
self.message = message
|
||||
self.nodeid = nodeid
|
||||
self.fslocation = fslocation
|
||||
|
||||
|
||||
class TerminalReporter:
|
||||
def __init__(self, config, file=None):
|
||||
self.config = config
|
||||
|
@ -151,6 +159,12 @@ class TerminalReporter:
|
|||
self.write_line("INTERNALERROR> " + line)
|
||||
return 1
|
||||
|
||||
def pytest_logwarning(self, code, fslocation, message, nodeid):
|
||||
warnings = self.stats.setdefault("warnings", [])
|
||||
warning = WarningReport(code=code, fslocation=fslocation,
|
||||
message=message, nodeid=nodeid)
|
||||
warnings.append(warning)
|
||||
|
||||
def pytest_plugin_registered(self, plugin):
|
||||
if self.config.option.traceconfig:
|
||||
msg = "PLUGIN registered: %s" % (plugin,)
|
||||
|
@ -335,6 +349,7 @@ class TerminalReporter:
|
|||
self.summary_errors()
|
||||
self.summary_failures()
|
||||
self.summary_hints()
|
||||
self.summary_warnings()
|
||||
self.config.hook.pytest_terminal_summary(terminalreporter=self)
|
||||
if exitstatus == 2:
|
||||
self._report_keyboardinterrupt()
|
||||
|
@ -405,6 +420,16 @@ class TerminalReporter:
|
|||
for hint in self.config.pluginmanager._hints:
|
||||
self._tw.line("hint: %s" % hint)
|
||||
|
||||
def summary_warnings(self):
|
||||
if self.hasopt("w"):
|
||||
warnings = self.stats.get("warnings")
|
||||
if not warnings:
|
||||
return
|
||||
self.write_sep("=", "warning summary")
|
||||
for w in warnings:
|
||||
self._tw.line("W%s %s %s" % (w.code,
|
||||
w.fslocation, w.message))
|
||||
|
||||
def summary_failures(self):
|
||||
if self.config.option.tbstyle != "no":
|
||||
reports = self.getreports('failed')
|
||||
|
@ -449,7 +474,8 @@ class TerminalReporter:
|
|||
def summary_stats(self):
|
||||
session_duration = py.std.time.time() - self._sessionstarttime
|
||||
|
||||
keys = "failed passed skipped deselected xfailed xpassed".split()
|
||||
keys = ("failed passed skipped deselected "
|
||||
"xfailed xpassed warnings").split()
|
||||
for key in self.stats.keys():
|
||||
if key not in keys:
|
||||
keys.append(key)
|
||||
|
|
|
@ -360,4 +360,43 @@ def test_load_initial_conftest_last_ordering(testdir):
|
|||
assert l[-2] == m.pytest_load_initial_conftests
|
||||
assert l[-3].__module__ == "_pytest.config"
|
||||
|
||||
class TestWarning:
|
||||
def test_warn_config(self, testdir):
|
||||
testdir.makeconftest("""
|
||||
l = []
|
||||
def pytest_configure(config):
|
||||
config.warn("C1", "hello")
|
||||
def pytest_logwarning(code, message):
|
||||
assert code == "C1"
|
||||
assert message == "hello"
|
||||
l.append(1)
|
||||
""")
|
||||
testdir.makepyfile("""
|
||||
def test_proper(pytestconfig):
|
||||
import conftest
|
||||
assert conftest.l == [1]
|
||||
""")
|
||||
reprec = testdir.inline_run()
|
||||
reprec.assertoutcome(passed=1)
|
||||
|
||||
def test_warn_on_test_item_from_request(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
|
||||
@pytest.fixture
|
||||
def fix(request):
|
||||
request.node.warn("T1", "hello")
|
||||
def test_hello(fix):
|
||||
pass
|
||||
""")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines("""
|
||||
*1 warning*
|
||||
""")
|
||||
assert "hello" not in result.stdout.str()
|
||||
result = testdir.runpytest("-rw")
|
||||
result.stdout.fnmatch_lines("""
|
||||
===*warning summary*===
|
||||
*WT1*test_warn_on_test_item*:5*hello*
|
||||
*1 warning*
|
||||
""")
|
||||
|
|
Loading…
Reference in New Issue