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.hook.pytest_unconfigure(config=self)
|
||||||
self.pluginmanager.ensure_shutdown()
|
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):
|
def pytest_cmdline_parse(self, pluginmanager, args):
|
||||||
assert self == pluginmanager.config, (self, pluginmanager.config)
|
assert self == pluginmanager.config, (self, pluginmanager.config)
|
||||||
self.parse(args)
|
self.parse(args)
|
||||||
|
|
|
@ -227,6 +227,11 @@ pytest_report_teststatus.firstresult = True
|
||||||
def pytest_terminal_summary(terminalreporter):
|
def pytest_terminal_summary(terminalreporter):
|
||||||
""" add additional section in terminal summary reporting. """
|
""" 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
|
# doctest hooks
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
|
|
|
@ -263,6 +263,20 @@ class Node(object):
|
||||||
return "<%s %r>" %(self.__class__.__name__,
|
return "<%s %r>" %(self.__class__.__name__,
|
||||||
getattr(self, 'name', None))
|
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
|
# methods for ordering nodes
|
||||||
@property
|
@property
|
||||||
def nodeid(self):
|
def nodeid(self):
|
||||||
|
|
|
@ -75,6 +75,14 @@ def pytest_report_teststatus(report):
|
||||||
letter = "f"
|
letter = "f"
|
||||||
return report.outcome, letter, report.outcome.upper()
|
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:
|
class TerminalReporter:
|
||||||
def __init__(self, config, file=None):
|
def __init__(self, config, file=None):
|
||||||
self.config = config
|
self.config = config
|
||||||
|
@ -151,6 +159,12 @@ class TerminalReporter:
|
||||||
self.write_line("INTERNALERROR> " + line)
|
self.write_line("INTERNALERROR> " + line)
|
||||||
return 1
|
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):
|
def pytest_plugin_registered(self, plugin):
|
||||||
if self.config.option.traceconfig:
|
if self.config.option.traceconfig:
|
||||||
msg = "PLUGIN registered: %s" % (plugin,)
|
msg = "PLUGIN registered: %s" % (plugin,)
|
||||||
|
@ -335,6 +349,7 @@ class TerminalReporter:
|
||||||
self.summary_errors()
|
self.summary_errors()
|
||||||
self.summary_failures()
|
self.summary_failures()
|
||||||
self.summary_hints()
|
self.summary_hints()
|
||||||
|
self.summary_warnings()
|
||||||
self.config.hook.pytest_terminal_summary(terminalreporter=self)
|
self.config.hook.pytest_terminal_summary(terminalreporter=self)
|
||||||
if exitstatus == 2:
|
if exitstatus == 2:
|
||||||
self._report_keyboardinterrupt()
|
self._report_keyboardinterrupt()
|
||||||
|
@ -405,6 +420,16 @@ class TerminalReporter:
|
||||||
for hint in self.config.pluginmanager._hints:
|
for hint in self.config.pluginmanager._hints:
|
||||||
self._tw.line("hint: %s" % hint)
|
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):
|
def summary_failures(self):
|
||||||
if self.config.option.tbstyle != "no":
|
if self.config.option.tbstyle != "no":
|
||||||
reports = self.getreports('failed')
|
reports = self.getreports('failed')
|
||||||
|
@ -449,7 +474,8 @@ class TerminalReporter:
|
||||||
def summary_stats(self):
|
def summary_stats(self):
|
||||||
session_duration = py.std.time.time() - self._sessionstarttime
|
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():
|
for key in self.stats.keys():
|
||||||
if key not in keys:
|
if key not in keys:
|
||||||
keys.append(key)
|
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[-2] == m.pytest_load_initial_conftests
|
||||||
assert l[-3].__module__ == "_pytest.config"
|
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