fix issue226 - LIFO ordering for fixture-added teardowns

This commit is contained in:
holger krekel 2012-11-16 10:03:51 +01:00
parent e15da7cbef
commit b940ed11a0
5 changed files with 38 additions and 7 deletions

View File

@ -5,6 +5,7 @@ Changes between 2.3.3 and 2.3.4.dev
with autouse fixtures. If you need generative tests, use with autouse fixtures. If you need generative tests, use
@pytest.mark.parametrize or pytest_generate_tests, see the @pytest.mark.parametrize or pytest_generate_tests, see the
many examples at http://pytest.org/latest/example/parametrize.html many examples at http://pytest.org/latest/example/parametrize.html
- fix issue226 - LIFO ordering for fixture teardowns
- fix issue224 - invocations with >256 char arguments now work - fix issue224 - invocations with >256 char arguments now work
- fix issue91 - add/discuss package/directory level setups in example - fix issue91 - add/discuss package/directory level setups in example
- allow to dynamically define markers via - allow to dynamically define markers via

View File

@ -1,2 +1,2 @@
# #
__version__ = '2.3.4.dev4' __version__ = '2.3.4.dev5'

View File

@ -1224,6 +1224,7 @@ class FixtureRequest(FuncargnamesCompatAttr):
if paramscopenum != scopenum_subfunction: if paramscopenum != scopenum_subfunction:
scope = scopes[paramscopenum] scope = scopes[paramscopenum]
# check if a higher-level scoped fixture accesses a lower level one
if scope is not None: if scope is not None:
__tracebackhide__ = True __tracebackhide__ = True
if scopemismatch(self.scope, scope): if scopemismatch(self.scope, scope):
@ -1236,15 +1237,18 @@ class FixtureRequest(FuncargnamesCompatAttr):
__tracebackhide__ = False __tracebackhide__ = False
mp.setattr(self, "scope", scope) mp.setattr(self, "scope", scope)
# route request.addfinalizer to fixturedef
mp.setattr(self, "addfinalizer", fixturedef.addfinalizer)
# perform the fixture call
val = fixturedef.execute(request=self)
# prepare finalization according to scope # prepare finalization according to scope
# (XXX analyse exact finalizing mechanics / cleanup) # (XXX analyse exact finalizing mechanics / cleanup)
self.session._setupstate.addfinalizer(fixturedef.finish, self.node) self.session._setupstate.addfinalizer(fixturedef.finish, self.node)
self._fixturemanager.addargfinalizer(fixturedef.finish, argname) self._fixturemanager.addargfinalizer(fixturedef.finish, argname)
for subargname in fixturedef.argnames: # XXX all deps? for subargname in fixturedef.argnames: # XXX all deps?
self._fixturemanager.addargfinalizer(fixturedef.finish, subargname) self._fixturemanager.addargfinalizer(fixturedef.finish, subargname)
mp.setattr(self, "addfinalizer", fixturedef.addfinalizer)
# finally perform the fixture call
val = fixturedef.execute(request=self)
mp.undo() mp.undo()
return val return val
@ -1503,6 +1507,8 @@ class FixtureManager:
items[:] = parametrize_sorted(items, set(), {}, 0) items[:] = parametrize_sorted(items, set(), {}, 0)
def pytest_runtest_teardown(self, item, nextitem): def pytest_runtest_teardown(self, item, nextitem):
# XXX teardown needs to be normalized for parametrized and
# no-parametrized functions
try: try:
cs1 = item.callspec cs1 = item.callspec
except AttributeError: except AttributeError:
@ -1524,7 +1530,7 @@ class FixtureManager:
keylist.sort() keylist.sort()
for (scopenum, name, param) in keylist: for (scopenum, name, param) in keylist:
item.session._setupstate._callfinalizers((name, param)) item.session._setupstate._callfinalizers((name, param))
l = self._arg2finish.get(name) l = self._arg2finish.pop(name, None)
if l is not None: if l is not None:
for fin in reversed(l): for fin in reversed(l):
fin() fin()

View File

@ -48,7 +48,7 @@ def main():
name='pytest', name='pytest',
description='py.test: simple powerful testing with Python', description='py.test: simple powerful testing with Python',
long_description = long_description, long_description = long_description,
version='2.3.4.dev4', version='2.3.4.dev5',
url='http://pytest.org', url='http://pytest.org',
license='MIT license', license='MIT license',
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],

View File

@ -1105,7 +1105,7 @@ class TestAutouseManagement:
reprec = testdir.inline_run() reprec = testdir.inline_run()
reprec.assertoutcome(passed=5) reprec.assertoutcome(passed=5)
def test_setup_funcarg_order(self, testdir): def test_ordering_autouse_before_explicit(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
@ -1122,6 +1122,30 @@ class TestAutouseManagement:
reprec = testdir.inline_run() reprec = testdir.inline_run()
reprec.assertoutcome(passed=1) reprec.assertoutcome(passed=1)
@pytest.mark.issue226
@pytest.mark.parametrize("param1", ["", "params=[1]"])
@pytest.mark.parametrize("param2", ["", "params=[1]"])
def test_ordering_dependencies_torndown_first(self, testdir, param1, param2):
testdir.makepyfile("""
import pytest
l = []
@pytest.fixture(%(param1)s)
def arg1(request):
request.addfinalizer(lambda: l.append("fin1"))
l.append("new1")
@pytest.fixture(%(param2)s)
def arg2(request, arg1):
request.addfinalizer(lambda: l.append("fin2"))
l.append("new2")
def test_arg(arg2):
pass
def test_check():
assert l == ["new1", "new2", "fin2", "fin1"]
""" % locals())
reprec = testdir.inline_run("-s")
reprec.assertoutcome(passed=2)
class TestFixtureMarker: class TestFixtureMarker:
def test_parametrize(self, testdir): def test_parametrize(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""