168 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			168 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Python
		
	
	
	
| import py
 | |
| import sys
 | |
| from py.__.test.outcome import Outcome, Failed, Passed, Skipped
 | |
| from py.__.test.reporter import choose_reporter, TestReporter
 | |
| 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)
 | |
|                 if session and session.config.option.keyword_oneshot:
 | |
|                     keyword = None
 | |
|                 yield next
 | |
|             except Skipped:
 | |
|                 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))
 | |
|                     if session.config.option.usepdb: 
 | |
|                         py.__.test.custompdb.post_mortem(excinfo._excinfo[2])
 | |
|         if reporter: 
 | |
|             reporter(repevent.ItemFinish(next))
 | |
| 
 | |
| class AbstractSession(object): 
 | |
|     """ An abstract session executes collectors/items through a runner. 
 | |
|     """
 | |
|     def __init__(self, config):
 | |
|         self.config = config
 | |
|         self._keyword = config.option.keyword
 | |
| 
 | |
|     def fixoptions(self):
 | |
|         """ check, fix and determine conflicting options. """
 | |
|         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
 | |
|         # 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."
 | |
| 
 | |
|         if option.keyword_oneshot and not option.keyword:
 | |
|             raise ValueError, "--keyword-oneshot makes sense only when --keyword is supplied"
 | |
| 
 | |
|     def init_reporter(self, reporter, config, hosts):
 | |
|         if reporter is None:
 | |
|             reporter = choose_reporter(self.reporterclass, config)\
 | |
|                        (config, hosts)
 | |
|         else:
 | |
|             reporter = TestReporter(reporter)
 | |
|         checkfun = lambda : self.config.option.exitfirst and \
 | |
|                             reporter.was_failure()
 | |
|         return reporter, checkfun
 | |
| 
 | |
| class Session(AbstractSession):
 | |
|     """
 | |
|         A Session gets test Items from Collectors, executes the
 | |
|         Items and sends the Outcome to the Reporter.
 | |
|     """
 | |
|     reporterclass = LocalReporter
 | |
|     
 | |
|     def shouldclose(self): 
 | |
|         return False
 | |
| 
 | |
|     def header(self, colitems):
 | |
|         """ setup any neccessary resources ahead of the test run. """
 | |
|         self.reporter(repevent.TestStarted(None, self.config,
 | |
|                                             None))
 | |
|         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)
 | |
|         self.reporter(repevent.TestFinished())
 | |
|     
 | |
|     def main(self, reporter=None):
 | |
|         """ main loop for running tests. """
 | |
|         config = self.config
 | |
|         self.reporter, shouldstop = self.init_reporter(reporter, config, None)
 | |
| 
 | |
|         colitems = self.config.getcolitems()
 | |
|         self.header(colitems)
 | |
|         keyword = self.config.option.keyword
 | |
|         reporter = self.reporter
 | |
|         itemgenerator = itemgen(self, colitems, reporter, keyword)
 | |
|         failures = []
 | |
|         try:
 | |
|             while 1:
 | |
|                 try:
 | |
|                     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 
 | |
|         return self.getitemoutcomepairs(Failed)
 | |
| 
 | |
|     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())
 | |
| 
 | |
| 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)
 | |
| 
 |