273 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			273 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Python
		
	
	
	
| import py
 | |
| from py.__.test.outcome import Skipped
 | |
| 
 | |
| class RunnerPlugin:
 | |
|     def pytest_configure(self, config):
 | |
|         config._setupstate = SetupState()
 | |
| 
 | |
|     def pytest_unconfigure(self, config):
 | |
|         config._setupstate.teardown_all()
 | |
| 
 | |
|     def pytest_item_setup_and_runtest(self, item):
 | |
|         setupstate = item.config._setupstate
 | |
|         call = item.config.guardedcall(lambda: setupstate.prepare(item))
 | |
|         if call.excinfo:
 | |
|             rep = ItemSetupReport(item, call.excinfo, call.outerr)
 | |
|             item.config.api.pytest_itemsetupreport(rep=rep)
 | |
|         else:
 | |
|             call = item.config.guardedcall(lambda: item.runtest())
 | |
|             item.config.api.pytest_item_runtest_finished(
 | |
|                 item=item, excinfo=call.excinfo, outerr=call.outerr)
 | |
|             call = item.config.guardedcall(lambda: self.teardown_exact(item))
 | |
|             if call.excinfo:
 | |
|                 rep = ItemSetupReport(item, call.excinfo, call.outerr)
 | |
|                 item.config.api.pytest_itemsetupreport(rep=rep)
 | |
| 
 | |
|     def pytest_collector_collect(self, collector):
 | |
|         call = item.config.guardedcall(lambda x: collector._memocollect())
 | |
|         return CollectReport(collector, res, excinfo, outerr)
 | |
| 
 | |
| class BaseReport(object):
 | |
|     def __repr__(self):
 | |
|         l = ["%s=%s" %(key, value)
 | |
|            for key, value in self.__dict__.items()]
 | |
|         return "<%s %s>" %(self.__class__.__name__, " ".join(l),)
 | |
| 
 | |
|     def toterminal(self, out):
 | |
|         longrepr = self.longrepr 
 | |
|         if hasattr(longrepr, 'toterminal'):
 | |
|             longrepr.toterminal(out)
 | |
|         else:
 | |
|             out.line(str(longrepr))
 | |
|    
 | |
| class ItemTestReport(BaseReport):
 | |
|     failed = passed = skipped = False
 | |
| 
 | |
|     def __init__(self, colitem, excinfo=None, when=None, outerr=None):
 | |
|         self.colitem = colitem 
 | |
|         if colitem and when != "setup":
 | |
|             self.keywords = colitem.readkeywords() 
 | |
|         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:
 | |
|             self.when = when 
 | |
|             if not isinstance(excinfo, py.code.ExceptionInfo):
 | |
|                 self.failed = True
 | |
|                 shortrepr = "?"
 | |
|                 longrepr = excinfo 
 | |
|             elif excinfo.errisinstance(Skipped):
 | |
|                 self.skipped = True 
 | |
|                 shortrepr = "s"
 | |
|                 longrepr = self.colitem._repr_failure_py(excinfo, outerr)
 | |
|             else:
 | |
|                 self.failed = True
 | |
|                 shortrepr = self.colitem.shortfailurerepr
 | |
|                 if self.when == "execute":
 | |
|                     longrepr = self.colitem.repr_failure(excinfo, outerr)
 | |
|                 else: # exception in setup or teardown 
 | |
|                     longrepr = self.colitem._repr_failure_py(excinfo, outerr)
 | |
|                     shortrepr = shortrepr.lower()
 | |
|             self.shortrepr = shortrepr 
 | |
|             self.longrepr = longrepr 
 | |
|             
 | |
| 
 | |
| class CollectReport(BaseReport):
 | |
|     skipped = failed = passed = False 
 | |
| 
 | |
|     def __init__(self, colitem, result, excinfo=None, outerr=None):
 | |
|         # XXX rename to collector 
 | |
|         self.colitem = colitem 
 | |
|         if not excinfo:
 | |
|             self.passed = True
 | |
|             self.result = result 
 | |
|         else:
 | |
|             self.outerr = outerr
 | |
|             self.longrepr = self.colitem._repr_failure_py(excinfo, outerr)
 | |
|             if excinfo.errisinstance(Skipped):
 | |
|                 self.skipped = True
 | |
|                 self.reason = str(excinfo.value)
 | |
|             else:
 | |
|                 self.failed = True
 | |
| 
 | |
| class ItemSetupReport(BaseReport):
 | |
|     failed = passed = skipped = False
 | |
|     def __init__(self, item, excinfo=None, outerr=None):
 | |
|         self.item = item 
 | |
|         self.outerr = outerr 
 | |
|         if not excinfo:
 | |
|             self.passed = True
 | |
|         else:
 | |
|             if excinfo.errisinstance(Skipped):
 | |
|                 self.skipped = True 
 | |
|             else:
 | |
|                 self.failed = True
 | |
|             self.excrepr = item._repr_failure_py(excinfo, [])
 | |
| 
 | |
| class SetupState(object):
 | |
|     """ shared state for setting up/tearing down test items or collectors. """
 | |
|     def __init__(self):
 | |
|         self.stack = []
 | |
| 
 | |
|     def teardown_all(self): 
 | |
|         while self.stack: 
 | |
|             col = self.stack.pop() 
 | |
|             col.teardown() 
 | |
| 
 | |
|     def teardown_exact(self, item):
 | |
|         if self.stack and self.stack[-1] == item:
 | |
|             col = self.stack.pop()
 | |
|             col.teardown()
 | |
|      
 | |
|     def prepare(self, colitem): 
 | |
|         """ setup objects along the collector chain to the test-method
 | |
|             Teardown any unneccessary previously setup objects."""
 | |
|         needed_collectors = colitem.listchain() 
 | |
|         while self.stack: 
 | |
|             if self.stack == needed_collectors[:len(self.stack)]: 
 | |
|                 break 
 | |
|             col = self.stack.pop() 
 | |
|             col.teardown()
 | |
|         for col in needed_collectors[len(self.stack):]: 
 | |
|             col.setup() 
 | |
|             self.stack.append(col) 
 | |
| 
 | |
| # ===============================================================================
 | |
| #
 | |
| # plugin tests 
 | |
| #
 | |
| # ===============================================================================
 | |
| 
 | |
| def test_generic(plugintester):
 | |
|     plugintester.apicheck(RunnerPlugin())
 | |
| 
 | |
| class TestSetupState:
 | |
|     def test_setup_prepare(self, testdir):
 | |
|         modcol = testdir.getmodulecol("""
 | |
|             def setup_function(function):
 | |
|                 function.myfunc = function
 | |
|             def test_func1():
 | |
|                 pass
 | |
|             def test_func2():
 | |
|                 pass
 | |
|             def teardown_function(function):
 | |
|                 del function.myfunc 
 | |
|         """)
 | |
|         item1, item2 = modcol.collect()
 | |
|         setup = SetupState()
 | |
|         setup.prepare(item1)
 | |
|         assert item1.obj.myfunc == item1.obj 
 | |
|         assert not hasattr(item2, 'myfunc')
 | |
| 
 | |
|         setup.prepare(item2)
 | |
|         assert item2.obj.myfunc == item2.obj 
 | |
|         assert not hasattr(item1, 'myfunc')
 | |
| 
 | |
|     def test_setup_teardown_exact(self, testdir):
 | |
|         item = testdir.getitem("""
 | |
|             def test_func():
 | |
|                 pass
 | |
|             def teardown_function(function):
 | |
|                 function.tear = "down"
 | |
|         """)
 | |
|         setup = SetupState()
 | |
|         setup.prepare(item)
 | |
|         setup.teardown_exact(item)
 | |
|         assert item.obj.tear == "down"
 | |
| 
 | |
|     def test_setup_teardown_exact(self, testdir):
 | |
|         item = testdir.getitem("""
 | |
|             def setup_module(mod):
 | |
|                 mod.x = 1
 | |
|             def setup_function(function):
 | |
|                 function.y = 2
 | |
|             def test_func():
 | |
|                 pass
 | |
|             def teardown_function(function):
 | |
|                 del function.y
 | |
|             def teardown_module(mod):
 | |
|                 del mod.x
 | |
|         """)
 | |
|         setup = SetupState()
 | |
|         setup.prepare(item)
 | |
|         assert item.obj.y == 2
 | |
|         assert item.parent.obj.x == 1
 | |
|         setup.teardown_all()
 | |
|         assert not hasattr(item.obj, 'y')
 | |
|         assert not hasattr(item.parent.obj, 'x')
 | |
| 
 | |
| class TestRunnerPlugin:
 | |
|     def test_pytest_item_setup_and_runtest(self, testdir):
 | |
|         item = testdir.getitem("""def test_func(): pass""")
 | |
|         plugin = RunnerPlugin()
 | |
|         plugin.pytest_configure(item.config)
 | |
|         sorter = testdir.geteventrecorder(item.config.pluginmanager)
 | |
|         plugin.pytest_item_setup_and_runtest(item)
 | |
|         rep = sorter.getcall("pytest_itemtestreport").rep
 | |
|         assert rep.passed 
 | |
|         
 | |
| class TestSetupEvents:
 | |
|     disabled = True
 | |
| 
 | |
|     def test_setup_runtest_ok(self, testdir):
 | |
|         sorter = testdir.inline_runsource("""
 | |
|             def setup_module(mod):
 | |
|                 pass 
 | |
|             def test_func():
 | |
|                 pass
 | |
|         """)
 | |
|         assert not sorter.getcalls(pytest_itemsetupreport)
 | |
| 
 | |
|     def test_setup_fails(self, testdir):
 | |
|         sorter = testdir.inline_runsource("""
 | |
|             def setup_module(mod):
 | |
|                 print "world"
 | |
|                 raise ValueError(42)
 | |
|             def test_func():
 | |
|                 pass
 | |
|         """)
 | |
|         rep = sorter.popcall(pytest_itemsetupreport).rep
 | |
|         assert rep.failed
 | |
|         assert not rep.skipped
 | |
|         assert rep.excrepr 
 | |
|         assert "42" in str(rep.excrepr)
 | |
|         assert rep.outerr[0].find("world") != -1
 | |
| 
 | |
|     def test_teardown_fails(self, testdir):
 | |
|         sorter = testdir.inline_runsource("""
 | |
|             def test_func():
 | |
|                 pass
 | |
|             def teardown_function(func): 
 | |
|                 print "13"
 | |
|                 raise ValueError(25)
 | |
|         """)
 | |
|         rep = evrec.popcall(pytest_itemsetupreport).rep 
 | |
|         assert rep.failed 
 | |
|         assert rep.item == item 
 | |
|         assert not rep.passed
 | |
|         assert "13" in rep.outerr[0]
 | |
|         assert "25" in str(rep.excrepr)
 | |
| 
 | |
|     def test_setup_skips(self, testdir):
 | |
|         sorter = testdir.inline_runsource("""
 | |
|             import py
 | |
|             def setup_module(mod):
 | |
|                 py.test.skip("17")
 | |
|             def test_func():
 | |
|                 pass
 | |
|         """)
 | |
|         rep = sorter.popcall(pytest_itemsetupreport)
 | |
|         assert not rep.failed
 | |
|         assert rep.skipped
 | |
|         assert rep.excrepr 
 | |
|         assert "17" in str(rep.excrepr)
 | |
|     
 | |
| 
 |