[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:
hpk 2009-04-04 21:06:20 +02:00
parent ec2c167264
commit ba6eca8da4
5 changed files with 162 additions and 45 deletions

View File

@ -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(

View File

@ -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). """

View File

@ -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:

View File

@ -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)

View File

@ -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)