shift reporting info generation away from terminal reporting time, simplify code.

also get rid of redundant 'shortrepr' on collect/test reports
and rename reportinfo to "location" in some places

--HG--
branch : trunk
This commit is contained in:
holger krekel 2010-09-26 16:23:44 +02:00
parent 7d1585215d
commit 1c020c3d32
21 changed files with 255 additions and 308 deletions

View File

@ -1,3 +1,10 @@
checks / deprecations for next release
---------------------------------------------------------------
tags: bug 1.4 core xdist
* reportinfo -> location in hooks and items
* check oejskit plugin compatibility
refine session initialization / fix custom collect crash
---------------------------------------------------------------
tags: bug 1.4 core xdist

View File

@ -160,8 +160,10 @@ def pytest_terminal_summary(terminalreporter):
""" add additional section in terminal summary reporting. """
def pytest_report_iteminfo(item):
""" return (fspath, lineno, name) for the item.
the information is used for result display and to sort tests
""" return (fspath, lineno, domainpath) 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
"""
pytest_report_iteminfo.firstresult = True

View File

@ -86,6 +86,9 @@ class DoctestItem(py.test.collect.Item):
else:
return super(DoctestItem, self).repr_failure(excinfo)
def reportinfo(self):
return self.fspath, None, "[doctest]"
class DoctestTextfile(DoctestItem):
def runtest(self):
if not self._deprecated_testexecution():

View File

@ -37,12 +37,9 @@ class LogXML(object):
self._durations = {}
def _opentestcase(self, report):
if hasattr(report, 'item'):
node = report.item
else:
node = report.collector
d = {'time': self._durations.pop(node, "0")}
names = [x.replace(".py", "") for x in node.listnames() if x != "()"]
names = report.nodenames
d = {'time': self._durations.pop(names, "0")}
names = [x.replace(".py", "") for x in names if x != "()"]
classnames = names[:-1]
if self.prefix:
classnames.insert(0, self.prefix)
@ -122,11 +119,12 @@ class LogXML(object):
self.append_skipped(report)
def pytest_runtest_call(self, item, __multicall__):
names = tuple(item.listnames())
start = time.time()
try:
return __multicall__.execute()
finally:
self._durations[item] = time.time() - start
self._durations[names] = time.time() - start
def pytest_collectreport(self, report):
if not report.passed:

View File

@ -53,7 +53,7 @@ you start monkeypatching after the undo call.
.. _`monkeypatch blog post`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/
"""
import py, os, sys
import os, sys
def pytest_funcarg__monkeypatch(request):
"""The returned ``monkeypatch`` funcarg provides these

View File

@ -396,8 +396,7 @@ class ReportRecorder(object):
""" return a testreport whose dotted import path matches """
l = []
for rep in self.getreports(names=names):
colitem = rep.getnode()
if not inamepart or inamepart in colitem.listnames():
if not inamepart or inamepart in rep.nodenames:
l.append(rep)
if not l:
raise ValueError("could not find test report matching %r: no test reports at all!" %

View File

@ -57,21 +57,20 @@ class ResultLog(object):
self.config = config
self.logfile = logfile # preferably line buffered
def write_log_entry(self, testpath, shortrepr, longrepr):
print_("%s %s" % (shortrepr, testpath), file=self.logfile)
def write_log_entry(self, testpath, lettercode, longrepr):
print_("%s %s" % (lettercode, testpath), file=self.logfile)
for line in longrepr.splitlines():
print_(" %s" % line, file=self.logfile)
def log_outcome(self, node, shortrepr, longrepr):
testpath = generic_path(node)
self.write_log_entry(testpath, shortrepr, longrepr)
def log_outcome(self, report, lettercode, longrepr):
testpath = getattr(report, 'nodeid', None)
if testpath is None:
testpath = report.fspath
self.write_log_entry(testpath, lettercode, longrepr)
def pytest_runtest_logreport(self, report):
res = self.config.hook.pytest_report_teststatus(report=report)
if res is not None:
code = res[1]
else:
code = report.shortrepr
code = res[1]
if code == 'x':
longrepr = str(report.longrepr)
elif code == 'X':
@ -82,7 +81,7 @@ class ResultLog(object):
longrepr = str(report.longrepr)
elif report.skipped:
longrepr = str(report.longrepr.reprcrash.message)
self.log_outcome(report.item, code, longrepr)
self.log_outcome(report, code, longrepr)
def pytest_collectreport(self, report):
if not report.passed:
@ -92,7 +91,7 @@ class ResultLog(object):
assert report.skipped
code = "S"
longrepr = str(report.longrepr.reprcrash)
self.log_outcome(report.collector, code, longrepr)
self.log_outcome(report, code, longrepr)
def pytest_internalerror(self, excrepr):
path = excrepr.reprcrash.path

View File

@ -3,6 +3,7 @@ collect and run test items and create reports.
"""
import py, sys
from py._code.code import TerminalRepr
def pytest_namespace():
return {
@ -28,16 +29,6 @@ def pytest_sessionfinish(session, exitstatus):
if rep:
hook.pytest__teardown_final_logerror(report=rep)
def pytest_make_collect_report(collector):
result = excinfo = None
try:
result = collector._memocollect()
except KeyboardInterrupt:
raise
except:
excinfo = py.code.ExceptionInfo()
return CollectReport(collector, result, excinfo)
def pytest_runtest_protocol(item):
runtestprotocol(item)
return True
@ -57,9 +48,6 @@ def pytest_runtest_call(item):
if not item._deprecated_testexecution():
item.runtest()
def pytest_runtest_makereport(item, call):
return ItemTestReport(item, call.excinfo, call.when)
def pytest_runtest_teardown(item):
item.config._setupstate.teardown_exact(item)
@ -68,8 +56,7 @@ def pytest__teardown_final(session):
if call.excinfo:
ntraceback = call.excinfo.traceback .cut(excludepath=py._pydir)
call.excinfo.traceback = ntraceback.filter()
rep = TeardownErrorReport(call.excinfo)
return rep
return TeardownErrorReport(call.excinfo)
def pytest_report_teststatus(report):
if report.when in ("setup", "teardown"):
@ -80,6 +67,8 @@ def pytest_report_teststatus(report):
return "skipped", "s", "SKIPPED"
else:
return "", "", ""
#
# Implementation
@ -115,123 +104,120 @@ class CallInfo:
return "<CallInfo when=%r %s>" % (self.when, status)
class BaseReport(object):
def __init__(self):
self.headerlines = []
def __repr__(self):
l = ["%s=%s" %(key, value)
for key, value in self.__dict__.items()]
return "<%s %s>" %(self.__class__.__name__, " ".join(l),)
def _getcrashline(self):
try:
return str(self.longrepr.reprcrash)
except AttributeError:
try:
return str(self.longrepr)[:50]
except AttributeError:
return ""
def toterminal(self, out):
for line in self.headerlines:
out.line(line)
longrepr = self.longrepr
if hasattr(longrepr, 'toterminal'):
longrepr.toterminal(out)
else:
out.line(str(longrepr))
class CollectErrorRepr(BaseReport):
passed = property(lambda x: x.outcome == "passed")
failed = property(lambda x: x.outcome == "failed")
skipped = property(lambda x: x.outcome == "skipped")
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
when = call.when
keywords = dict([(x,1) for x in item.keywords])
excinfo = call.excinfo
if not call.excinfo:
outcome = "passed"
longrepr = None
else:
if not isinstance(excinfo, py.code.ExceptionInfo):
outcome = "failed"
longrepr = excinfo
elif excinfo.errisinstance(py.test.skip.Exception):
outcome = "skipped"
longrepr = item._repr_failure_py(excinfo)
else:
outcome = "failed"
if call.when == "call":
longrepr = item.repr_failure(excinfo)
else: # exception in setup or teardown
longrepr = item._repr_failure_py(excinfo)
return TestReport(nodeid, nodenames, fspath, location,
keywords, outcome, longrepr, when)
class TestReport(BaseReport):
def __init__(self, nodeid, nodenames, fspath, location,
keywords, outcome, longrepr, when):
self.nodeid = nodeid
self.nodenames = nodenames
self.fspath = fspath # where the test was collected
self.location = location
self.keywords = keywords
self.outcome = outcome
self.longrepr = longrepr
self.when = when
def __repr__(self):
return "<TestReport %r when=%r outcome=%r>" % (
self.nodeid, self.when, self.outcome)
class TeardownErrorReport(BaseReport):
outcome = "failed"
when = "teardown"
def __init__(self, excinfo):
self.longrepr = excinfo.getrepr(funcargs=True)
def pytest_make_collect_report(collector):
result = excinfo = None
try:
result = collector._memocollect()
except KeyboardInterrupt:
raise
except:
excinfo = py.code.ExceptionInfo()
nodenames = tuple(collector.listnames())
nodeid = collector.collection.getid(collector)
fspath = str(collector.fspath)
reason = longrepr = None
if not excinfo:
outcome = "passed"
else:
if excinfo.errisinstance(py.test.skip.Exception):
outcome = "skipped"
reason = str(excinfo.value)
longrepr = collector._repr_failure_py(excinfo, "line")
else:
outcome = "failed"
errorinfo = collector.repr_failure(excinfo)
if not hasattr(errorinfo, "toterminal"):
errorinfo = CollectErrorRepr(errorinfo)
longrepr = errorinfo
return CollectReport(nodenames, nodeid, fspath,
outcome, longrepr, result, reason)
class CollectReport(BaseReport):
def __init__(self, nodenames, nodeid, fspath, outcome,
longrepr, result, reason):
self.nodenames = nodenames
self.nodeid = nodeid
self.fspath = fspath
self.outcome = outcome
self.longrepr = longrepr
self.result = result
self.reason = reason
@property
def location(self):
return (self.fspath, None, self.fspath)
def __repr__(self):
return "<CollectReport %r outcome=%r>" % (self.nodeid, self.outcome)
class CollectErrorRepr(TerminalRepr):
def __init__(self, msg):
super(CollectErrorRepr, self).__init__()
self.longrepr = msg
def toterminal(self, out):
out.line(str(self.longrepr), red=True)
class ItemTestReport(BaseReport):
failed = passed = skipped = False
def __init__(self, item, excinfo=None, when=None):
super(ItemTestReport, self).__init__()
self.item = item
self.when = when
if item and when != "setup":
self.keywords = item.keywords
else:
# if we fail during setup it might mean
# we are not able to access the underlying object
# this might e.g. happen if we are unpickled
# and our parent collector did not collect us
# (because it e.g. skipped for platform reasons)
self.keywords = {}
if not excinfo:
self.passed = True
self.shortrepr = "."
else:
if not isinstance(excinfo, py.code.ExceptionInfo):
self.failed = True
shortrepr = "?"
longrepr = excinfo
elif excinfo.errisinstance(py.test.skip.Exception):
self.skipped = True
shortrepr = "s"
longrepr = self.item._repr_failure_py(excinfo)
else:
self.failed = True
shortrepr = self.item.shortfailurerepr
if self.when == "call":
longrepr = self.item.repr_failure(excinfo)
else: # exception in setup or teardown
longrepr = self.item._repr_failure_py(excinfo)
shortrepr = shortrepr.lower()
self.shortrepr = shortrepr
self.longrepr = longrepr
def __repr__(self):
status = (self.passed and "passed" or
self.skipped and "skipped" or
self.failed and "failed" or
"CORRUPT")
l = [repr(self.item.name), "when=%r" % self.when, "outcome %r" % status,]
if hasattr(self, 'node'):
l.append("txnode=%s" % self.node.gateway.id)
info = " " .join(map(str, l))
return "<ItemTestReport %s>" % info
def getnode(self):
return self.item
class CollectReport(BaseReport):
skipped = failed = passed = False
def __init__(self, collector, result, excinfo=None):
super(CollectReport, self).__init__()
self.collector = collector
if not excinfo:
self.passed = True
self.result = result
else:
if excinfo.errisinstance(py.test.skip.Exception):
self.skipped = True
self.reason = str(excinfo.value)
self.longrepr = self.collector._repr_failure_py(excinfo, "line")
else:
self.failed = True
errorinfo = self.collector.repr_failure(excinfo)
if not hasattr(errorinfo, "toterminal"):
errorinfo = CollectErrorRepr(errorinfo)
self.longrepr = errorinfo
def getnode(self):
return self.collector
class TeardownErrorReport(BaseReport):
skipped = passed = False
failed = True
when = "teardown"
def __init__(self, excinfo):
super(TeardownErrorReport, self).__init__()
self.longrepr = excinfo.getrepr(funcargs=True)
class SetupState(object):
""" shared state for setting up/tearing down test items or collectors. """
def __init__(self):

View File

@ -231,19 +231,16 @@ def pytest_runtest_makereport(__multicall__, item, call):
if not item.config.getvalue("runxfail"):
rep = __multicall__.execute()
rep.keywords['xfail'] = "reason: " + call.excinfo.value.msg
rep.skipped = True
rep.failed = False
rep.outcome = "skipped"
return rep
if call.when == "call":
rep = __multicall__.execute()
evalxfail = getattr(item, '_evalxfail')
if not item.config.getvalue("runxfail") and evalxfail.istrue():
if call.excinfo:
rep.skipped = True
rep.failed = rep.passed = False
rep.outcome = "skipped"
else:
rep.skipped = rep.passed = False
rep.failed = True
rep.outcome = "failed"
rep.keywords['xfail'] = evalxfail.getexplanation()
else:
if 'xfail' in rep.keywords:
@ -275,9 +272,9 @@ def pytest_terminal_summary(terminalreporter):
show_xfailed(terminalreporter, lines)
elif char == "X":
show_xpassed(terminalreporter, lines)
elif char == "f":
elif char in "fF":
show_failed(terminalreporter, lines)
elif char == "s":
elif char in "sS":
show_skipped(terminalreporter, lines)
if lines:
tr._tw.sep("=", "short test summary info")
@ -289,22 +286,24 @@ def show_failed(terminalreporter, lines):
failed = terminalreporter.stats.get("failed")
if failed:
for rep in failed:
pos = terminalreporter.gettestid(rep.item)
pos = rep.nodeid
lines.append("FAIL %s" %(pos, ))
def show_xfailed(terminalreporter, lines):
xfailed = terminalreporter.stats.get("xfailed")
if xfailed:
for rep in xfailed:
pos = terminalreporter.gettestid(rep.item)
pos = rep.nodeid
reason = rep.keywords['xfail']
lines.append("XFAIL %s %s" %(pos, reason))
lines.append("XFAIL %s" % (pos,))
if reason:
lines.append(" " + str(reason))
def show_xpassed(terminalreporter, lines):
xpassed = terminalreporter.stats.get("xpassed")
if xpassed:
for rep in xpassed:
pos = terminalreporter.gettestid(rep.item)
pos = rep.nodeid
reason = rep.keywords['xfail']
lines.append("XPASS %s %s" %(pos, reason))

View File

@ -57,6 +57,17 @@ def getreportopt(config):
reportopts += char
return reportopts
def pytest_report_teststatus(report):
if report.passed:
letter = "."
elif report.skipped:
letter = "s"
elif report.failed:
letter = "F"
if report.when != "call":
letter = "f"
return report.outcome, letter, report.outcome.upper()
class TerminalReporter:
def __init__(self, config, file=None):
self.config = config
@ -104,42 +115,6 @@ class TerminalReporter:
self.ensure_newline()
self._tw.sep(sep, title, **markup)
def getcategoryletterword(self, rep):
res = self.config.hook.pytest_report_teststatus(report=rep)
if res:
return res
for cat in 'skipped failed passed ???'.split():
if getattr(rep, cat, None):
break
return cat, self.getoutcomeletter(rep), self.getoutcomeword(rep)
def getoutcomeletter(self, rep):
return rep.shortrepr
def getoutcomeword(self, rep):
if rep.passed:
return "PASS", dict(green=True)
elif rep.failed:
return "FAIL", dict(red=True)
elif rep.skipped:
return "SKIP"
else:
return "???", dict(red=True)
def gettestid(self, item, relative=True):
fspath = item.fspath
chain = [x for x in item.listchain() if x.fspath == fspath]
chain = chain[1:]
names = [x.name for x in chain if x.name != "()"]
path = item.fspath
if relative:
relpath = path.relto(self.curdir)
if relpath:
path = relpath
names.insert(0, str(path))
return "::".join(names)
def pytest_internalerror(self, excrepr):
for line in str(excrepr).split("\n"):
self.write_line("INTERNALERROR> " + line)
@ -160,21 +135,23 @@ 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._reportinfoline(item)
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_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_logreport(self, report):
rep = report
cat, letter, word = self.getcategoryletterword(rep)
res = self.config.hook.pytest_report_teststatus(report=rep)
cat, letter, word = res
self.stats.setdefault(cat, []).append(rep)
if not letter and not word:
# probably passed setup/teardown
return
@ -182,14 +159,10 @@ class TerminalReporter:
word, markup = word
else:
markup = {}
self.stats.setdefault(cat, []).append(rep)
if not self.config.option.verbose:
fspath = getattr(rep, 'fspath', None)
if not fspath:
fspath = self._getfspath(rep.item)
self.write_fspath_result(fspath, letter)
self.write_fspath_result(rep.fspath, letter)
else:
line = self._reportinfoline(rep.item)
line = self._locationline(rep)
if not hasattr(rep, 'node'):
self.write_ensure_prefix(line, word, **markup)
else:
@ -204,10 +177,10 @@ class TerminalReporter:
if not report.passed:
if report.failed:
self.stats.setdefault("error", []).append(report)
self.write_fspath_result(report.collector.fspath, "E")
self.write_fspath_result(report.fspath, "E")
elif report.skipped:
self.stats.setdefault("skipped", []).append(report)
self.write_fspath_result(report.collector.fspath, "S")
self.write_fspath_result(report.fspath, "S")
def pytest_sessionstart(self, session):
self.write_sep("=", "test session starts", bold=True)
@ -253,14 +226,14 @@ class TerminalReporter:
else:
excrepr.reprcrash.toterminal(self._tw)
def _reportinfoline(self, item):
collect_fspath = self._getfspath(item)
fspath, lineno, msg = self._getreportinfo(item)
if fspath and fspath != collect_fspath:
fspath = "%s <- %s" % (
self.curdir.bestrelpath(collect_fspath),
self.curdir.bestrelpath(fspath))
elif fspath:
def _locationline(self, rep):
#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)
if lineno is not None:
lineno += 1
@ -271,34 +244,24 @@ class TerminalReporter:
elif fspath and lineno:
line = "%(fspath)s:%(lineno)s %(extrapath)s"
else:
line = "[noreportinfo]"
line = "[nolocation]"
return line % locals() + " "
def _getfailureheadline(self, rep):
if hasattr(rep, "collector"):
return str(rep.collector.fspath)
elif hasattr(rep, 'item'):
fspath, lineno, msg = self._getreportinfo(rep.item)
return msg
if hasattr(rep, 'location'):
fspath, lineno, domain = rep.location
return domain
else:
return "test session"
return "test session" # XXX?
def _getreportinfo(self, item):
def _getcrashline(self, rep):
try:
return item.__reportinfo
return str(rep.longrepr.reprcrash)
except AttributeError:
pass
reportinfo = item.config.hook.pytest_report_iteminfo(item=item)
# cache on item
item.__reportinfo = reportinfo
return reportinfo
def _getfspath(self, item):
try:
return item.fspath
except AttributeError:
fspath, lineno, msg = self._getreportinfo(item)
return fspath
try:
return str(rep.longrepr)[:50]
except AttributeError:
return ""
#
# summaries for sessionfinish
@ -310,7 +273,7 @@ class TerminalReporter:
self.write_sep("=", "FAILURES")
for rep in self.stats['failed']:
if tbstyle == "line":
line = rep._getcrashline()
line = self._getcrashline(rep)
self.write_line(line)
else:
msg = self._getfailureheadline(rep)

View File

@ -126,7 +126,6 @@ class Node(object):
style=style)
repr_failure = _repr_failure_py
shortfailurerepr = "F"
class Collector(Node):
"""

View File

@ -6,9 +6,9 @@ import inspect
from py._plugin import hookspec
default_plugins = (
"default python runner pdb capture mark terminal skipping tmpdir monkeypatch "
"recwarn pastebin unittest helpconfig nose assertion genscript "
"junitxml doctest keyword").split()
"default terminal python runner pdb capture mark skipping tmpdir monkeypatch "
"recwarn pastebin unittest helpconfig nose assertion genscript "
"junitxml doctest keyword").split()
def check_old_use(mod, modname):
clsname = modname[len('pytest_'):].capitalize() + "Plugin"

View File

@ -182,6 +182,9 @@ class Collection:
return nodes
def genitems(self, matching, names, result):
if not matching:
assert not names
return result
names = list(names)
name = names and names.pop(0) or None
for node in matching:

View File

@ -8,5 +8,5 @@ def test_functional(testdir):
testdir.runpytest("--hooklog=hook.log")
s = testdir.tmpdir.join("hook.log").read()
assert s.find("pytest_sessionstart") != -1
assert s.find("ItemTestReport") != -1
assert s.find("TestReport") != -1
assert s.find("sessionfinish") != -1

View File

@ -64,7 +64,7 @@ class TestKeywordSelection:
reprec = testdir.inline_run("-s", "-k", keyword, file_test)
passed, skipped, failed = reprec.listoutcomes()
assert len(failed) == 1
assert failed[0].item.name == name
assert failed[0].nodeid.split("::")[-1] == name
assert len(reprec.getcalls('pytest_deselected')) == 1
for keyword in ['test_one', 'est_on']:
@ -92,7 +92,7 @@ class TestKeywordSelection:
py.builtin.print_("keyword", repr(keyword))
passed, skipped, failed = reprec.listoutcomes()
assert len(passed) == 1
assert passed[0].item.name == "test_2"
assert passed[0].nodeid.endswith("test_2")
dlist = reprec.getcalls("pytest_deselected")
assert len(dlist) == 1
assert dlist[0].items[0].name == 'test_1'

View File

@ -53,8 +53,8 @@ class BaseFunctionalTests:
rep = reports[1]
assert rep.passed
assert not rep.failed
assert rep.shortrepr == "."
assert not hasattr(rep, 'longrepr')
assert rep.outcome == "passed"
assert not rep.longrepr
def test_failfunction(self, testdir):
reports = testdir.runitem("""
@ -66,23 +66,8 @@ class BaseFunctionalTests:
assert not rep.skipped
assert rep.failed
assert rep.when == "call"
assert isinstance(rep.longrepr, ReprExceptionInfo)
assert str(rep.shortrepr) == "F"
def test_failfunction_customized_report(self, testdir, LineMatcher):
reports = testdir.runitem("""
def test_func():
assert 0
""")
rep = reports[1]
rep.headerlines += ["hello world"]
tr = py.io.TerminalWriter(stringio=True)
rep.toterminal(tr)
val = tr.stringio.getvalue()
LineMatcher(val.split("\n")).fnmatch_lines([
"*hello world",
"*def test_func():*"
])
assert rep.outcome == "failed"
#assert isinstance(rep.longrepr, ReprExceptionInfo)
def test_skipfunction(self, testdir):
reports = testdir.runitem("""
@ -94,6 +79,7 @@ class BaseFunctionalTests:
assert not rep.failed
assert not rep.passed
assert rep.skipped
assert rep.outcome == "skipped"
#assert rep.skipped.when == "call"
#assert rep.skipped.when == "call"
#assert rep.skipped == "%sreason == "hello"
@ -150,8 +136,8 @@ class BaseFunctionalTests:
assert not rep.passed
assert rep.failed
assert rep.when == "teardown"
assert rep.longrepr.reprcrash.lineno == 3
assert rep.longrepr.reprtraceback.reprentries
#assert rep.longrepr.reprcrash.lineno == 3
#assert rep.longrepr.reprtraceback.reprentries
def test_custom_failure_repr(self, testdir):
testdir.makepyfile(conftest="""
@ -270,6 +256,10 @@ class TestCollectionReports:
assert not rep.failed
assert not rep.skipped
assert rep.passed
locinfo = rep.location
assert locinfo[0] == col.fspath
assert not locinfo[1]
assert locinfo[2] == col.fspath
res = rep.result
assert len(res) == 2
assert res[0].name == "test_func1"
@ -299,7 +289,7 @@ def test_callinfo():
assert "exc" in repr(ci)
# design question: do we want general hooks in python files?
# following passes if withpy defaults to True in pycoll.PyObjMix._getplugins()
# then something like the following functional tests makes sense
@py.test.mark.xfail
def test_runtest_in_module_ordering(testdir):
p1 = testdir.makepyfile("""

View File

@ -183,8 +183,10 @@ class TestXFail:
""")
result = testdir.runpytest(p, '--report=xfailed', )
result.stdout.fnmatch_lines([
"*test_one*test_this*NOTRUN*noway",
"*test_one*test_this_true*NOTRUN*condition:*True*",
"*test_one*test_this*",
"*NOTRUN*noway",
"*test_one*test_this_true*",
"*NOTRUN*condition:*True*",
"*1 passed*",
])
@ -199,7 +201,8 @@ class TestXFail:
""")
result = testdir.runpytest(p, '--report=xfailed', )
result.stdout.fnmatch_lines([
"*test_one*test_this*NOTRUN*hello",
"*test_one*test_this*",
"*NOTRUN*hello",
"*1 xfailed*",
])
@ -229,7 +232,8 @@ class TestXFail:
])
result = testdir.runpytest(p, "-rx")
result.stdout.fnmatch_lines([
"*XFAIL*test_this*reason:*hello*",
"*XFAIL*test_this*",
"*reason:*hello*",
])
result = testdir.runpytest(p, "--runxfail")
result.stdout.fnmatch_lines([
@ -252,7 +256,8 @@ class TestXFail:
])
result = testdir.runpytest(p, "-rx")
result.stdout.fnmatch_lines([
"*XFAIL*test_this*reason:*hello*",
"*XFAIL*test_this*",
"*reason:*hello*",
])
result = testdir.runpytest(p, "--runxfail")
result.stdout.fnmatch_lines([
@ -286,7 +291,8 @@ class TestXFail:
""")
result = testdir.runpytest(p, '-rxX')
result.stdout.fnmatch_lines([
"*XFAIL*test_this*NOTRUN*",
"*XFAIL*test_this*",
"*NOTRUN*",
])
def test_dynamic_xfail_set_during_funcarg_setup(self, testdir):
@ -360,7 +366,6 @@ def test_skipif_class(testdir):
def test_skip_reasons_folding():
from py._plugin import pytest_runner as runner
from py._plugin.pytest_skipping import folded_skips
class longrepr:
class reprcrash:
@ -368,12 +373,15 @@ def test_skip_reasons_folding():
lineno = 3
message = "justso"
ev1 = runner.CollectReport(None, None)
class X:
pass
ev1 = X()
ev1.when = "execute"
ev1.skipped = True
ev1.longrepr = longrepr
ev2 = runner.ItemTestReport(None, excinfo=longrepr)
ev2 = X()
ev2.longrepr = longrepr
ev2.skipped = True
l = folded_skips([ev1, ev2])

View File

@ -89,22 +89,7 @@ class TestTerminal:
assert lines[1].endswith("xy.py .")
assert lines[2] == "hello world"
def test_testid(self, testdir, linecomp):
func,method = testdir.getitems("""
def test_func():
pass
class TestClass:
def test_method(self):
pass
""")
tr = TerminalReporter(func.config, file=linecomp.stringio)
id = tr.gettestid(func)
assert id.endswith("test_testid.py::test_func")
fspath = py.path.local(id.split("::")[0])
assert fspath.check()
id = tr.gettestid(method)
assert id.endswith("test_testid.py::TestClass::test_method")
@py.test.mark.xfail(reason="re-implement ItemStart events")
def test_show_path_before_running_test(self, testdir, linecomp):
item = testdir.getitem("def test_func(): pass")
tr = TerminalReporter(item.config, file=linecomp.stringio)
@ -114,6 +99,7 @@ class TestTerminal:
"*test_show_path_before_running_test.py*"
])
@py.test.mark.xfail(reason="re-implement ItemStart events")
def test_itemreport_reportinfo(self, testdir, linecomp):
testdir.makeconftest("""
import py
@ -130,6 +116,7 @@ class TestTerminal:
"*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:
@ -144,6 +131,7 @@ class TestTerminal:
"*FGHJ:43: custom*"
])
@py.test.mark.xfail(reason="re-implement subclassing precision reporting")
def test_itemreport_subclasses_show_subclassed_file(self, testdir):
p1 = testdir.makepyfile(test_p1="""
class BaseTests:
@ -210,8 +198,8 @@ class TestCollectonly:
linecomp.assert_contains_lines([
" <Function 'test_func'>",
])
rep.config.hook.pytest_collectreport(
report=runner.CollectReport(modcol, [], excinfo=None))
report = rep.config.hook.pytest_make_collect_report(collector=modcol)
rep.config.hook.pytest_collectreport(report=report)
assert rep.indent == indent
def test_collectonly_skipped_module(self, testdir, linecomp):

View File

@ -140,7 +140,7 @@ class TestCollectPluginHookRelay:
assert "world" in wascalled
# make sure the directories do not get double-appended
colreports = reprec.getreports("pytest_collectreport")
names = [rep.collector.name for rep in colreports]
names = [rep.nodenames[-1] for rep in colreports]
assert names.count("hello") == 1
class TestPrunetraceback:
@ -180,6 +180,7 @@ class TestPrunetraceback:
"*hello world*",
])
@py.test.mark.xfail(reason="other mechanism for adding to reporting needed")
def test_collect_report_postprocessing(self, testdir):
p = testdir.makepyfile("""
import not_exists
@ -226,11 +227,13 @@ class TestCustomConftests:
testdir.mkdir("hello")
testdir.makepyfile(test_world="#")
reprec = testdir.inline_run(testdir.tmpdir)
names = [rep.collector.name for rep in reprec.getreports("pytest_collectreport")]
names = [rep.nodenames[-1]
for rep in reprec.getreports("pytest_collectreport")]
assert 'hello' not in names
assert 'test_world.py' not in names
reprec = testdir.inline_run(testdir.tmpdir, "--XX")
names = [rep.collector.name for rep in reprec.getreports("pytest_collectreport")]
names = [rep.nodenames[-1]
for rep in reprec.getreports("pytest_collectreport")]
assert 'hello' in names
assert 'test_world.py' in names

View File

@ -60,8 +60,8 @@ class TestCollection:
("pytest_collectstart", "collector.fspath == p"),
("pytest_make_collect_report", "collector.fspath == p"),
("pytest_pycollect_makeitem", "name == 'test_func'"),
("pytest_collectreport", "report.collector.fspath == p"),
("pytest_collectreport", "report.collector.fspath == topdir")
("pytest_collectreport", "report.fspath == p"),
("pytest_collectreport", "report.fspath == topdir")
])
def test_collect_protocol_method(self, testdir):
@ -115,9 +115,9 @@ class TestCollection:
("pytest_collectstart",
"collector.__class__.__name__ == 'Module'"),
("pytest_pycollect_makeitem", "name == 'test_func'"),
("pytest_collectreport", "report.collector.fspath == p"),
("pytest_collectreport", "report.fspath == p"),
("pytest_collectreport",
"report.collector.fspath == report.collector.collection.topdir")
"report.fspath == %r" % str(rcol.topdir)),
])
def test_collect_subdir_event_ordering(self, testdir):
@ -135,8 +135,8 @@ class TestCollection:
("pytest_collectstart", "collector.fspath == aaa"),
("pytest_collectstart", "collector.fspath == test_aaa"),
("pytest_pycollect_makeitem", "name == 'test_func'"),
("pytest_collectreport", "report.collector.fspath == test_aaa"),
("pytest_collectreport", "report.collector.fspath == aaa"),
("pytest_collectreport", "report.fspath == test_aaa"),
("pytest_collectreport", "report.fspath == aaa"),
])
def test_collect_two_commandline_args(self, testdir):
@ -156,10 +156,10 @@ class TestCollection:
hookrec.hookrecorder.contains([
("pytest_collectstart", "collector.fspath == aaa"),
("pytest_pycollect_makeitem", "name == 'test_func'"),
("pytest_collectreport", "report.collector.fspath == aaa"),
("pytest_collectreport", "report.fspath == aaa"),
("pytest_collectstart", "collector.fspath == bbb"),
("pytest_pycollect_makeitem", "name == 'test_func'"),
("pytest_collectreport", "report.collector.fspath == bbb"),
("pytest_collectreport", "report.fspath == bbb"),
])
def test_serialization_byid(self, testdir):

View File

@ -17,9 +17,9 @@ class SessionTests:
assert len(skipped) == 0
assert len(passed) == 1
assert len(failed) == 3
assert failed[0].item.name == "test_one_one"
assert failed[1].item.name == "test_other"
assert failed[2].item.name == "test_two"
assert failed[0].nodenames[-1] == "test_one_one"
assert failed[1].nodenames[-1] == "test_other"
assert failed[2].nodenames[-1] == "test_two"
itemstarted = reprec.getcalls("pytest_log_itemcollect")
assert len(itemstarted) == 4
colstarted = reprec.getcalls("pytest_collectstart")