* refine and rename pycollect related hooks

* refine runtest/test function call protocol

--HG--
branch : trunk
This commit is contained in:
holger krekel 2009-06-15 15:15:40 +02:00
parent a59d602bce
commit 771438fde5
11 changed files with 86 additions and 51 deletions

View File

@ -91,12 +91,11 @@ def pytest_configure(config):
def pytest_unconfigure(config):
gr_twisted.switch(None)
def pytest_pyfunc_call(pyfuncitem, *args, **kwargs):
args = args or pyfuncitem._args # generator tests
def pytest_pyfunc_call(pyfuncitem):
# XXX1 kwargs?
# XXX2 we want to delegate actual call to next plugin
# (which may want to produce test coverage, etc.)
res = gr_twisted.switch(lambda: pyfuncitem.obj(*args))
res = gr_twisted.switch(lambda: pyfuncitem.call())
if res:
res.raiseException()
return True # indicates that we performed the function call

View File

@ -46,8 +46,8 @@ for early mismatch reporting and minimizes version incompatibilites.
.. _`original definition of the hook`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/hookspec.py
"runtest" hooks
-------------------
generic "runtest" hooks
------------------------------
Each test item is usually executed by calling the following three hooks::
@ -101,6 +101,26 @@ The call object contains information about a performed call::
.. _`pytest_terminal plugin`: http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/pytest_terminal.py
generic collection hooks
------------------------------
XXX
Python module and test function hooks
-------------------------------------------
For influencing the collection of objects in Python modules
you can use the following hook:
pytest_pycollect_makeitem(collector, name, obj)
This hook will be called for each Python object in a collected
Python module. The return value is a custom `collection node`_.
.. XXX or ``False`` if you want to indicate that the given item should not be collected.
Included default plugins
=============================
@ -114,6 +134,8 @@ Additionally you can check out some more contributed plugins here
.. _`collection process`:
.. _`collection node`:
Test Collection process
======================================================

View File

@ -425,11 +425,11 @@ class Item(Node):
def run(self):
""" deprecated, here because subclasses might call it. """
return self.execute(self.obj, *self._args)
return self.execute(self.obj)
def execute(self, obj, *args):
def execute(self, obj):
""" deprecated, here because subclasses might call it. """
return obj(*args)
return obj()
def reportinfo(self):
return self.fspath, None, ""

View File

@ -34,9 +34,7 @@ def pytest_deselected(items):
# ------------------------------------------------------------------------------
# collection hooks
# ------------------------------------------------------------------------------
def pytest_make_collect_report(collector):
""" perform a collection and return a collection. """
pytest_make_collect_report.firstresult = True
def pytest_collect_file(path, parent):
""" return Collection node or None. """
@ -50,25 +48,37 @@ pytest_collect_recurse.firstresult = True
def pytest_collect_directory(path, parent):
""" return Collection node or None. """
def pytest_pycollect_obj(collector, name, obj):
""" return custom item/collector for a python object in a module, or None. """
pytest_pycollect_obj.firstresult = True
def pytest_generate_tests(metafunc):
""" generate (multiple) parametrized calls to a test function."""
def pytest_collectstart(collector):
""" collector starts collecting. """
def pytest_collectreport(rep):
""" collector finished collecting. """
def pytest_make_collect_report(collector):
""" perform a collection and return a collection. """
pytest_make_collect_report.firstresult = True
# XXX rename to item_collected()? meaning in distribution context?
def pytest_itemstart(item, node=None):
""" test item gets collected. """
# ------------------------------------------------------------------------------
# runtest related hooks
# Python test function related hooks
# ------------------------------------------------------------------------------
def pytest_pycollect_makeitem(collector, name, obj):
""" return custom item/collector for a python object in a module, or None. """
pytest_pycollect_makeitem.firstresult = True
def pytest_pyfunc_call(pyfuncitem):
""" perform function call with the given function arguments. """
pytest_pyfunc_call.firstresult = True
def pytest_generate_tests(metafunc):
""" generate (multiple) parametrized calls to a test function."""
# ------------------------------------------------------------------------------
# generic runtest related hooks
# ------------------------------------------------------------------------------
def pytest_runtest_setup(item):
""" called before pytest_runtest_call(). """
@ -83,10 +93,6 @@ def pytest_runtest_protocol(item):
""" run given test item and return test report. """
pytest_runtest_protocol.firstresult = True
def pytest_pyfunc_call(pyfuncitem, args, kwargs):
""" return True if we consumed/did the call to the python function item. """
pytest_pyfunc_call.firstresult = True
def pytest_runtest_makereport(item, call):
""" make ItemTestReport for the specified test outcome. """
pytest_runtest_makereport.firstresult = True
@ -95,7 +101,7 @@ def pytest_runtest_logreport(rep):
""" process item test report. """
# ------------------------------------------------------------------------------
# reporting hooks (invoked from pytest_terminal.py)
# generic reporting hooks (invoked from pytest_terminal.py)
# ------------------------------------------------------------------------------
def pytest_report_teststatus(rep):
""" return shortletter and verbose word. """
@ -117,6 +123,7 @@ def pytest_doctest_prepare_content(content):
""" return processed content for a given doctest"""
pytest_doctest_prepare_content.firstresult = True
# ------------------------------------------------------------------------------
# misc hooks
# ------------------------------------------------------------------------------
@ -133,8 +140,6 @@ def pytest_internalerror(excrepr):
def pytest_trace(category, msg):
""" called for debug info. """
# ------------------------------------------------------------------------------
# distributed testing
# ------------------------------------------------------------------------------

View File

@ -2,9 +2,14 @@
import py
def pytest_pyfunc_call(__call__, pyfuncitem, args, kwargs):
def pytest_pyfunc_call(__call__, pyfuncitem):
if not __call__.execute(firstresult=True):
pyfuncitem.obj(*args, **kwargs)
testfunction = pyfuncitem.obj
if pyfuncitem._isyieldedfunction():
testfunction(*pyfuncitem._args)
else:
funcargs = pyfuncitem.funcargs
testfunction(**funcargs)
def pytest_collect_file(path, parent):
ext = path.ext

View File

@ -29,8 +29,8 @@ class Execnetcleanup:
l.append(gw)
#for gw in l:
# gw.join()
#
def pytest_pyfunc_call(self, __call__, pyfuncitem, args, kwargs):
def pytest_pyfunc_call(self, __call__, pyfuncitem):
if self._gateways is not None:
gateways = self._gateways[:]
res = __call__.execute(firstresult=True)

View File

@ -22,8 +22,8 @@ def pytest_funcarg__capfd(request):
request.addfinalizer(capture.finalize)
return capture
def pytest_pyfunc_call(pyfuncitem, args, kwargs):
for funcarg, value in kwargs.items():
def pytest_pyfunc_call(pyfuncitem):
for funcarg, value in pyfuncitem.funcargs.items():
if funcarg == "capsys" or funcarg == "capfd":
value.reset()

View File

@ -271,7 +271,7 @@ class LinkCheckerMaker(py.test.collect.Collector):
if tryfn.startswith('http:') or tryfn.startswith('https'):
if self.config.getvalue("urlcheck"):
yield CheckLink(name, parent=self,
args=(tryfn, path, lineno, timeout), callobj=urlcheck)
args=(tryfn, path, lineno, timeout), checkfunc=urlcheck)
elif tryfn.startswith('webcal:'):
continue
else:
@ -282,16 +282,19 @@ class LinkCheckerMaker(py.test.collect.Collector):
checkfn = tryfn
if checkfn.strip() and (1 or checkfn.endswith('.html')):
yield CheckLink(name, parent=self,
args=(tryfn, path, lineno), callobj=localrefcheck)
args=(tryfn, path, lineno), checkfunc=localrefcheck)
class CheckLink(py.test.collect.Function):
def reportinfo(self, basedir=None):
return (self.fspath, self._args[2], "checklink: %s" % self._args[0])
class CheckLink(py.test.collect.Item):
def __init__(self, name, parent, args, checkfunc):
super(CheckLink, self).__init__(name, parent)
self.args = args
self.checkfunc = checkfunc
def setup(self):
pass
def teardown(self):
pass
def runtest(self):
return self.checkfunc(*self.args)
def reportinfo(self, basedir=None):
return (self.fspath, self.args[2], "checklink: %s" % self.args[0])
def urlcheck(tryfn, path, lineno, TIMEOUT_URLOPEN):
old = py.std.socket.getdefaulttimeout()

View File

@ -13,7 +13,7 @@ this code is somewhat derived from Guido Wesdorps
"""
import py
def pytest_pycollect_obj(collector, name, obj):
def pytest_pycollect_makeitem(collector, name, obj):
if py.std.inspect.isclass(obj) and issubclass(obj, py.std.unittest.TestCase):
return UnitTestCase(name, parent=collector)

View File

@ -119,9 +119,9 @@ class PyCollectorMixin(PyobjMixin, py.test.collect.Collector):
return self.join(name)
def makeitem(self, name, obj):
res = self.config.hook.pytest_pycollect_obj(
res = self.config.hook.pytest_pycollect_makeitem(
collector=self, name=name, obj=obj)
if res:
if res is not None:
return res
if (self.classnamefilter(name)) and \
py.std.inspect.isclass(obj):
@ -314,11 +314,11 @@ class Function(FunctionMixin, py.test.collect.Item):
and executing a Python callable test object.
"""
_genid = None
def __init__(self, name, parent=None, args=(),
def __init__(self, name, parent=None, args=None,
callspec=None, callobj=_dummy):
super(Function, self).__init__(name, parent)
self._args = args
if args:
if self._isyieldedfunction():
assert not callspec, "yielded functions (deprecated) cannot have funcargs"
else:
if callspec is not None:
@ -331,6 +331,9 @@ class Function(FunctionMixin, py.test.collect.Item):
if callobj is not _dummy:
self._obj = callobj
def _isyieldedfunction(self):
return self._args is not None
def readkeywords(self):
d = super(Function, self).readkeywords()
d.update(self.obj.func_dict)
@ -338,9 +341,7 @@ class Function(FunctionMixin, py.test.collect.Item):
def runtest(self):
""" execute the underlying test function. """
kwargs = getattr(self, 'funcargs', {})
self.config.hook.pytest_pyfunc_call(
pyfuncitem=self, args=self._args, kwargs=kwargs)
self.config.hook.pytest_pyfunc_call(pyfuncitem=self)
def setup(self):
super(Function, self).setup()

View File

@ -264,10 +264,10 @@ class TestFunction:
item = testdir.getitem("def test_func(): raise ValueError")
config = item.config
class MyPlugin1:
def pytest_pyfunc_call(self, pyfuncitem, *args, **kwargs):
def pytest_pyfunc_call(self, pyfuncitem):
raise ValueError
class MyPlugin2:
def pytest_pyfunc_call(self, pyfuncitem, *args, **kwargs):
def pytest_pyfunc_call(self, pyfuncitem):
return True
config.pluginmanager.register(MyPlugin1())
config.pluginmanager.register(MyPlugin2())