Skip to content

Commit

Permalink
implement pytest_runtest_logstart(nodeid, location) hook
Browse files Browse the repository at this point in the history
factor out a NodeInfo helper, and streamline terminal printing a bit

--HG--
branch : trunk
  • Loading branch information
hpk42 committed Sep 26, 2010
1 parent 1c020c3 commit a2fe671
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 62 deletions.
8 changes: 5 additions & 3 deletions py/_plugin/hookspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(). """

Expand Down Expand Up @@ -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
Expand Down
33 changes: 27 additions & 6 deletions py/_plugin/pytest_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -117,11 +141,7 @@ def toterminal(self, out):


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
Expand All @@ -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):
Expand Down
38 changes: 19 additions & 19 deletions py/_plugin/pytest_terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,12 @@ def hasopt(self, char):
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):
Expand Down Expand Up @@ -135,18 +135,18 @@ def pytest_trace(self, category, msg):
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)
Expand All @@ -162,9 +162,10 @@ def pytest_runtest_logreport(self, report):
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'):
Expand Down Expand Up @@ -226,21 +227,20 @@ def _report_keyboardinterrupt(self):
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:
Expand Down
25 changes: 25 additions & 0 deletions testing/plugin/test_pytest_python.py
Original file line number Diff line number Diff line change
Expand Up @@ -1109,3 +1109,28 @@ def test_lookup_error(unknown):
"*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
48 changes: 14 additions & 34 deletions testing/plugin/test_pytest_terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,47 +89,27 @@ def test_writeline(self, testdir, linecomp):
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):
Expand Down

0 comments on commit a2fe671

Please sign in to comment.