add unit-tests for xfail and refine xfail handling and reporting
--HG-- branch : trunk
This commit is contained in:
@@ -123,44 +123,83 @@ within test or setup code. Example::
|
||||
py.test.skip("unsuppored configuration")
|
||||
|
||||
"""
|
||||
# XXX py.test.skip, .importorskip and the Skipped class
|
||||
# should also be defined in this plugin, requires thought/changes
|
||||
|
||||
import py
|
||||
|
||||
class MarkEvaluator:
|
||||
def __init__(self, item, name):
|
||||
self.item = item
|
||||
self.name = name
|
||||
self.holder = getattr(item.obj, name, None)
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self.holder)
|
||||
__nonzero__ = __bool__
|
||||
|
||||
def istrue(self):
|
||||
if self.holder:
|
||||
d = {'os': py.std.os, 'sys': py.std.sys, 'config': self.item.config}
|
||||
self.result = True
|
||||
for expr in self.holder.args:
|
||||
self.expr = expr
|
||||
if isinstance(expr, str):
|
||||
result = cached_eval(self.item.config, expr, d)
|
||||
else:
|
||||
result = expr
|
||||
if not result:
|
||||
self.result = False
|
||||
self.expr = expr
|
||||
break
|
||||
return getattr(self, 'result', False)
|
||||
|
||||
def get(self, attr, default=None):
|
||||
return self.holder.kwargs.get(attr, default)
|
||||
|
||||
def getexplanation(self):
|
||||
expl = self.get('reason', None)
|
||||
if not expl:
|
||||
if not hasattr(self, 'expr'):
|
||||
return "condition: True"
|
||||
else:
|
||||
return "condition: " + self.expr
|
||||
return expl
|
||||
|
||||
|
||||
def pytest_runtest_setup(item):
|
||||
if not isinstance(item, py.test.collect.Function):
|
||||
return
|
||||
expr, result = evalexpression(item, 'skipif')
|
||||
if result:
|
||||
py.test.skip(expr)
|
||||
holder = getattr(item.obj, 'xfail', None)
|
||||
if holder and not holder.kwargs.get('run', True):
|
||||
py.test.skip("<did not run>")
|
||||
evalskip = MarkEvaluator(item, 'skipif')
|
||||
if evalskip.istrue():
|
||||
py.test.skip(evalskip.getexplanation())
|
||||
item._evalxfail = MarkEvaluator(item, 'xfail')
|
||||
if item._evalxfail.istrue():
|
||||
if not item._evalxfail.get('run', True):
|
||||
py.test.skip("xfail")
|
||||
|
||||
def pytest_runtest_makereport(__multicall__, item, call):
|
||||
if not isinstance(item, py.test.collect.Function):
|
||||
return
|
||||
if call.when == "setup":
|
||||
holder = getattr(item.obj, 'xfail', None)
|
||||
if holder:
|
||||
rep = __multicall__.execute()
|
||||
reason = holder.kwargs.get("reason", "<no reason given>")
|
||||
rep.keywords['xfail'] = "[not run] " + reason
|
||||
return rep
|
||||
evalxfail = getattr(item, '_evalxfail', None)
|
||||
if not evalxfail:
|
||||
return
|
||||
elif call.when == "call":
|
||||
expr, result = evalexpression(item, 'xfail')
|
||||
if call.when == "setup":
|
||||
rep = __multicall__.execute()
|
||||
if result:
|
||||
if rep.skipped and evalxfail.istrue():
|
||||
expl = evalxfail.getexplanation()
|
||||
if not evalxfail.get("run", True):
|
||||
expl = "[NOTRUN] " + expl
|
||||
rep.keywords['xfail'] = expl
|
||||
return rep
|
||||
elif call.when == "call":
|
||||
rep = __multicall__.execute()
|
||||
if evalxfail.istrue():
|
||||
if call.excinfo:
|
||||
rep.skipped = True
|
||||
rep.failed = rep.passed = False
|
||||
else:
|
||||
rep.skipped = rep.passed = False
|
||||
rep.failed = True
|
||||
rep.keywords['xfail'] = expr
|
||||
rep.keywords['xfail'] = evalxfail.getexplanation()
|
||||
else:
|
||||
if 'xfail' in rep.keywords:
|
||||
del rep.keywords['xfail']
|
||||
@@ -190,43 +229,17 @@ def show_xfailed(terminalreporter):
|
||||
return
|
||||
tr.write_sep("_", "expected failures")
|
||||
for rep in xfailed:
|
||||
entry = rep.longrepr.reprcrash
|
||||
modpath = rep.item.getmodpath(includemodule=True)
|
||||
pos = "%s %s:%d: " %(modpath, entry.path, entry.lineno)
|
||||
if rep.keywords['xfail']:
|
||||
reason = rep.keywords['xfail'].strip()
|
||||
else:
|
||||
reason = rep.longrepr.reprcrash.message
|
||||
i = reason.find("\n")
|
||||
if i != -1:
|
||||
reason = reason[:i]
|
||||
pos = terminalreporter.gettestid(rep.item)
|
||||
reason = rep.keywords['xfail']
|
||||
tr._tw.line("%s %s" %(pos, reason))
|
||||
|
||||
xpassed = terminalreporter.stats.get("xpassed")
|
||||
if xpassed:
|
||||
tr.write_sep("_", "UNEXPECTEDLY PASSING TESTS")
|
||||
for rep in xpassed:
|
||||
fspath, lineno, modpath = rep.item.reportinfo()
|
||||
pos = "%s %s:%d: unexpectedly passing" %(modpath, fspath, lineno)
|
||||
tr._tw.line(pos)
|
||||
|
||||
|
||||
def evalexpression(item, keyword):
|
||||
if isinstance(item, py.test.collect.Function):
|
||||
markholder = getattr(item.obj, keyword, None)
|
||||
result = False
|
||||
if markholder:
|
||||
d = {'os': py.std.os, 'sys': py.std.sys, 'config': item.config}
|
||||
expr, result = None, True
|
||||
for expr in markholder.args:
|
||||
if isinstance(expr, str):
|
||||
result = cached_eval(item.config, expr, d)
|
||||
else:
|
||||
result = expr
|
||||
if not result:
|
||||
break
|
||||
return expr, result
|
||||
return None, False
|
||||
pos = terminalreporter.gettestid(rep.item)
|
||||
reason = rep.keywords['xfail']
|
||||
tr._tw.line("%s %s" %(pos, reason))
|
||||
|
||||
def cached_eval(config, expr, d):
|
||||
if not hasattr(config, '_evalcache'):
|
||||
|
||||
Reference in New Issue
Block a user