2007-01-24 22:24:01 +08:00
|
|
|
import py
|
2008-01-25 23:54:04 +08:00
|
|
|
import sys
|
2007-02-03 20:14:46 +08:00
|
|
|
from py.__.test.outcome import Outcome, Failed, Passed, Skipped
|
2007-08-27 17:02:50 +08:00
|
|
|
from py.__.test.reporter import choose_reporter, TestReporter
|
2008-01-25 23:54:04 +08:00
|
|
|
from py.__.test import repevent
|
|
|
|
from py.__.test.outcome import SerializableOutcome, ReprOutcome
|
|
|
|
from py.__.test.reporter import LocalReporter
|
|
|
|
from py.__.test.executor import RunExecutor, BoxExecutor
|
|
|
|
|
|
|
|
""" The session implementation - reporter version:
|
|
|
|
|
|
|
|
* itemgen is responsible for iterating and telling reporter
|
|
|
|
about skipped and failed iterations (this is for collectors only),
|
|
|
|
this should be probably moved to session (for uniformity)
|
|
|
|
* session gets items which needs to be executed one after another
|
|
|
|
and tells reporter about that
|
|
|
|
"""
|
|
|
|
|
|
|
|
try:
|
|
|
|
GeneratorExit
|
|
|
|
except NameError:
|
|
|
|
GeneratorExit = StopIteration # I think
|
|
|
|
|
|
|
|
def itemgen(session, colitems, reporter, keyword=None):
|
|
|
|
stopitems = py.test.collect.Item # XXX should be generator here as well
|
|
|
|
while 1:
|
|
|
|
if not colitems:
|
|
|
|
break
|
|
|
|
next = colitems.pop(0)
|
|
|
|
if reporter:
|
|
|
|
reporter(repevent.ItemStart(next))
|
|
|
|
|
|
|
|
if isinstance(next, stopitems):
|
|
|
|
try:
|
|
|
|
next._skipbykeyword(keyword)
|
2008-05-10 21:55:22 +08:00
|
|
|
if session and session.config.option.keyword_oneshot:
|
2008-01-25 23:54:04 +08:00
|
|
|
keyword = None
|
2008-05-10 21:39:39 +08:00
|
|
|
yield next
|
|
|
|
except Skipped:
|
2008-01-25 23:54:04 +08:00
|
|
|
excinfo = py.code.ExceptionInfo()
|
|
|
|
reporter(repevent.SkippedTryiter(excinfo, next))
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
cols = [next.join(x) for x in next.run()]
|
|
|
|
for x in itemgen(session, cols, reporter, keyword):
|
|
|
|
yield x
|
|
|
|
except (KeyboardInterrupt, SystemExit, GeneratorExit):
|
|
|
|
raise
|
|
|
|
except:
|
|
|
|
excinfo = py.code.ExceptionInfo()
|
|
|
|
if excinfo.type is Skipped:
|
|
|
|
reporter(repevent.SkippedTryiter(excinfo, next))
|
|
|
|
else:
|
|
|
|
reporter(repevent.FailedTryiter(excinfo, next))
|
2008-05-21 17:54:33 +08:00
|
|
|
if session.config.option.usepdb:
|
|
|
|
py.__.test.custompdb.post_mortem(excinfo._excinfo[2])
|
2008-01-25 23:54:04 +08:00
|
|
|
if reporter:
|
|
|
|
reporter(repevent.ItemFinish(next))
|
2007-01-24 22:24:01 +08:00
|
|
|
|
2007-08-27 17:02:50 +08:00
|
|
|
class AbstractSession(object):
|
|
|
|
""" An abstract session executes collectors/items through a runner.
|
2007-01-24 22:24:01 +08:00
|
|
|
"""
|
2008-01-25 23:54:04 +08:00
|
|
|
def __init__(self, config):
|
2007-04-03 23:23:00 +08:00
|
|
|
self.config = config
|
2007-04-04 01:28:04 +08:00
|
|
|
self._keyword = config.option.keyword
|
2007-01-24 22:24:01 +08:00
|
|
|
|
2007-01-28 21:25:03 +08:00
|
|
|
def fixoptions(self):
|
|
|
|
""" check, fix and determine conflicting options. """
|
2007-08-27 17:02:50 +08:00
|
|
|
option = self.config.option
|
|
|
|
if option.runbrowser and not option.startserver:
|
|
|
|
#print "--runbrowser implies --startserver"
|
|
|
|
option.startserver = True
|
|
|
|
if self.config.getvalue("dist_boxed") and option.dist:
|
|
|
|
option.boxed = True
|
2007-01-28 21:25:03 +08:00
|
|
|
# conflicting options
|
|
|
|
if option.looponfailing and option.usepdb:
|
|
|
|
raise ValueError, "--looponfailing together with --pdb not supported."
|
|
|
|
if option.looponfailing and option.dist:
|
|
|
|
raise ValueError, "--looponfailing together with --dist not supported."
|
|
|
|
if option.executable and option.usepdb:
|
|
|
|
raise ValueError, "--exec together with --pdb not supported."
|
|
|
|
|
2007-04-04 01:28:04 +08:00
|
|
|
if option.keyword_oneshot and not option.keyword:
|
|
|
|
raise ValueError, "--keyword-oneshot makes sense only when --keyword is supplied"
|
2007-04-03 23:23:00 +08:00
|
|
|
|
2007-08-27 17:02:50 +08:00
|
|
|
def init_reporter(self, reporter, config, hosts):
|
|
|
|
if reporter is None:
|
2008-01-25 23:54:04 +08:00
|
|
|
reporter = choose_reporter(self.reporterclass, config)\
|
|
|
|
(config, hosts)
|
2007-08-27 17:02:50 +08:00
|
|
|
else:
|
|
|
|
reporter = TestReporter(reporter)
|
|
|
|
checkfun = lambda : self.config.option.exitfirst and \
|
|
|
|
reporter.was_failure()
|
|
|
|
return reporter, checkfun
|
|
|
|
|
|
|
|
class Session(AbstractSession):
|
|
|
|
"""
|
2007-10-19 21:46:10 +08:00
|
|
|
A Session gets test Items from Collectors, executes the
|
2007-08-27 17:02:50 +08:00
|
|
|
Items and sends the Outcome to the Reporter.
|
|
|
|
"""
|
2008-01-25 23:54:04 +08:00
|
|
|
reporterclass = LocalReporter
|
|
|
|
|
2007-08-27 17:02:50 +08:00
|
|
|
def shouldclose(self):
|
|
|
|
return False
|
|
|
|
|
|
|
|
def header(self, colitems):
|
|
|
|
""" setup any neccessary resources ahead of the test run. """
|
2008-01-25 23:54:04 +08:00
|
|
|
self.reporter(repevent.TestStarted(None, self.config,
|
|
|
|
None))
|
2007-08-27 17:02:50 +08:00
|
|
|
if not self.config.option.nomagic:
|
|
|
|
py.magic.invoke(assertion=1)
|
|
|
|
|
|
|
|
def footer(self, colitems):
|
|
|
|
""" teardown any resources after a test run. """
|
|
|
|
py.test.collect.Function._state.teardown_all()
|
|
|
|
if not self.config.option.nomagic:
|
|
|
|
py.magic.revoke(assertion=1)
|
2008-01-25 23:54:04 +08:00
|
|
|
self.reporter(repevent.TestFinished())
|
|
|
|
|
|
|
|
def main(self, reporter=None):
|
2007-01-24 22:24:01 +08:00
|
|
|
""" main loop for running tests. """
|
2008-01-25 23:54:04 +08:00
|
|
|
config = self.config
|
|
|
|
self.reporter, shouldstop = self.init_reporter(reporter, config, None)
|
|
|
|
|
2007-01-25 00:46:46 +08:00
|
|
|
colitems = self.config.getcolitems()
|
2008-01-25 23:54:04 +08:00
|
|
|
self.header(colitems)
|
|
|
|
keyword = self.config.option.keyword
|
|
|
|
reporter = self.reporter
|
|
|
|
itemgenerator = itemgen(self, colitems, reporter, keyword)
|
|
|
|
failures = []
|
2007-01-24 22:24:01 +08:00
|
|
|
try:
|
2008-01-25 23:54:04 +08:00
|
|
|
while 1:
|
2007-01-25 01:48:13 +08:00
|
|
|
try:
|
2008-01-25 23:54:04 +08:00
|
|
|
item = itemgenerator.next()
|
|
|
|
if shouldstop():
|
|
|
|
return
|
|
|
|
outcome = self.run(item)
|
|
|
|
if outcome is not None:
|
|
|
|
if not outcome.passed and not outcome.skipped:
|
|
|
|
failures.append((item, outcome))
|
|
|
|
reporter(repevent.ReceivedItemOutcome(None, item, outcome))
|
|
|
|
except StopIteration:
|
|
|
|
break
|
|
|
|
finally:
|
|
|
|
self.footer(colitems)
|
|
|
|
return failures
|
2007-12-21 17:55:40 +08:00
|
|
|
return self.getitemoutcomepairs(Failed)
|
2007-01-24 22:24:01 +08:00
|
|
|
|
2008-01-25 23:54:04 +08:00
|
|
|
def run(self, item):
|
|
|
|
if not self.config.option.boxed:
|
|
|
|
executor = RunExecutor(item, self.config.option.usepdb,
|
|
|
|
self.reporter, self.config)
|
|
|
|
return ReprOutcome(executor.execute().make_repr())
|
|
|
|
else:
|
|
|
|
executor = BoxExecutor(item, self.config.option.usepdb,
|
|
|
|
self.reporter, self.config)
|
|
|
|
return ReprOutcome(executor.execute())
|
2007-01-24 22:24:01 +08:00
|
|
|
|
|
|
|
class Exit(Exception):
|
|
|
|
""" for immediate program exits without tracebacks and reporter/summary. """
|
|
|
|
def __init__(self, msg="unknown reason", item=None):
|
|
|
|
self.msg = msg
|
|
|
|
Exception.__init__(self, msg)
|
|
|
|
|
|
|
|
def exit(msg, item=None):
|
|
|
|
raise Exit(msg=msg, item=item)
|
|
|
|
|