implement pytest_runtest_logstart(nodeid, location) hook

factor out a NodeInfo helper, and streamline terminal printing a bit

--HG--
branch : trunk
This commit is contained in:
holger krekel 2010-09-26 16:23:45 +02:00
parent 1c020c3d32
commit a2fe6714f8
5 changed files with 90 additions and 62 deletions

View File

@ -102,14 +102,16 @@ def pytest_generate_tests(metafunc):
# -------------------------------------------------------------------------
# generic runtest related hooks
# -------------------------------------------------------------------------
def pytest_itemstart(item, node=None):
""" test item starts running. """
""" (deprecated, use pytest_runtest_logstart). """
def pytest_runtest_protocol(item):
""" implement fixture, run and report about the given test item. """
pytest_runtest_protocol.firstresult = True
def pytest_runtest_logstart(nodeid, location):
""" signal the start of a test run. """
def pytest_runtest_setup(item):
""" called before pytest_runtest_call(). """
@ -160,7 +162,7 @@ def pytest_terminal_summary(terminalreporter):
""" add additional section in terminal summary reporting. """
def pytest_report_iteminfo(item):
""" return (fspath, lineno, domainpath) for the item.
""" return (fspath, lineno, domainpath) location info for the item.
the information is used for result display and to sort tests.
fspath,lineno: file and linenumber of source of item definition.
domainpath: custom id - e.g. for python: dotted import address

View File

@ -29,7 +29,31 @@ def pytest_sessionfinish(session, exitstatus):
if rep:
hook.pytest__teardown_final_logerror(report=rep)
class NodeInfo:
def __init__(self, nodeid, nodenames, fspath, location):
self.nodeid = nodeid
self.nodenames = nodenames
self.fspath = fspath
self.location = location
def getitemnodeinfo(item):
try:
return item._nodeinfo
except AttributeError:
location = item.ihook.pytest_report_iteminfo(item=item)
location = (str(location[0]), location[1], str(location[2]))
nodenames = tuple(item.listnames())
nodeid = item.collection.getid(item)
fspath = item.fspath
item._nodeinfo = n = NodeInfo(nodeid, nodenames, fspath, location)
return n
def pytest_runtest_protocol(item):
nodeinfo = getitemnodeinfo(item)
item.ihook.pytest_runtest_logstart(
nodeid=nodeinfo.nodeid,
location=nodeinfo.location
)
runtestprotocol(item)
return True
@ -117,11 +141,7 @@ class BaseReport(object):
def pytest_runtest_makereport(item, call):
location = item.ihook.pytest_report_iteminfo(item=item)
location = (str(location[0]), location[1], str(location[2]))
nodenames = tuple(item.listnames())
nodeid = item.collection.getid(item)
fspath = item.fspath
nodeinfo = getitemnodeinfo(item)
when = call.when
keywords = dict([(x,1) for x in item.keywords])
excinfo = call.excinfo
@ -141,7 +161,8 @@ def pytest_runtest_makereport(item, call):
longrepr = item.repr_failure(excinfo)
else: # exception in setup or teardown
longrepr = item._repr_failure_py(excinfo)
return TestReport(nodeid, nodenames, fspath, location,
return TestReport(nodeinfo.nodeid, nodeinfo.nodenames,
nodeinfo.fspath, nodeinfo.location,
keywords, outcome, longrepr, when)
class TestReport(BaseReport):

View File

@ -84,12 +84,12 @@ class TerminalReporter:
return char in self.reportchars
def write_fspath_result(self, fspath, res):
fspath = self.curdir.bestrelpath(fspath)
if fspath != self.currentfspath:
self.currentfspath = fspath
fspath = self.curdir.bestrelpath(fspath)
self._tw.line()
relpath = self.curdir.bestrelpath(fspath)
self._tw.write(relpath + " ")
self.currentfspath = fspath
self._tw.write(res)
def write_ensure_prefix(self, prefix, extra="", **kwargs):
@ -135,18 +135,18 @@ class TerminalReporter:
def pytest_deselected(self, items):
self.stats.setdefault('deselected', []).extend(items)
#def pytest_itemstart(self, item, node=None):
# if self.config.option.verbose:
# line = self._locationline(rep)
# self.write_ensure_prefix(line, "")
# else:
# # ensure that the path is printed before the
# # 1st test of a module starts running
# self.write_fspath_result(self._getfspath(item), "")
def pytest__teardown_final_logerror(self, report):
self.stats.setdefault("error", []).append(report)
def pytest_runtest_logstart(self, nodeid, location):
# ensure that the path is printed before the
# 1st test of a module starts running
if self.config.option.verbose:
line = self._locationline(*location)
self.write_ensure_prefix(line, "")
else:
self.write_fspath_result(py.path.local(location[0]), "")
def pytest_runtest_logreport(self, report):
rep = report
res = self.config.hook.pytest_report_teststatus(report=rep)
@ -162,9 +162,10 @@ class TerminalReporter:
if not self.config.option.verbose:
self.write_fspath_result(rep.fspath, letter)
else:
line = self._locationline(rep)
line = self._locationline(*rep.location)
if not hasattr(rep, 'node'):
self.write_ensure_prefix(line, word, **markup)
#self._tw.write(word, **markup)
else:
self.ensure_newline()
if hasattr(rep, 'node'):
@ -226,21 +227,20 @@ class TerminalReporter:
else:
excrepr.reprcrash.toterminal(self._tw)
def _locationline(self, rep):
def _locationline(self, fspath, lineno, domain):
#collect_fspath = self._getfspath(item)
fspath, lineno, msg = rep.location
#if fspath and fspath != collect_fspath:
# fspath = "%s <- %s" % (
# self.curdir.bestrelpath(collect_fspath),
# self.curdir.bestrelpath(fspath))
if fspath:
fspath = self.curdir.bestrelpath(fspath)
fspath = self.curdir.bestrelpath(py.path.local(fspath))
if lineno is not None:
lineno += 1
if fspath and lineno and msg:
line = "%(fspath)s:%(lineno)s: %(msg)s"
elif fspath and msg:
line = "%(fspath)s: %(msg)s"
if fspath and lineno and domain:
line = "%(fspath)s:%(lineno)s: %(domain)s"
elif fspath and domain:
line = "%(fspath)s: %(domain)s"
elif fspath and lineno:
line = "%(fspath)s:%(lineno)s %(extrapath)s"
else:

View File

@ -1109,3 +1109,28 @@ def test_funcarg_lookup_error(testdir):
"*1 error*",
])
assert "INTERNAL" not in result.stdout.str()
class TestReportInfo:
def test_itemreport_reportinfo(self, testdir, linecomp):
testdir.makeconftest("""
import py
class Function(py.test.collect.Function):
def reportinfo(self):
return "ABCDE", 42, "custom"
""")
item = testdir.getitem("def test_func(): pass")
runner = item.config.pluginmanager.getplugin("runner")
nodeinfo = runner.getitemnodeinfo(item)
assert nodeinfo.location == ("ABCDE", 42, "custom")
def test_itemreport_pytest_report_iteminfo(self, testdir, linecomp):
item = testdir.getitem("def test_func(): pass")
tup = "FGHJ", 42, "custom"
class Plugin:
def pytest_report_iteminfo(self, item):
return tup
item.config.pluginmanager.register(Plugin())
runner = runner = item.config.pluginmanager.getplugin("runner")
nodeinfo = runner.getitemnodeinfo(item)
location = nodeinfo.location
assert location == tup

View File

@ -89,47 +89,27 @@ class TestTerminal:
assert lines[1].endswith("xy.py .")
assert lines[2] == "hello world"
@py.test.mark.xfail(reason="re-implement ItemStart events")
def test_show_path_before_running_test(self, testdir, linecomp):
def test_show_runtest_logstart(self, testdir, linecomp):
item = testdir.getitem("def test_func(): pass")
tr = TerminalReporter(item.config, file=linecomp.stringio)
item.config.pluginmanager.register(tr)
tr.config.hook.pytest_itemstart(item=item)
nodeid = item.collection.getid(item)
location = item.ihook.pytest_report_iteminfo(item=item)
tr.config.hook.pytest_runtest_logstart(nodeid=nodeid, location=location)
linecomp.assert_contains_lines([
"*test_show_path_before_running_test.py*"
"*test_show_runtest_logstart.py*"
])
@py.test.mark.xfail(reason="re-implement ItemStart events")
def test_itemreport_reportinfo(self, testdir, linecomp):
testdir.makeconftest("""
import py
class Function(py.test.collect.Function):
def reportinfo(self):
return "ABCDE", 42, "custom"
def test_runtest_location_shown_before_test_starts(self, testdir):
p1 = testdir.makepyfile("""
def test_1():
import time
time.sleep(20)
""")
item = testdir.getitem("def test_func(): pass")
tr = TerminalReporter(item.config, file=linecomp.stringio)
item.config.pluginmanager.register(tr)
tr.config.option.verbose = True
tr.config.hook.pytest_itemstart(item=item)
linecomp.assert_contains_lines([
"*ABCDE:43: custom*"
])
@py.test.mark.xfail(reason="re-implement ItemStart events")
def test_itemreport_pytest_report_iteminfo(self, testdir, linecomp):
item = testdir.getitem("def test_func(): pass")
class Plugin:
def pytest_report_iteminfo(self, item):
return "FGHJ", 42, "custom"
item.config.pluginmanager.register(Plugin())
tr = TerminalReporter(item.config, file=linecomp.stringio)
item.config.pluginmanager.register(tr)
tr.config.option.verbose = True
tr.config.hook.pytest_itemstart(item=item)
linecomp.assert_contains_lines([
"*FGHJ:43: custom*"
])
child = testdir.spawn_pytest("")
child.expect(".*test_runtest_location.*py")
child.sendeof()
child.kill(15)
@py.test.mark.xfail(reason="re-implement subclassing precision reporting")
def test_itemreport_subclasses_show_subclassed_file(self, testdir):