introduce and document "session" scope for finalization helpers
--HG-- branch : trunk
This commit is contained in:
parent
b8c3f866b5
commit
5d3a3add83
|
@ -125,13 +125,14 @@ perform scoped setup and teardown
|
||||||
.. sourcecode:: python
|
.. sourcecode:: python
|
||||||
|
|
||||||
def cached_setup(setup, teardown=None, scope="module", keyextra=None):
|
def cached_setup(setup, teardown=None, scope="module", keyextra=None):
|
||||||
""" setup and return value of calling setup(), cache results and
|
""" cache and return result of calling setup().
|
||||||
optionally teardown the value by calling ``teardown(value)``. The scope
|
|
||||||
determines the key for cashing the setup value. Specify ``keyextra``
|
The scope determines the cache key and ``keyextra`` adds to the cachekey.
|
||||||
to add to the cash-key.
|
The scope also determines when teardown(result) will be called.
|
||||||
scope == 'function': when test function run finishes.
|
valid scopes:
|
||||||
|
scope == 'function': when the single test function run finishes.
|
||||||
scope == 'module': when tests in a different module are run
|
scope == 'module': when tests in a different module are run
|
||||||
scope == 'run': when the test run has been finished.
|
scope == 'session': when tests of the session have run.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
example for providing a value that is to be setup only once during a test run:
|
example for providing a value that is to be setup only once during a test run:
|
||||||
|
@ -153,9 +154,9 @@ cleanup after test function execution
|
||||||
|
|
||||||
def addfinalizer(func, scope="function"):
|
def addfinalizer(func, scope="function"):
|
||||||
""" register calling a a finalizer function.
|
""" register calling a a finalizer function.
|
||||||
scope == 'function': when test function run finishes.
|
scope == 'function': when the single test function run finishes.
|
||||||
scope == 'module': when tests in a different module are run
|
scope == 'module': when tests in a different module are run
|
||||||
scope == 'run': when all tests have been run.
|
scope == 'session': when tests of the session have run.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Calling ``request.addfinalizer()`` is useful for scheduling teardown
|
Calling ``request.addfinalizer()`` is useful for scheduling teardown
|
||||||
|
|
|
@ -148,6 +148,8 @@ class FuncargRequest:
|
||||||
return self._pyfuncitem
|
return self._pyfuncitem
|
||||||
elif scope == "module":
|
elif scope == "module":
|
||||||
return self._pyfuncitem.getparent(py.test.collect.Module)
|
return self._pyfuncitem.getparent(py.test.collect.Module)
|
||||||
|
elif scope == "session":
|
||||||
|
return None
|
||||||
raise ValueError("unknown finalization scope %r" %(scope,))
|
raise ValueError("unknown finalization scope %r" %(scope,))
|
||||||
|
|
||||||
def addfinalizer(self, finalizer, scope="function"):
|
def addfinalizer(self, finalizer, scope="function"):
|
||||||
|
|
|
@ -175,29 +175,37 @@ class SetupState(object):
|
||||||
self._finalizers = {}
|
self._finalizers = {}
|
||||||
|
|
||||||
def addfinalizer(self, finalizer, colitem):
|
def addfinalizer(self, finalizer, colitem):
|
||||||
|
""" attach a finalizer to the given colitem.
|
||||||
|
if colitem is None, this will add a finalizer that
|
||||||
|
is called at the end of teardown_all().
|
||||||
|
"""
|
||||||
assert callable(finalizer)
|
assert callable(finalizer)
|
||||||
#assert colitem in self.stack
|
#assert colitem in self.stack
|
||||||
self._finalizers.setdefault(colitem, []).append(finalizer)
|
self._finalizers.setdefault(colitem, []).append(finalizer)
|
||||||
|
|
||||||
def _teardown(self, colitem):
|
def _pop_and_teardown(self):
|
||||||
|
colitem = self.stack.pop()
|
||||||
|
self._teardown_with_finalization(colitem)
|
||||||
|
|
||||||
|
def _teardown_with_finalization(self, colitem):
|
||||||
finalizers = self._finalizers.pop(colitem, None)
|
finalizers = self._finalizers.pop(colitem, None)
|
||||||
while finalizers:
|
while finalizers:
|
||||||
fin = finalizers.pop()
|
fin = finalizers.pop()
|
||||||
fin()
|
fin()
|
||||||
|
if colitem:
|
||||||
colitem.teardown()
|
colitem.teardown()
|
||||||
for colitem in self._finalizers:
|
for colitem in self._finalizers:
|
||||||
assert colitem in self.stack
|
assert colitem is None or colitem in self.stack
|
||||||
|
|
||||||
def teardown_all(self):
|
def teardown_all(self):
|
||||||
while self.stack:
|
while self.stack:
|
||||||
col = self.stack.pop()
|
self._pop_and_teardown()
|
||||||
self._teardown(col)
|
self._teardown_with_finalization(None)
|
||||||
assert not self._finalizers
|
assert not self._finalizers
|
||||||
|
|
||||||
def teardown_exact(self, item):
|
def teardown_exact(self, item):
|
||||||
if self.stack and self.stack[-1] == item:
|
assert self.stack and self.stack[-1] == item
|
||||||
col = self.stack.pop()
|
self._pop_and_teardown()
|
||||||
self._teardown(col)
|
|
||||||
|
|
||||||
def prepare(self, colitem):
|
def prepare(self, colitem):
|
||||||
""" setup objects along the collector chain to the test-method
|
""" setup objects along the collector chain to the test-method
|
||||||
|
@ -206,8 +214,7 @@ class SetupState(object):
|
||||||
while self.stack:
|
while self.stack:
|
||||||
if self.stack == needed_collectors[:len(self.stack)]:
|
if self.stack == needed_collectors[:len(self.stack)]:
|
||||||
break
|
break
|
||||||
col = self.stack.pop()
|
self._pop_and_teardown()
|
||||||
self._teardown(col)
|
|
||||||
for col in needed_collectors[len(self.stack):]:
|
for col in needed_collectors[len(self.stack):]:
|
||||||
col.setup()
|
col.setup()
|
||||||
self.stack.append(col)
|
self.stack.append(col)
|
||||||
|
|
|
@ -147,17 +147,36 @@ class TestRequest:
|
||||||
req._fillfuncargs()
|
req._fillfuncargs()
|
||||||
assert item.funcargs == {'something': 1}
|
assert item.funcargs == {'something': 1}
|
||||||
|
|
||||||
def test_request_addfinalizer(self, testdir):
|
def test_request_addfinalizer_scopes(self, testdir):
|
||||||
item = testdir.getitem("""
|
item = testdir.getitem("""
|
||||||
def pytest_funcarg__something(request): pass
|
teardownlist = []
|
||||||
|
def pytest_funcarg__something(request):
|
||||||
|
for scope in ("function", "module", "session"):
|
||||||
|
request.addfinalizer(
|
||||||
|
lambda x=scope: teardownlist.append(x),
|
||||||
|
scope=scope)
|
||||||
|
|
||||||
def test_func(something): pass
|
def test_func(something): pass
|
||||||
""")
|
""")
|
||||||
req = funcargs.FuncargRequest(item)
|
req = funcargs.FuncargRequest(item)
|
||||||
|
req.config._setupstate.prepare(item) # XXX
|
||||||
|
req._fillfuncargs()
|
||||||
|
# successively check finalization calls
|
||||||
|
teardownlist = item.getparent(py.test.collect.Module).obj.teardownlist
|
||||||
|
ss = item.config._setupstate
|
||||||
|
assert not teardownlist
|
||||||
|
ss.teardown_exact(item)
|
||||||
|
print ss.stack
|
||||||
|
assert teardownlist == ['function']
|
||||||
|
ss.teardown_exact(item.parent)
|
||||||
|
assert teardownlist == ['function', 'module']
|
||||||
|
ss.teardown_all()
|
||||||
|
assert teardownlist == ['function', 'module', 'session']
|
||||||
|
|
||||||
|
def test_request_addfinalizer_unknown_scope(self, testdir):
|
||||||
|
item = testdir.getitem("def test_func(): pass")
|
||||||
|
req = funcargs.FuncargRequest(item)
|
||||||
py.test.raises(ValueError, "req.addfinalizer(None, scope='xyz')")
|
py.test.raises(ValueError, "req.addfinalizer(None, scope='xyz')")
|
||||||
l = [1]
|
|
||||||
req.addfinalizer(l.pop)
|
|
||||||
req.config._setupstate._teardown(item)
|
|
||||||
assert not l
|
|
||||||
|
|
||||||
def test_request_getmodulepath(self, testdir):
|
def test_request_getmodulepath(self, testdir):
|
||||||
modcol = testdir.getmodulecol("def test_somefunc(): pass")
|
modcol = testdir.getmodulecol("def test_somefunc(): pass")
|
||||||
|
|
|
@ -6,8 +6,24 @@ class TestSetupState:
|
||||||
ss = SetupState()
|
ss = SetupState()
|
||||||
item = testdir.getitem("def test_func(): pass")
|
item = testdir.getitem("def test_func(): pass")
|
||||||
l = [1]
|
l = [1]
|
||||||
|
ss.prepare(item)
|
||||||
ss.addfinalizer(l.pop, colitem=item)
|
ss.addfinalizer(l.pop, colitem=item)
|
||||||
ss._teardown(item)
|
assert l
|
||||||
|
ss._pop_and_teardown()
|
||||||
|
assert not l
|
||||||
|
|
||||||
|
def test_setup_scope_None(self, testdir):
|
||||||
|
item = testdir.getitem("def test_func(): pass")
|
||||||
|
ss = SetupState()
|
||||||
|
l = [1]
|
||||||
|
ss.prepare(item)
|
||||||
|
ss.addfinalizer(l.pop, colitem=None)
|
||||||
|
assert l
|
||||||
|
ss._pop_and_teardown()
|
||||||
|
assert l
|
||||||
|
ss._pop_and_teardown()
|
||||||
|
assert l
|
||||||
|
ss.teardown_all()
|
||||||
assert not l
|
assert not l
|
||||||
|
|
||||||
class TestSetupStateFunctional:
|
class TestSetupStateFunctional:
|
||||||
|
|
Loading…
Reference in New Issue