Improve warning representation in terminal plugin and fix tests
This commit is contained in:
parent
be5db6fa22
commit
78194093af
|
@ -910,11 +910,11 @@ class Config(object):
|
||||||
fin = self._cleanup.pop()
|
fin = self._cleanup.pop()
|
||||||
fin()
|
fin()
|
||||||
|
|
||||||
def warn(self, code, message, fslocation=None):
|
def warn(self, code, message, fslocation=None, nodeid=None):
|
||||||
""" generate a warning for this test session. """
|
""" generate a warning for this test session. """
|
||||||
self.hook.pytest_logwarning.call_historic(kwargs=dict(
|
self.hook.pytest_logwarning.call_historic(kwargs=dict(
|
||||||
code=code, message=message,
|
code=code, message=message,
|
||||||
fslocation=fslocation, nodeid=None))
|
fslocation=fslocation, nodeid=nodeid))
|
||||||
|
|
||||||
def get_terminal_writer(self):
|
def get_terminal_writer(self):
|
||||||
return self.pluginmanager.get_plugin("terminalreporter")._tw
|
return self.pluginmanager.get_plugin("terminalreporter")._tw
|
||||||
|
|
|
@ -1080,7 +1080,7 @@ class FixtureManager(object):
|
||||||
continue
|
continue
|
||||||
marker = defaultfuncargprefixmarker
|
marker = defaultfuncargprefixmarker
|
||||||
from _pytest import deprecated
|
from _pytest import deprecated
|
||||||
self.config.warn('C1', deprecated.FUNCARG_PREFIX.format(name=name))
|
self.config.warn('C1', deprecated.FUNCARG_PREFIX.format(name=name), nodeid=nodeid)
|
||||||
name = name[len(self._argprefix):]
|
name = name[len(self._argprefix):]
|
||||||
elif not isinstance(marker, FixtureFunctionMarker):
|
elif not isinstance(marker, FixtureFunctionMarker):
|
||||||
# magic globals with __getattr__ might have got us a wrong
|
# magic globals with __getattr__ might have got us a wrong
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
This is a good source for looking at the various reporting hooks.
|
This is a good source for looking at the various reporting hooks.
|
||||||
"""
|
"""
|
||||||
import operator
|
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
from _pytest.main import EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, \
|
from _pytest.main import EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, \
|
||||||
|
@ -83,13 +82,40 @@ 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(object):
|
class WarningReport(object):
|
||||||
|
"""
|
||||||
|
Simple structure to hold warnings information captured by ``pytest_logwarning``.
|
||||||
|
"""
|
||||||
def __init__(self, code, message, nodeid=None, fslocation=None):
|
def __init__(self, code, message, nodeid=None, fslocation=None):
|
||||||
|
"""
|
||||||
|
:param code: unused
|
||||||
|
:param str message: user friendly message about the warning
|
||||||
|
:param str|None nodeid: node id that generated the warning (see ``get_location``).
|
||||||
|
:param tuple|py.path.local fslocation:
|
||||||
|
file system location of the source of the warning (see ``get_location``).
|
||||||
|
"""
|
||||||
self.code = code
|
self.code = code
|
||||||
self.message = message
|
self.message = message
|
||||||
self.nodeid = nodeid
|
self.nodeid = nodeid
|
||||||
self.fslocation = fslocation
|
self.fslocation = fslocation
|
||||||
|
|
||||||
|
def get_location(self, config):
|
||||||
|
"""
|
||||||
|
Returns the more user-friendly information about the location
|
||||||
|
of a warning, or None.
|
||||||
|
"""
|
||||||
|
if self.nodeid:
|
||||||
|
return self.nodeid
|
||||||
|
if self.fslocation:
|
||||||
|
if isinstance(self.fslocation, tuple) and len(self.fslocation) == 2:
|
||||||
|
filename, linenum = self.fslocation
|
||||||
|
relpath = py.path.local(filename).relto(config.invocation_dir)
|
||||||
|
return '%s:%d' % (relpath, linenum)
|
||||||
|
else:
|
||||||
|
return str(self.fslocation)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class TerminalReporter(object):
|
class TerminalReporter(object):
|
||||||
def __init__(self, config, file=None):
|
def __init__(self, config, file=None):
|
||||||
|
@ -169,8 +195,6 @@ class TerminalReporter(object):
|
||||||
|
|
||||||
def pytest_logwarning(self, code, fslocation, message, nodeid):
|
def pytest_logwarning(self, code, fslocation, message, nodeid):
|
||||||
warnings = self.stats.setdefault("warnings", [])
|
warnings = self.stats.setdefault("warnings", [])
|
||||||
if isinstance(fslocation, tuple):
|
|
||||||
fslocation = "%s:%d" % fslocation
|
|
||||||
warning = WarningReport(code=code, fslocation=fslocation,
|
warning = WarningReport(code=code, fslocation=fslocation,
|
||||||
message=message, nodeid=nodeid)
|
message=message, nodeid=nodeid)
|
||||||
warnings.append(warning)
|
warnings.append(warning)
|
||||||
|
@ -444,12 +468,17 @@ class TerminalReporter(object):
|
||||||
all_warnings = self.stats.get("warnings")
|
all_warnings = self.stats.get("warnings")
|
||||||
if not all_warnings:
|
if not all_warnings:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
grouped = itertools.groupby(all_warnings, key=lambda wr: wr.get_location(self.config))
|
||||||
|
|
||||||
self.write_sep("=", "warnings summary", yellow=True, bold=False)
|
self.write_sep("=", "warnings summary", yellow=True, bold=False)
|
||||||
grouped = itertools.groupby(all_warnings, key=operator.attrgetter('nodeid'))
|
for location, warnings in grouped:
|
||||||
for nodeid, warnings in grouped:
|
self._tw.line(str(location) or '<undetermined location>')
|
||||||
self._tw.line(str(nodeid))
|
|
||||||
for w in warnings:
|
for w in warnings:
|
||||||
self._tw.line(w.message)
|
lines = w.message.splitlines()
|
||||||
|
indented = '\n'.join(' ' + x for x in lines)
|
||||||
|
self._tw.line(indented)
|
||||||
|
self._tw.line()
|
||||||
self._tw.line('-- Docs: http://doc.pytest.org/en/latest/warnings.html')
|
self._tw.line('-- Docs: http://doc.pytest.org/en/latest/warnings.html')
|
||||||
|
|
||||||
def summary_passes(self):
|
def summary_passes(self):
|
||||||
|
|
|
@ -26,7 +26,7 @@ def test_funcarg_prefix_deprecation(testdir):
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest('-ra')
|
result = testdir.runpytest('-ra')
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
('pytest_funcarg__value: '
|
('*pytest_funcarg__value: '
|
||||||
'declaring fixtures using "pytest_funcarg__" prefix is deprecated '
|
'declaring fixtures using "pytest_funcarg__" prefix is deprecated '
|
||||||
'and scheduled to be removed in pytest 4.0. '
|
'and scheduled to be removed in pytest 4.0. '
|
||||||
'Please remove the prefix and use the @pytest.fixture decorator instead.'),
|
'Please remove the prefix and use the @pytest.fixture decorator instead.'),
|
||||||
|
|
|
@ -113,9 +113,9 @@ class TestClass(object):
|
||||||
pass
|
pass
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest("-rw")
|
result = testdir.runpytest("-rw")
|
||||||
result.stdout.fnmatch_lines_random("""
|
result.stdout.fnmatch_lines([
|
||||||
WC1*test_class_with_init_warning.py*__init__*
|
"*cannot collect test class 'TestClass1' because it has a __init__ constructor",
|
||||||
""")
|
])
|
||||||
|
|
||||||
def test_class_subclassobject(self, testdir):
|
def test_class_subclassobject(self, testdir):
|
||||||
testdir.getmodulecol("""
|
testdir.getmodulecol("""
|
||||||
|
@ -1241,8 +1241,8 @@ def test_dont_collect_non_function_callable(testdir):
|
||||||
result = testdir.runpytest('-rw')
|
result = testdir.runpytest('-rw')
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
'*collected 1 item*',
|
'*collected 1 item*',
|
||||||
'WC2 *',
|
"*cannot collect 'test_a' because it is not a function*",
|
||||||
'*1 passed, 1 pytest-warnings in *',
|
'*1 passed, 1 warnings in *',
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -975,7 +975,10 @@ def test_assert_tuple_warning(testdir):
|
||||||
assert(False, 'you shall not pass')
|
assert(False, 'you shall not pass')
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest('-rw')
|
result = testdir.runpytest('-rw')
|
||||||
result.stdout.fnmatch_lines('*test_assert_tuple_warning.py:2 assertion is always true*')
|
result.stdout.fnmatch_lines([
|
||||||
|
'*test_assert_tuple_warning.py:2',
|
||||||
|
'*assertion is always true*',
|
||||||
|
])
|
||||||
|
|
||||||
def test_assert_indirect_tuple_no_warning(testdir):
|
def test_assert_indirect_tuple_no_warning(testdir):
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
|
|
|
@ -638,7 +638,7 @@ class TestWarning(object):
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
result.stdout.fnmatch_lines("""
|
result.stdout.fnmatch_lines("""
|
||||||
===*warnings summary*===
|
===*warnings summary*===
|
||||||
*test_warn_on_test_item_from_request::test_hello*
|
*test_warn_on_test_item_from_request.py::test_hello*
|
||||||
*hello*
|
*hello*
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
|
@ -854,7 +854,7 @@ def test_record_property(testdir):
|
||||||
pnodes[1].assert_attr(name="foo", value="<1")
|
pnodes[1].assert_attr(name="foo", value="<1")
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
'test_record_property.py::test_record',
|
'test_record_property.py::test_record',
|
||||||
'record_xml_property*experimental*',
|
'*record_xml_property*experimental*',
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -39,10 +39,10 @@ def test_normal_flow(testdir, pyfile_with_warnings):
|
||||||
'*test_normal_flow.py::test_func',
|
'*test_normal_flow.py::test_func',
|
||||||
|
|
||||||
'*normal_flow_module.py:3: PendingDeprecationWarning: functionality is pending deprecation',
|
'*normal_flow_module.py:3: PendingDeprecationWarning: functionality is pending deprecation',
|
||||||
' warnings.warn(PendingDeprecationWarning("functionality is pending deprecation"))',
|
'* warnings.warn(PendingDeprecationWarning("functionality is pending deprecation"))',
|
||||||
|
|
||||||
'*normal_flow_module.py:4: DeprecationWarning: functionality is deprecated',
|
'*normal_flow_module.py:4: DeprecationWarning: functionality is deprecated',
|
||||||
' warnings.warn(DeprecationWarning("functionality is deprecated"))',
|
'* warnings.warn(DeprecationWarning("functionality is deprecated"))',
|
||||||
'* 1 passed, 2 warnings*',
|
'* 1 passed, 2 warnings*',
|
||||||
])
|
])
|
||||||
assert result.stdout.str().count('test_normal_flow.py::test_func') == 1
|
assert result.stdout.str().count('test_normal_flow.py::test_func') == 1
|
||||||
|
@ -67,10 +67,10 @@ def test_setup_teardown_warnings(testdir, pyfile_with_warnings):
|
||||||
'*== %s ==*' % WARNINGS_SUMMARY_HEADER,
|
'*== %s ==*' % WARNINGS_SUMMARY_HEADER,
|
||||||
|
|
||||||
'*test_setup_teardown_warnings.py:6: UserWarning: warning during setup',
|
'*test_setup_teardown_warnings.py:6: UserWarning: warning during setup',
|
||||||
' warnings.warn(UserWarning("warning during setup"))',
|
'*warnings.warn(UserWarning("warning during setup"))',
|
||||||
|
|
||||||
'*test_setup_teardown_warnings.py:8: UserWarning: warning during teardown',
|
'*test_setup_teardown_warnings.py:8: UserWarning: warning during teardown',
|
||||||
' warnings.warn(UserWarning("warning during teardown"))',
|
'*warnings.warn(UserWarning("warning during teardown"))',
|
||||||
'* 1 passed, 2 warnings*',
|
'* 1 passed, 2 warnings*',
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue