2010-11-06 18:38:53 +08:00
|
|
|
""" basic collect and runtest protocol implementations """
|
2008-08-16 23:26:59 +08:00
|
|
|
|
2011-09-23 16:53:03 +08:00
|
|
|
import py, sys, time
|
2010-09-26 22:23:44 +08:00
|
|
|
from py._code.code import TerminalRepr
|
2010-04-28 03:13:09 +08:00
|
|
|
|
|
|
|
def pytest_namespace():
|
|
|
|
return {
|
2010-11-06 06:37:31 +08:00
|
|
|
'fail' : fail,
|
2010-04-28 03:13:09 +08:00
|
|
|
'skip' : skip,
|
|
|
|
'importorskip' : importorskip,
|
2010-07-27 03:15:15 +08:00
|
|
|
'exit' : exit,
|
2010-04-28 03:13:09 +08:00
|
|
|
}
|
2009-05-23 01:57:11 +08:00
|
|
|
|
2009-06-09 00:31:10 +08:00
|
|
|
#
|
2010-07-27 03:15:15 +08:00
|
|
|
# pytest plugin hooks
|
2009-06-09 00:31:10 +08:00
|
|
|
|
2011-11-09 01:20:56 +08:00
|
|
|
def pytest_addoption(parser):
|
|
|
|
group = parser.getgroup("terminal reporting", "reporting", after="general")
|
|
|
|
group.addoption('--durations',
|
2011-11-09 19:04:37 +08:00
|
|
|
action="store", type="int", default=None, metavar="N",
|
2011-11-09 01:20:56 +08:00
|
|
|
help="show N slowest setup/test durations (N=0 for all)."),
|
|
|
|
|
|
|
|
def pytest_terminal_summary(terminalreporter):
|
|
|
|
durations = terminalreporter.config.option.durations
|
|
|
|
if durations is None:
|
|
|
|
return
|
|
|
|
tr = terminalreporter
|
2011-11-19 01:35:23 +08:00
|
|
|
dlist = []
|
|
|
|
for replist in tr.stats.values():
|
2011-11-09 01:20:56 +08:00
|
|
|
for rep in replist:
|
2011-11-09 03:00:25 +08:00
|
|
|
if hasattr(rep, 'duration'):
|
2011-11-19 01:59:52 +08:00
|
|
|
dlist.append(rep)
|
2011-11-19 01:35:23 +08:00
|
|
|
if not dlist:
|
2011-11-09 01:20:56 +08:00
|
|
|
return
|
2011-11-19 01:59:52 +08:00
|
|
|
dlist.sort(key=lambda x: x.duration)
|
2011-11-19 01:35:23 +08:00
|
|
|
dlist.reverse()
|
2011-11-09 01:20:56 +08:00
|
|
|
if not durations:
|
|
|
|
tr.write_sep("=", "slowest test durations")
|
|
|
|
else:
|
|
|
|
tr.write_sep("=", "slowest %s test durations" % durations)
|
2011-11-19 01:35:23 +08:00
|
|
|
dlist = dlist[:durations]
|
2011-11-09 01:20:56 +08:00
|
|
|
|
2011-11-19 01:59:52 +08:00
|
|
|
for rep in dlist:
|
2011-11-09 01:20:56 +08:00
|
|
|
nodeid = rep.nodeid.replace("::()::", "::")
|
2011-11-18 22:59:39 +08:00
|
|
|
tr.write_line("%02.2fs %-8s %s" %
|
2011-11-19 01:59:52 +08:00
|
|
|
(rep.duration, rep.when, nodeid))
|
2011-11-09 01:20:56 +08:00
|
|
|
|
2011-05-27 06:08:56 +08:00
|
|
|
def pytest_sessionstart(session):
|
|
|
|
session._setupstate = SetupState()
|
2009-06-09 00:31:10 +08:00
|
|
|
|
2009-07-20 20:01:40 +08:00
|
|
|
def pytest_sessionfinish(session, exitstatus):
|
2011-05-27 06:08:56 +08:00
|
|
|
hook = session.config.hook
|
|
|
|
rep = hook.pytest__teardown_final(session=session)
|
|
|
|
if rep:
|
|
|
|
hook.pytest__teardown_final_logerror(session=session, report=rep)
|
|
|
|
session.exitstatus = 1
|
2009-07-09 19:12:00 +08:00
|
|
|
|
2010-09-26 22:23:45 +08:00
|
|
|
class NodeInfo:
|
2010-11-06 16:58:04 +08:00
|
|
|
def __init__(self, location):
|
2010-09-26 22:23:45 +08:00
|
|
|
self.location = location
|
|
|
|
|
2011-11-19 00:01:29 +08:00
|
|
|
def perform_pending_teardown(config, nextitem):
|
|
|
|
try:
|
|
|
|
olditem, log = config._pendingteardown
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
del config._pendingteardown
|
|
|
|
olditem.nextitem = nextitem
|
|
|
|
call_and_report(olditem, "teardown", log)
|
|
|
|
|
2009-06-09 00:31:10 +08:00
|
|
|
def pytest_runtest_protocol(item):
|
2011-11-19 00:01:29 +08:00
|
|
|
perform_pending_teardown(item.config, item)
|
2010-09-26 22:23:45 +08:00
|
|
|
item.ihook.pytest_runtest_logstart(
|
2010-11-06 16:58:04 +08:00
|
|
|
nodeid=item.nodeid, location=item.location,
|
2010-09-26 22:23:45 +08:00
|
|
|
)
|
2011-11-19 00:01:29 +08:00
|
|
|
runtestprotocol(item, teardowndelayed=True)
|
2009-06-09 00:31:10 +08:00
|
|
|
return True
|
|
|
|
|
2011-11-19 00:01:29 +08:00
|
|
|
def runtestprotocol(item, log=True, teardowndelayed=False):
|
2009-06-09 22:08:34 +08:00
|
|
|
rep = call_and_report(item, "setup", log)
|
|
|
|
reports = [rep]
|
|
|
|
if rep.passed:
|
|
|
|
reports.append(call_and_report(item, "call", log))
|
2011-11-19 00:01:29 +08:00
|
|
|
if teardowndelayed:
|
|
|
|
item.config._pendingteardown = item, log
|
|
|
|
else:
|
|
|
|
reports.append(call_and_report(item, "teardown", log))
|
2009-06-09 22:08:34 +08:00
|
|
|
return reports
|
|
|
|
|
2009-06-09 00:31:10 +08:00
|
|
|
def pytest_runtest_setup(item):
|
2011-05-27 06:08:56 +08:00
|
|
|
item.session._setupstate.prepare(item)
|
2009-06-09 00:31:10 +08:00
|
|
|
|
|
|
|
def pytest_runtest_call(item):
|
2010-10-14 00:41:53 +08:00
|
|
|
item.runtest()
|
2009-06-09 00:31:10 +08:00
|
|
|
|
|
|
|
def pytest_runtest_teardown(item):
|
2011-05-27 06:08:56 +08:00
|
|
|
item.session._setupstate.teardown_exact(item)
|
2009-06-09 00:31:10 +08:00
|
|
|
|
2009-07-31 20:22:02 +08:00
|
|
|
def pytest__teardown_final(session):
|
2011-11-19 00:01:29 +08:00
|
|
|
perform_pending_teardown(session.config, None)
|
|
|
|
#call = CallInfo(session._setupstate.teardown_all, when="teardown")
|
|
|
|
#if call.excinfo:
|
|
|
|
# ntraceback = call.excinfo.traceback .cut(excludepath=py._pydir)
|
|
|
|
# call.excinfo.traceback = ntraceback.filter()
|
|
|
|
# longrepr = call.excinfo.getrepr(funcargs=True)
|
|
|
|
# return TeardownErrorReport(longrepr)
|
2009-07-31 20:22:02 +08:00
|
|
|
|
2009-08-06 21:02:38 +08:00
|
|
|
def pytest_report_teststatus(report):
|
|
|
|
if report.when in ("setup", "teardown"):
|
|
|
|
if report.failed:
|
2010-07-27 03:15:15 +08:00
|
|
|
# category, shortletter, verbose-word
|
2009-07-31 20:22:02 +08:00
|
|
|
return "error", "E", "ERROR"
|
2009-08-06 21:02:38 +08:00
|
|
|
elif report.skipped:
|
2009-07-31 20:22:02 +08:00
|
|
|
return "skipped", "s", "SKIPPED"
|
|
|
|
else:
|
|
|
|
return "", "", ""
|
2010-09-26 22:23:44 +08:00
|
|
|
|
|
|
|
|
2009-06-09 00:31:10 +08:00
|
|
|
#
|
|
|
|
# Implementation
|
|
|
|
|
2009-06-09 22:08:34 +08:00
|
|
|
def call_and_report(item, when, log=True):
|
2009-07-31 20:22:02 +08:00
|
|
|
call = call_runtest_hook(item, when)
|
2009-12-30 09:36:58 +08:00
|
|
|
hook = item.ihook
|
2009-06-09 22:08:34 +08:00
|
|
|
report = hook.pytest_runtest_makereport(item=item, call=call)
|
2011-11-09 01:53:46 +08:00
|
|
|
if log:
|
2010-07-27 03:15:15 +08:00
|
|
|
hook.pytest_runtest_logreport(report=report)
|
2009-06-09 22:08:34 +08:00
|
|
|
return report
|
|
|
|
|
2009-07-31 20:22:02 +08:00
|
|
|
def call_runtest_hook(item, when):
|
2010-07-27 03:15:15 +08:00
|
|
|
hookname = "pytest_runtest_" + when
|
2009-12-30 09:36:58 +08:00
|
|
|
ihook = getattr(item.ihook, hookname)
|
|
|
|
return CallInfo(lambda: ihook(item=item), when=when)
|
2009-07-31 20:22:02 +08:00
|
|
|
|
|
|
|
class CallInfo:
|
2010-11-06 18:38:53 +08:00
|
|
|
""" Result/Exception info a function invocation. """
|
2010-10-11 18:54:28 +08:00
|
|
|
#: None or ExceptionInfo object.
|
2010-07-27 03:15:15 +08:00
|
|
|
excinfo = None
|
2009-07-31 20:22:02 +08:00
|
|
|
def __init__(self, func, when):
|
2010-11-06 18:38:53 +08:00
|
|
|
#: context of invocation: one of "setup", "call",
|
|
|
|
#: "teardown", "memocollect"
|
2010-07-27 03:15:15 +08:00
|
|
|
self.when = when
|
2011-09-23 16:53:03 +08:00
|
|
|
self.start = time.time()
|
2009-05-23 01:57:11 +08:00
|
|
|
try:
|
2011-09-23 16:53:03 +08:00
|
|
|
try:
|
|
|
|
self.result = func()
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
raise
|
|
|
|
except:
|
|
|
|
self.excinfo = py.code.ExceptionInfo()
|
|
|
|
finally:
|
|
|
|
self.stop = time.time()
|
2009-04-04 04:16:02 +08:00
|
|
|
|
2009-08-06 20:34:19 +08:00
|
|
|
def __repr__(self):
|
|
|
|
if self.excinfo:
|
|
|
|
status = "exception: %s" % str(self.excinfo.value)
|
|
|
|
else:
|
|
|
|
status = "result: %r" % (self.result,)
|
|
|
|
return "<CallInfo when=%r %s>" % (self.when, status)
|
|
|
|
|
2010-11-14 04:03:28 +08:00
|
|
|
def getslaveinfoline(node):
|
|
|
|
try:
|
|
|
|
return node._slaveinfocache
|
|
|
|
except AttributeError:
|
|
|
|
d = node.slaveinfo
|
|
|
|
ver = "%s.%s.%s" % d['version_info'][:3]
|
|
|
|
node._slaveinfocache = s = "[%s] %s -- Python %s %s" % (
|
|
|
|
d['id'], d['sysplatform'], ver, d['executable'])
|
|
|
|
return s
|
|
|
|
|
2009-04-04 08:34:20 +08:00
|
|
|
class BaseReport(object):
|
|
|
|
def toterminal(self, out):
|
2010-07-27 03:15:15 +08:00
|
|
|
longrepr = self.longrepr
|
2010-11-14 04:03:28 +08:00
|
|
|
if hasattr(self, 'node'):
|
|
|
|
out.line(getslaveinfoline(self.node))
|
2009-04-04 08:34:20 +08:00
|
|
|
if hasattr(longrepr, 'toterminal'):
|
|
|
|
longrepr.toterminal(out)
|
|
|
|
else:
|
|
|
|
out.line(str(longrepr))
|
2010-07-04 23:06:50 +08:00
|
|
|
|
2010-09-26 22:23:44 +08:00
|
|
|
passed = property(lambda x: x.outcome == "passed")
|
|
|
|
failed = property(lambda x: x.outcome == "failed")
|
|
|
|
skipped = property(lambda x: x.outcome == "skipped")
|
2010-07-27 03:15:15 +08:00
|
|
|
|
2010-11-06 16:58:04 +08:00
|
|
|
@property
|
|
|
|
def fspath(self):
|
|
|
|
return self.nodeid.split("::")[0]
|
2009-04-04 08:34:20 +08:00
|
|
|
|
2010-09-26 22:23:44 +08:00
|
|
|
def pytest_runtest_makereport(item, call):
|
|
|
|
when = call.when
|
2011-09-23 16:53:03 +08:00
|
|
|
duration = call.stop-call.start
|
2010-09-26 22:23:44 +08:00
|
|
|
keywords = dict([(x,1) for x in item.keywords])
|
2010-11-23 22:42:23 +08:00
|
|
|
excinfo = call.excinfo
|
2010-09-26 22:23:44 +08:00
|
|
|
if not call.excinfo:
|
|
|
|
outcome = "passed"
|
|
|
|
longrepr = None
|
|
|
|
else:
|
2010-11-06 16:58:04 +08:00
|
|
|
excinfo = call.excinfo
|
2010-09-26 22:23:44 +08:00
|
|
|
if not isinstance(excinfo, py.code.ExceptionInfo):
|
|
|
|
outcome = "failed"
|
|
|
|
longrepr = excinfo
|
|
|
|
elif excinfo.errisinstance(py.test.skip.Exception):
|
|
|
|
outcome = "skipped"
|
2011-03-11 22:43:24 +08:00
|
|
|
r = excinfo._getreprcrash()
|
2010-11-14 04:03:28 +08:00
|
|
|
longrepr = (str(r.path), r.lineno, r.message)
|
2009-04-04 08:34:20 +08:00
|
|
|
else:
|
2010-09-26 22:23:44 +08:00
|
|
|
outcome = "failed"
|
|
|
|
if call.when == "call":
|
|
|
|
longrepr = item.repr_failure(excinfo)
|
|
|
|
else: # exception in setup or teardown
|
|
|
|
longrepr = item._repr_failure_py(excinfo)
|
2010-11-06 16:58:04 +08:00
|
|
|
return TestReport(item.nodeid, item.location,
|
2011-09-23 16:53:03 +08:00
|
|
|
keywords, outcome, longrepr, when,
|
|
|
|
duration=duration)
|
2010-09-26 22:23:44 +08:00
|
|
|
|
|
|
|
class TestReport(BaseReport):
|
2010-10-11 18:54:28 +08:00
|
|
|
""" Basic test report object (also used for setup and teardown calls if
|
|
|
|
they fail).
|
|
|
|
"""
|
2010-11-06 16:58:04 +08:00
|
|
|
def __init__(self, nodeid, location,
|
2011-09-23 16:53:03 +08:00
|
|
|
keywords, outcome, longrepr, when, sections=(), duration=0):
|
2010-10-11 18:54:28 +08:00
|
|
|
#: normalized collection node id
|
2010-09-26 22:23:44 +08:00
|
|
|
self.nodeid = nodeid
|
2010-10-11 18:54:28 +08:00
|
|
|
|
|
|
|
#: a (filesystempath, lineno, domaininfo) tuple indicating the
|
|
|
|
#: actual location of a test item - it might be different from the
|
|
|
|
#: collected one e.g. if a method is inherited from a different module.
|
2010-09-26 22:23:44 +08:00
|
|
|
self.location = location
|
2010-10-11 18:54:28 +08:00
|
|
|
|
|
|
|
#: a name -> value dictionary containing all keywords and
|
|
|
|
#: markers associated with a test invocation.
|
2010-09-26 22:23:44 +08:00
|
|
|
self.keywords = keywords
|
2011-11-09 01:20:56 +08:00
|
|
|
|
2010-10-11 18:54:28 +08:00
|
|
|
#: test outcome, always one of "passed", "failed", "skipped".
|
2010-09-26 22:23:44 +08:00
|
|
|
self.outcome = outcome
|
2010-10-11 18:54:28 +08:00
|
|
|
|
|
|
|
#: None or a failure representation.
|
2010-09-26 22:23:44 +08:00
|
|
|
self.longrepr = longrepr
|
2011-11-09 01:20:56 +08:00
|
|
|
|
2010-10-11 18:54:28 +08:00
|
|
|
#: one of 'setup', 'call', 'teardown' to indicate runtest phase.
|
2010-09-26 22:23:44 +08:00
|
|
|
self.when = when
|
2009-05-23 01:56:05 +08:00
|
|
|
|
2011-07-13 05:09:03 +08:00
|
|
|
#: list of (secname, data) extra information which needs to
|
|
|
|
#: marshallable
|
|
|
|
self.sections = list(sections)
|
|
|
|
|
2011-09-23 16:53:03 +08:00
|
|
|
#: time it took to run just the test
|
|
|
|
self.duration = duration
|
|
|
|
|
2009-07-31 20:22:02 +08:00
|
|
|
def __repr__(self):
|
2010-09-26 22:23:44 +08:00
|
|
|
return "<TestReport %r when=%r outcome=%r>" % (
|
|
|
|
self.nodeid, self.when, self.outcome)
|
2009-04-05 03:06:20 +08:00
|
|
|
|
2009-07-31 20:22:02 +08:00
|
|
|
class TeardownErrorReport(BaseReport):
|
2010-09-26 22:23:44 +08:00
|
|
|
outcome = "failed"
|
2009-07-31 20:22:02 +08:00
|
|
|
when = "teardown"
|
2010-09-28 18:59:48 +08:00
|
|
|
def __init__(self, longrepr):
|
|
|
|
self.longrepr = longrepr
|
2011-07-13 05:09:03 +08:00
|
|
|
self.sections = []
|
2009-07-31 20:22:02 +08:00
|
|
|
|
2010-09-26 22:23:44 +08:00
|
|
|
def pytest_make_collect_report(collector):
|
2010-11-06 16:58:04 +08:00
|
|
|
call = CallInfo(collector._memocollect, "memocollect")
|
2010-11-14 04:03:28 +08:00
|
|
|
longrepr = None
|
2010-11-06 16:58:04 +08:00
|
|
|
if not call.excinfo:
|
2010-09-26 22:23:44 +08:00
|
|
|
outcome = "passed"
|
|
|
|
else:
|
2010-11-06 16:58:04 +08:00
|
|
|
if call.excinfo.errisinstance(py.test.skip.Exception):
|
2010-09-26 22:23:44 +08:00
|
|
|
outcome = "skipped"
|
2010-11-14 04:03:28 +08:00
|
|
|
r = collector._repr_failure_py(call.excinfo, "line").reprcrash
|
|
|
|
longrepr = (str(r.path), r.lineno, r.message)
|
2010-09-26 22:23:44 +08:00
|
|
|
else:
|
|
|
|
outcome = "failed"
|
2010-11-06 16:58:04 +08:00
|
|
|
errorinfo = collector.repr_failure(call.excinfo)
|
2010-09-26 22:23:44 +08:00
|
|
|
if not hasattr(errorinfo, "toterminal"):
|
|
|
|
errorinfo = CollectErrorRepr(errorinfo)
|
|
|
|
longrepr = errorinfo
|
2010-11-06 16:58:04 +08:00
|
|
|
return CollectReport(collector.nodeid, outcome, longrepr,
|
2010-11-14 04:03:28 +08:00
|
|
|
getattr(call, 'result', None))
|
2010-09-26 22:23:44 +08:00
|
|
|
|
|
|
|
class CollectReport(BaseReport):
|
2011-07-13 05:09:03 +08:00
|
|
|
def __init__(self, nodeid, outcome, longrepr, result, sections=()):
|
2010-09-26 22:23:44 +08:00
|
|
|
self.nodeid = nodeid
|
|
|
|
self.outcome = outcome
|
|
|
|
self.longrepr = longrepr
|
2010-10-26 05:08:56 +08:00
|
|
|
self.result = result or []
|
2011-07-13 05:09:03 +08:00
|
|
|
self.sections = list(sections)
|
2010-09-26 22:23:44 +08:00
|
|
|
|
|
|
|
@property
|
|
|
|
def location(self):
|
|
|
|
return (self.fspath, None, self.fspath)
|
|
|
|
|
|
|
|
def __repr__(self):
|
2010-11-06 16:58:04 +08:00
|
|
|
return "<CollectReport %r lenresult=%s outcome=%r>" % (
|
|
|
|
self.nodeid, len(self.result), self.outcome)
|
2010-09-26 22:23:44 +08:00
|
|
|
|
|
|
|
class CollectErrorRepr(TerminalRepr):
|
|
|
|
def __init__(self, msg):
|
|
|
|
self.longrepr = msg
|
|
|
|
def toterminal(self, out):
|
|
|
|
out.line(str(self.longrepr), red=True)
|
|
|
|
|
2009-04-05 03:06:20 +08:00
|
|
|
class SetupState(object):
|
|
|
|
""" shared state for setting up/tearing down test items or collectors. """
|
|
|
|
def __init__(self):
|
|
|
|
self.stack = []
|
2009-05-19 01:06:16 +08:00
|
|
|
self._finalizers = {}
|
|
|
|
|
|
|
|
def addfinalizer(self, finalizer, colitem):
|
2010-07-27 03:15:15 +08:00
|
|
|
""" attach a finalizer to the given colitem.
|
|
|
|
if colitem is None, this will add a finalizer that
|
|
|
|
is called at the end of teardown_all().
|
2009-05-21 20:37:30 +08:00
|
|
|
"""
|
2009-08-29 01:16:15 +08:00
|
|
|
assert hasattr(finalizer, '__call__')
|
2009-05-19 01:06:16 +08:00
|
|
|
#assert colitem in self.stack
|
|
|
|
self._finalizers.setdefault(colitem, []).append(finalizer)
|
|
|
|
|
2009-05-21 20:37:30 +08:00
|
|
|
def _pop_and_teardown(self):
|
|
|
|
colitem = self.stack.pop()
|
|
|
|
self._teardown_with_finalization(colitem)
|
|
|
|
|
2009-07-08 22:41:30 +08:00
|
|
|
def _callfinalizers(self, colitem):
|
2009-05-19 01:06:16 +08:00
|
|
|
finalizers = self._finalizers.pop(colitem, None)
|
|
|
|
while finalizers:
|
|
|
|
fin = finalizers.pop()
|
|
|
|
fin()
|
2009-07-08 22:41:30 +08:00
|
|
|
|
2010-07-27 03:15:15 +08:00
|
|
|
def _teardown_with_finalization(self, colitem):
|
|
|
|
self._callfinalizers(colitem)
|
|
|
|
if colitem:
|
2009-05-21 20:37:30 +08:00
|
|
|
colitem.teardown()
|
2009-05-19 01:06:16 +08:00
|
|
|
for colitem in self._finalizers:
|
2009-05-21 20:37:30 +08:00
|
|
|
assert colitem is None or colitem in self.stack
|
2009-04-05 03:06:20 +08:00
|
|
|
|
2010-07-27 03:15:15 +08:00
|
|
|
def teardown_all(self):
|
|
|
|
while self.stack:
|
2009-05-21 20:37:30 +08:00
|
|
|
self._pop_and_teardown()
|
|
|
|
self._teardown_with_finalization(None)
|
2009-05-19 01:06:16 +08:00
|
|
|
assert not self._finalizers
|
2009-04-05 03:06:20 +08:00
|
|
|
|
|
|
|
def teardown_exact(self, item):
|
2011-11-19 00:58:21 +08:00
|
|
|
colitem = item.nextitem
|
2011-11-19 00:01:29 +08:00
|
|
|
needed_collectors = colitem and colitem.listchain() or []
|
|
|
|
self._teardown_towards(needed_collectors)
|
|
|
|
|
|
|
|
def _teardown_towards(self, needed_collectors):
|
|
|
|
while self.stack:
|
|
|
|
if self.stack == needed_collectors[:len(self.stack)]:
|
|
|
|
break
|
2009-07-08 22:41:30 +08:00
|
|
|
self._pop_and_teardown()
|
2010-07-27 03:15:15 +08:00
|
|
|
|
|
|
|
def prepare(self, colitem):
|
2009-04-05 03:06:20 +08:00
|
|
|
""" setup objects along the collector chain to the test-method
|
2009-07-08 22:41:30 +08:00
|
|
|
and teardown previously setup objects."""
|
2010-07-27 03:15:15 +08:00
|
|
|
needed_collectors = colitem.listchain()
|
2011-11-19 00:01:29 +08:00
|
|
|
self._teardown_towards(needed_collectors)
|
|
|
|
|
2010-07-27 03:15:15 +08:00
|
|
|
# check if the last collection node has raised an error
|
2010-01-28 22:36:27 +08:00
|
|
|
for col in self.stack:
|
|
|
|
if hasattr(col, '_prepare_exc'):
|
2010-07-27 03:15:15 +08:00
|
|
|
py.builtin._reraise(*col._prepare_exc)
|
|
|
|
for col in needed_collectors[len(self.stack):]:
|
|
|
|
self.stack.append(col)
|
2010-01-27 19:09:30 +08:00
|
|
|
try:
|
2010-07-27 03:15:15 +08:00
|
|
|
col.setup()
|
2010-01-28 21:20:58 +08:00
|
|
|
except Exception:
|
|
|
|
col._prepare_exc = sys.exc_info()
|
|
|
|
raise
|
2010-04-28 03:13:09 +08:00
|
|
|
|
|
|
|
# =============================================================
|
2010-07-27 03:15:15 +08:00
|
|
|
# Test OutcomeExceptions and helpers for creating them.
|
2010-04-28 03:13:09 +08:00
|
|
|
|
|
|
|
|
2010-07-27 03:15:15 +08:00
|
|
|
class OutcomeException(Exception):
|
|
|
|
""" OutcomeException and its subclass instances indicate and
|
|
|
|
contain info about test and collection outcomes.
|
|
|
|
"""
|
2010-11-23 22:42:23 +08:00
|
|
|
def __init__(self, msg=None, pytrace=True):
|
2010-07-27 03:15:15 +08:00
|
|
|
self.msg = msg
|
2010-11-23 22:42:23 +08:00
|
|
|
self.pytrace = pytrace
|
2010-04-28 03:13:09 +08:00
|
|
|
|
|
|
|
def __repr__(self):
|
2010-07-27 03:15:15 +08:00
|
|
|
if self.msg:
|
2010-11-06 06:37:31 +08:00
|
|
|
return str(self.msg)
|
2010-04-28 03:13:09 +08:00
|
|
|
return "<%s instance>" %(self.__class__.__name__,)
|
|
|
|
__str__ = __repr__
|
|
|
|
|
2010-07-27 03:15:15 +08:00
|
|
|
class Skipped(OutcomeException):
|
|
|
|
# XXX hackish: on 3k we fake to live in the builtins
|
2010-04-28 03:13:09 +08:00
|
|
|
# in order to have Skipped exception printing shorter/nicer
|
|
|
|
__module__ = 'builtins'
|
|
|
|
|
2010-07-27 03:15:15 +08:00
|
|
|
class Failed(OutcomeException):
|
2010-04-28 03:13:09 +08:00
|
|
|
""" raised from an explicit call to py.test.fail() """
|
2010-04-28 19:22:05 +08:00
|
|
|
__module__ = 'builtins'
|
2010-04-28 03:13:09 +08:00
|
|
|
|
|
|
|
class Exit(KeyboardInterrupt):
|
2010-11-06 06:37:31 +08:00
|
|
|
""" raised for immediate program exits (no tracebacks/summaries)"""
|
2010-04-28 03:13:09 +08:00
|
|
|
def __init__(self, msg="unknown reason"):
|
2010-07-27 03:15:15 +08:00
|
|
|
self.msg = msg
|
2010-04-28 03:13:09 +08:00
|
|
|
KeyboardInterrupt.__init__(self, msg)
|
|
|
|
|
2010-07-27 03:15:15 +08:00
|
|
|
# exposed helper methods
|
2010-04-28 03:13:09 +08:00
|
|
|
|
2010-07-27 03:15:15 +08:00
|
|
|
def exit(msg):
|
|
|
|
""" exit testing process as if KeyboardInterrupt was triggered. """
|
2010-04-28 03:13:09 +08:00
|
|
|
__tracebackhide__ = True
|
|
|
|
raise Exit(msg)
|
|
|
|
|
2010-04-28 14:42:56 +08:00
|
|
|
exit.Exception = Exit
|
|
|
|
|
2010-04-28 03:13:09 +08:00
|
|
|
def skip(msg=""):
|
|
|
|
""" skip an executing test with the given message. Note: it's usually
|
2010-11-06 06:37:31 +08:00
|
|
|
better to use the py.test.mark.skipif marker to declare a test to be
|
2010-07-27 03:15:15 +08:00
|
|
|
skipped under certain conditions like mismatching platforms or
|
|
|
|
dependencies. See the pytest_skipping plugin for details.
|
2010-04-28 03:13:09 +08:00
|
|
|
"""
|
|
|
|
__tracebackhide__ = True
|
2010-07-27 03:15:15 +08:00
|
|
|
raise Skipped(msg=msg)
|
2010-04-28 14:42:56 +08:00
|
|
|
skip.Exception = Skipped
|
|
|
|
|
2010-11-23 22:42:23 +08:00
|
|
|
def fail(msg="", pytrace=True):
|
2010-11-23 23:10:47 +08:00
|
|
|
""" explicitely fail an currently-executing test with the given Message.
|
|
|
|
if @pytrace is not True the msg represents the full failure information.
|
|
|
|
"""
|
2010-04-28 03:13:09 +08:00
|
|
|
__tracebackhide__ = True
|
2010-11-23 22:42:23 +08:00
|
|
|
raise Failed(msg=msg, pytrace=pytrace)
|
2010-04-28 14:42:56 +08:00
|
|
|
fail.Exception = Failed
|
|
|
|
|
|
|
|
|
2010-04-28 03:13:09 +08:00
|
|
|
def importorskip(modname, minversion=None):
|
2010-07-27 03:15:15 +08:00
|
|
|
""" return imported module if it has a higher __version__ than the
|
|
|
|
optionally specified 'minversion' - otherwise call py.test.skip()
|
|
|
|
with a message detailing the mismatch.
|
2010-04-28 03:13:09 +08:00
|
|
|
"""
|
2010-11-14 04:03:28 +08:00
|
|
|
__tracebackhide__ = True
|
2010-04-28 03:13:09 +08:00
|
|
|
compile(modname, '', 'eval') # to catch syntaxerrors
|
|
|
|
try:
|
|
|
|
mod = __import__(modname, None, None, ['__doc__'])
|
|
|
|
except ImportError:
|
|
|
|
py.test.skip("could not import %r" %(modname,))
|
|
|
|
if minversion is None:
|
|
|
|
return mod
|
|
|
|
verattr = getattr(mod, '__version__', None)
|
|
|
|
if isinstance(minversion, str):
|
|
|
|
minver = minversion.split(".")
|
|
|
|
else:
|
|
|
|
minver = list(minversion)
|
|
|
|
if verattr is None or verattr.split(".") < minver:
|
|
|
|
py.test.skip("module %r has __version__ %r, required is: %r" %(
|
|
|
|
modname, verattr, minversion))
|
|
|
|
return mod
|