[svn r63628] * shuffle SetupState and fixture handling into runner.py
* introduce a itemsetupreport and new setupitem/teardownitem methods. * more tests --HG-- branch : trunk
This commit is contained in:
parent
ec2c167264
commit
ba6eca8da4
|
@ -3,6 +3,7 @@ from conftesthandle import Conftest
|
|||
|
||||
from py.__.test import parseopt
|
||||
from py.__.misc.warn import APIWARN
|
||||
from py.__.test.runner import SetupState
|
||||
|
||||
def ensuretemp(string, dir=1):
|
||||
""" return temporary directory path with
|
||||
|
@ -312,34 +313,6 @@ def gettopdir(args):
|
|||
else:
|
||||
return pkgdir.dirpath()
|
||||
|
||||
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)
|
||||
|
||||
# this is the one per-process instance of py.test configuration
|
||||
config_per_process = Config(
|
||||
|
|
|
@ -85,6 +85,9 @@ class Events:
|
|||
def pyevent__itemtestreport(self, event):
|
||||
""" test has been run. """
|
||||
|
||||
def pyevent__itemsetupreport(self, rep):
|
||||
""" test has been run. """
|
||||
|
||||
def pyevent__deselected(self, items):
|
||||
""" collected items that were deselected (by keyword). """
|
||||
|
||||
|
|
|
@ -74,6 +74,11 @@ class TmpTestdir:
|
|||
if hasattr(self, '_olddir'):
|
||||
self._olddir.chdir()
|
||||
|
||||
def geteventrecorder(self, config):
|
||||
evrec = EventRecorder(config.bus)
|
||||
self.pyfuncitem.addfinalizer(lambda: config.bus.unregister(evrec))
|
||||
return evrec
|
||||
|
||||
def chdir(self):
|
||||
old = self.tmpdir.chdir()
|
||||
if not hasattr(self, '_olddir'):
|
||||
|
@ -174,9 +179,12 @@ class TmpTestdir:
|
|||
|
||||
def getitem(self, source, funcname="test_func"):
|
||||
modcol = self.getmodulecol(source)
|
||||
item = modcol.join(funcname)
|
||||
assert item is not None, "%r item not found in module:\n%s" %(funcname, source)
|
||||
return item
|
||||
moditems = modcol.collect()
|
||||
for item in modcol.collect():
|
||||
if item.name == funcname:
|
||||
return item
|
||||
else:
|
||||
assert 0, "%r item not found in module:\n%s" %(funcname, source)
|
||||
|
||||
def getitems(self, source):
|
||||
modcol = self.getmodulecol(source)
|
||||
|
@ -284,12 +292,13 @@ class EventRecorder(object):
|
|||
for i, event in py.builtin.enumerate(self.events):
|
||||
if event.name == name:
|
||||
del self.events[i]
|
||||
return event
|
||||
eventparser = self.geteventparser(name)
|
||||
return eventparser(*event.args, **event.kwargs)
|
||||
raise KeyError("popevent: %r not found in %r" %(name, self.events))
|
||||
|
||||
def getevents(self, eventname):
|
||||
""" return list of ParsedEvent instances matching the given eventname. """
|
||||
method = self.geteventmethod(eventname)
|
||||
method = self.geteventparser(eventname)
|
||||
l = []
|
||||
for event in self.events:
|
||||
if event.name == eventname:
|
||||
|
@ -297,23 +306,18 @@ class EventRecorder(object):
|
|||
l.append(pevent)
|
||||
return l
|
||||
|
||||
def geteventmethod(self, eventname):
|
||||
def geteventparser(self, eventname):
|
||||
mname = "pyevent__" + eventname
|
||||
method = getattr(api.Events, mname)
|
||||
args, varargs, varkw, default = inspect.getargspec(method)
|
||||
assert args[0] == "self"
|
||||
args = args[1:]
|
||||
fspec = inspect.formatargspec(args, varargs, varkw, default)
|
||||
source = """def %(mname)s%(fspec)s:
|
||||
return ParsedEvent(locals())""" % locals()
|
||||
print source
|
||||
exec source
|
||||
code = py.code.compile("""def %(mname)s%(fspec)s:
|
||||
return ParsedEvent(locals())""" % locals())
|
||||
exec code
|
||||
return locals()[mname]
|
||||
|
||||
|
||||
def firstparsedevent(self, eventname):
|
||||
return self.parsedevents(eventname)[0]
|
||||
|
||||
def get(self, cls):
|
||||
l = []
|
||||
for event in self.events:
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* and generating report events about it
|
||||
"""
|
||||
|
||||
import py, os, sys
|
||||
import py
|
||||
|
||||
from py.__.test.outcome import Exit, Skipped
|
||||
|
||||
|
@ -55,7 +55,6 @@ def basic_collect_report(collector):
|
|||
return CollectionReport(collector, res, excinfo, outerr)
|
||||
|
||||
from cPickle import Pickler, Unpickler
|
||||
from cStringIO import StringIO
|
||||
|
||||
def forked_run_report(item, pdb=None):
|
||||
EXITSTATUS_TESTEXIT = 4
|
||||
|
@ -66,7 +65,7 @@ def forked_run_report(item, pdb=None):
|
|||
try:
|
||||
testrep = basic_run_report(item)
|
||||
except (KeyboardInterrupt, Exit):
|
||||
os._exit(EXITSTATUS_TESTEXIT)
|
||||
py.std.os._exit(EXITSTATUS_TESTEXIT)
|
||||
return ipickle.dumps(testrep)
|
||||
|
||||
ff = py.process.ForkedFunc(runforked)
|
||||
|
@ -164,3 +163,70 @@ class CollectionReport(BaseReport):
|
|||
else:
|
||||
out.line(str(longrepr))
|
||||
|
||||
class ItemSetupReport(BaseReport):
|
||||
""" Test Execution Report. """
|
||||
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, outerr)
|
||||
|
||||
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)
|
||||
|
||||
def fixturecall(self, callable, item):
|
||||
excinfo = None
|
||||
capture = item.config._getcapture()
|
||||
try:
|
||||
try:
|
||||
callable(item)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
excinfo = py.code.ExceptionInfo()
|
||||
finally:
|
||||
outerr = capture.reset()
|
||||
if not excinfo:
|
||||
return True
|
||||
else:
|
||||
rep = ItemSetupReport(item, excinfo, outerr)
|
||||
item.config.pytestplugins.notify("itemsetupreport", rep)
|
||||
|
||||
def setupitem(self, item):
|
||||
return self.fixturecall(self.prepare, item)
|
||||
|
||||
def teardownitem(self, item):
|
||||
self.fixturecall(self.teardown_exact, item)
|
||||
|
|
|
@ -138,3 +138,74 @@ def test_method_setup_uses_fresh_instances(testdir):
|
|||
assert not hasattr(self, 'world')
|
||||
""")
|
||||
sorter.assertoutcome(passed=4, failed=0)
|
||||
|
||||
from py.__.test.config import SetupState
|
||||
|
||||
class TestSetupState:
|
||||
def test_setupitem_works(self, testdir):
|
||||
item = testdir.getitem("""
|
||||
def setup_module(mod):
|
||||
pass
|
||||
def test_func():
|
||||
pass
|
||||
""")
|
||||
evrec = testdir.geteventrecorder(item.config)
|
||||
setup = SetupState()
|
||||
res = setup.setupitem(item)
|
||||
assert res
|
||||
|
||||
def test_setupitem_fails(self, testdir):
|
||||
item = testdir.getitem("""
|
||||
def setup_module(mod):
|
||||
print "world"
|
||||
raise ValueError(42)
|
||||
def test_func():
|
||||
pass
|
||||
""")
|
||||
evrec = testdir.geteventrecorder(item.config)
|
||||
setup = SetupState()
|
||||
res = setup.setupitem(item)
|
||||
assert not res
|
||||
rep = evrec.popevent("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_teardownitem_fails(self, testdir):
|
||||
item = testdir.getitem("""
|
||||
def test_func():
|
||||
pass
|
||||
def teardown_function(func):
|
||||
print "13"
|
||||
raise ValueError(25)
|
||||
""")
|
||||
evrec = testdir.geteventrecorder(item.config)
|
||||
setup = SetupState()
|
||||
res = setup.setupitem(item)
|
||||
assert res
|
||||
setup.teardownitem(item)
|
||||
rep = evrec.popevent("itemsetupreport").rep
|
||||
assert rep.item == item
|
||||
assert rep.failed
|
||||
assert not rep.passed
|
||||
assert "13" in rep.outerr[0]
|
||||
assert "25" in str(rep.excrepr)
|
||||
|
||||
def test_setupitem_skips(self, testdir):
|
||||
item = testdir.getitem("""
|
||||
import py
|
||||
def setup_module(mod):
|
||||
py.test.skip("17")
|
||||
def test_func():
|
||||
pass
|
||||
""")
|
||||
evrec = testdir.geteventrecorder(item.config)
|
||||
setup = SetupState()
|
||||
setup.setupitem(item)
|
||||
rep = evrec.popevent("itemsetupreport").rep
|
||||
assert not rep.failed
|
||||
assert rep.skipped
|
||||
assert rep.excrepr
|
||||
assert "17" in str(rep.excrepr)
|
Loading…
Reference in New Issue