integrate plugin hook checking directly when registering
remove plugintester plugin, all functionality now in testdir --HG-- branch : trunk
This commit is contained in:
parent
db2ef3e9e8
commit
a93918a480
|
@ -327,22 +327,3 @@ class CoveragePlugin:
|
|||
self.coverage.start()
|
||||
|
||||
|
||||
# ===============================================================================
|
||||
# plugin tests
|
||||
# ===============================================================================
|
||||
# XXX
|
||||
'''
|
||||
def test_generic(plugintester):
|
||||
plugintester.apicheck(EventlogPlugin)
|
||||
|
||||
testdir = plugintester.testdir()
|
||||
testdir.makepyfile("""
|
||||
def test_pass():
|
||||
pass
|
||||
""")
|
||||
testdir.runpytest("--eventlog=event.log")
|
||||
s = testdir.tmpdir.join("event.log").read()
|
||||
assert s.find("TestrunStart") != -1
|
||||
assert s.find("ItemTestReport") != -1
|
||||
assert s.find("TestrunFinish") != -1
|
||||
'''
|
||||
|
|
|
@ -115,10 +115,7 @@ gr_tests = greenlet.getcurrent()
|
|||
# plugin tests
|
||||
# ===============================================================================
|
||||
|
||||
def test_generic(plugintester):
|
||||
plugintester.apicheck(TwistedPlugin)
|
||||
|
||||
testdir = plugintester.testdir()
|
||||
def test_generic(testdir):
|
||||
testdir.makepyfile('''
|
||||
def test_pass():
|
||||
pass
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import py
|
||||
|
||||
pytest_plugins = "pytester", "plugintester"
|
||||
pytest_plugins = "pytester"
|
||||
|
||||
def pytest_collect_file(path, parent):
|
||||
if path.basename.startswith("pytest_") and path.ext == ".py":
|
||||
|
|
|
@ -99,9 +99,6 @@ class HookRecorder:
|
|||
return l[0]
|
||||
|
||||
|
||||
def test_generic(plugintester):
|
||||
plugintester.hookcheck()
|
||||
|
||||
def test_hookrecorder_basic():
|
||||
comregistry = py._com.Registry()
|
||||
rec = HookRecorder(comregistry)
|
||||
|
|
|
@ -172,9 +172,6 @@ def test_implied_different_sessions(tmpdir):
|
|||
assert x('-n3') == 'DSession'
|
||||
assert x('-f') == 'LooponfailingSession'
|
||||
|
||||
def test_generic(plugintester):
|
||||
plugintester.hookcheck()
|
||||
|
||||
def test_plugin_specify(testdir):
|
||||
testdir.chdir()
|
||||
config = py.test.raises(ImportError, """
|
||||
|
|
|
@ -156,8 +156,3 @@ class TestDoctests:
|
|||
" 1",
|
||||
"*test_txtfile_failing.txt:2: DocTestFailure"
|
||||
])
|
||||
|
||||
|
||||
def test_generic(plugintester):
|
||||
plugintester.hookcheck()
|
||||
|
||||
|
|
|
@ -38,10 +38,6 @@ class Execnetcleanup:
|
|||
self._gateways[-1].exit()
|
||||
return res
|
||||
|
||||
def test_generic(plugintester):
|
||||
plugintester.hookcheck(cls=Execnetcleanup)
|
||||
plugintester.hookcheck()
|
||||
|
||||
@py.test.mark.xfail("clarify plugin registration/unregistration")
|
||||
def test_execnetplugin(testdir):
|
||||
p = ExecnetcleanupPlugin()
|
||||
|
|
|
@ -50,9 +50,6 @@ def get_coverage(datafile, config):
|
|||
return coverage
|
||||
|
||||
|
||||
def test_generic(plugintester):
|
||||
plugintester.hookcheck()
|
||||
|
||||
def test_functional(testdir):
|
||||
py.test.importorskip("figleaf")
|
||||
testdir.plugins.append("figleaf")
|
||||
|
|
|
@ -21,9 +21,6 @@ def pytest_unconfigure(config):
|
|||
# plugin tests
|
||||
# ===============================================================================
|
||||
|
||||
def test_generic(plugintester):
|
||||
plugintester.hookcheck()
|
||||
|
||||
def test_functional(testdir):
|
||||
testdir.makepyfile("""
|
||||
def test_pass():
|
||||
|
|
|
@ -35,9 +35,6 @@ class Capture:
|
|||
self._capture = self._captureclass()
|
||||
return res
|
||||
|
||||
def test_generic(plugintester):
|
||||
plugintester.hookcheck()
|
||||
|
||||
class TestCapture:
|
||||
def test_std_functional(self, testdir):
|
||||
reprec = testdir.inline_runsource("""
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
"""
|
||||
plugin with support classes and functions for testing pytest functionality
|
||||
"""
|
||||
import py
|
||||
from py.__.test.plugin import api
|
||||
|
||||
def pytest_funcarg__plugintester(request):
|
||||
return PluginTester(request)
|
||||
|
||||
class PluginTester:
|
||||
def __init__(self, request):
|
||||
self.request = request
|
||||
|
||||
def testdir(self, globs=None):
|
||||
from pytest_pytester import TmpTestdir
|
||||
testdir = TmpTestdir(self.request)
|
||||
self.request.addfinalizer(testdir.finalize)
|
||||
if globs is None:
|
||||
globs = py.std.sys._getframe(-1).f_globals
|
||||
testdir.plugins.append(globs)
|
||||
#
|
||||
#for colitem in self.request.listchain():
|
||||
# if isinstance(colitem, py.test.collect.Module) and \
|
||||
# colitem.name.startswith("pytest_"):
|
||||
# crunner.plugins.append(colitem.fspath.purebasename)
|
||||
# break
|
||||
return testdir
|
||||
|
||||
def hookcheck(self, name=None, cls=None):
|
||||
if cls is None:
|
||||
if name is None:
|
||||
name = py.std.sys._getframe(-1).f_globals['__name__']
|
||||
plugin = __import__(name)
|
||||
else:
|
||||
plugin = cls
|
||||
print "checking", plugin
|
||||
fail = False
|
||||
pm = py.test._PluginManager()
|
||||
methods = collectattr(plugin)
|
||||
hooks = collectattr(api.PluginHooks)
|
||||
getargs = py.std.inspect.getargs
|
||||
|
||||
def isgenerichook(name):
|
||||
return name.startswith("pytest_funcarg__")
|
||||
|
||||
while methods:
|
||||
name, method = methods.popitem()
|
||||
if isgenerichook(name):
|
||||
continue
|
||||
if name not in hooks:
|
||||
print "found unknown hook: %s" % name
|
||||
fail = True
|
||||
else:
|
||||
hook = hooks[name]
|
||||
if not hasattr(hook, 'func_code'):
|
||||
continue # XXX do some checks on attributes as well?
|
||||
method_args = getargs(method.func_code)
|
||||
if '__call__' in method_args[0]:
|
||||
method_args[0].remove('__call__')
|
||||
hookargs = getargs(hook.func_code)
|
||||
for arg, hookarg in zip(method_args[0], hookargs[0]):
|
||||
if arg != hookarg:
|
||||
print "argument mismatch:"
|
||||
print "actual : %s.%s" %(plugin.__name__, formatdef(method))
|
||||
print "required:", formatdef(hook)
|
||||
fail = True
|
||||
break
|
||||
if not fail:
|
||||
print "matching hook:", formatdef(method)
|
||||
if fail:
|
||||
py.test.fail("Plugin API error")
|
||||
|
||||
def collectattr(obj, prefixes=("pytest_",)):
|
||||
methods = {}
|
||||
for apiname in vars(obj):
|
||||
for prefix in prefixes:
|
||||
if apiname.startswith(prefix):
|
||||
methods[apiname] = getattr(obj, apiname)
|
||||
return methods
|
||||
|
||||
def formatdef(func):
|
||||
formatargspec = py.std.inspect.formatargspec
|
||||
getargspec = py.std.inspect.formatargspec
|
||||
return "%s%s" %(
|
||||
func.func_name,
|
||||
py.std.inspect.formatargspec(*py.std.inspect.getargspec(func))
|
||||
)
|
||||
|
||||
# ===============================================================================
|
||||
# plugin tests
|
||||
# ===============================================================================
|
||||
|
||||
def test_generic(plugintester):
|
||||
plugintester.hookcheck()
|
|
@ -38,9 +38,6 @@ def pytest_terminal_summary(terminalreporter):
|
|||
break
|
||||
|
||||
|
||||
def test_apicheck(plugintester):
|
||||
plugintester.hookcheck()
|
||||
|
||||
def test_toproxy(testdir, monkeypatch):
|
||||
l = []
|
||||
class MockProxy:
|
||||
|
|
|
@ -38,8 +38,4 @@ class PylintItem(py.test.collect.Item):
|
|||
print ">>>",
|
||||
print rating
|
||||
|
||||
def test_generic(plugintester):
|
||||
plugintester.hookcheck(PylintPlugin)
|
||||
|
||||
#def test_functional <pull from figleaf plugin>
|
||||
|
||||
|
|
|
@ -26,9 +26,6 @@ def pytest_funcarg__reportrecorder(request):
|
|||
request.addfinalizer(lambda: reprec.comregistry.unregister(reprec))
|
||||
return reprec
|
||||
|
||||
def test_generic(plugintester):
|
||||
plugintester.hookcheck()
|
||||
|
||||
class RunResult:
|
||||
def __init__(self, ret, outlines, errlines):
|
||||
self.ret = ret
|
||||
|
|
|
@ -348,8 +348,6 @@ def localrefcheck(tryfn, path, lineno):
|
|||
#
|
||||
# PLUGIN tests
|
||||
#
|
||||
def test_generic(plugintester):
|
||||
plugintester.hookcheck()
|
||||
|
||||
def test_deindent():
|
||||
assert deindent('foo') == 'foo'
|
||||
|
|
|
@ -300,9 +300,8 @@ class TestWithFunctionIntegration:
|
|||
archive.init_db()
|
||||
return archive
|
||||
|
||||
def test_collection_report(self, plugintester):
|
||||
def test_collection_report(self, testdir):
|
||||
py.test.skip("Needs a rewrite for db version.")
|
||||
testdir = plugintester.testdir()
|
||||
ok = testdir.makepyfile(test_collection_ok="")
|
||||
skip = testdir.makepyfile(test_collection_skip="import py ; py.test.skip('hello')")
|
||||
fail = testdir.makepyfile(test_collection_fail="XXX")
|
||||
|
@ -360,9 +359,7 @@ class TestWithFunctionIntegration:
|
|||
assert entry_lines[-1][0] == ' '
|
||||
assert 'ValueError' in entry
|
||||
|
||||
def test_generic(plugintester):
|
||||
plugintester.hookcheck()
|
||||
testdir = plugintester.testdir()
|
||||
def test_generic(testdir):
|
||||
testdir.makepyfile("""
|
||||
import py
|
||||
def test_pass():
|
||||
|
|
|
@ -163,8 +163,7 @@ class TestWithFunctionIntegration:
|
|||
testdir.runpytest(*args)
|
||||
return filter(None, resultlog.readlines(cr=0))
|
||||
|
||||
def test_collection_report(self, plugintester):
|
||||
testdir = plugintester.testdir()
|
||||
def test_collection_report(self, testdir):
|
||||
ok = testdir.makepyfile(test_collection_ok="")
|
||||
skip = testdir.makepyfile(test_collection_skip="import py ; py.test.skip('hello')")
|
||||
fail = testdir.makepyfile(test_collection_fail="XXX")
|
||||
|
@ -186,8 +185,7 @@ class TestWithFunctionIntegration:
|
|||
assert x.startswith(" ")
|
||||
assert "XXX" in "".join(lines[1:])
|
||||
|
||||
def test_log_test_outcomes(self, plugintester):
|
||||
testdir = plugintester.testdir()
|
||||
def test_log_test_outcomes(self, testdir):
|
||||
mod = testdir.makepyfile(test_mod="""
|
||||
import py
|
||||
def test_pass(): pass
|
||||
|
@ -224,9 +222,7 @@ class TestWithFunctionIntegration:
|
|||
assert entry_lines[-1][0] == ' '
|
||||
assert 'ValueError' in entry
|
||||
|
||||
def test_generic(plugintester, LineMatcher):
|
||||
plugintester.hookcheck()
|
||||
testdir = plugintester.testdir()
|
||||
def test_generic(testdir, LineMatcher):
|
||||
testdir.plugins.append("resultlog")
|
||||
testdir.makepyfile("""
|
||||
import py
|
||||
|
|
|
@ -126,8 +126,6 @@ class ItemFixtureReport(BaseReport):
|
|||
#
|
||||
# ===============================================================================
|
||||
|
||||
def test_generic(plugintester):
|
||||
plugintester.hookcheck()
|
||||
|
||||
class TestSetupState:
|
||||
def test_setup_prepare(self, testdir):
|
||||
|
|
|
@ -746,7 +746,3 @@ def test_repr_python_version(monkeypatch):
|
|||
py.std.sys.version_info = x = (2,3)
|
||||
assert repr_pythonversion() == str(x)
|
||||
|
||||
def test_generic(plugintester):
|
||||
plugintester.hookcheck()
|
||||
plugintester.hookcheck(cls=TerminalReporter)
|
||||
plugintester.hookcheck(cls=CollectonlyReporter)
|
||||
|
|
|
@ -21,8 +21,6 @@ def pytest_funcarg__tmpdir(request):
|
|||
#
|
||||
# ===============================================================================
|
||||
#
|
||||
def test_generic(plugintester):
|
||||
plugintester.hookcheck()
|
||||
|
||||
def test_funcarg(testdir):
|
||||
from py.__.test.funcargs import FuncargRequest
|
||||
|
|
|
@ -65,9 +65,6 @@ class UnitTestFunction(py.test.collect.Function):
|
|||
instance.tearDown()
|
||||
|
||||
|
||||
def test_generic(plugintester):
|
||||
plugintester.hookcheck()
|
||||
|
||||
def test_simple_unittest(testdir):
|
||||
testpath = testdir.makepyfile("""
|
||||
import unittest
|
||||
|
|
|
@ -56,11 +56,8 @@ def pytest_terminal_summary(terminalreporter):
|
|||
#
|
||||
# ===============================================================================
|
||||
|
||||
def test_generic(plugintester):
|
||||
plugintester.hookcheck()
|
||||
|
||||
def test_xfail(plugintester, linecomp):
|
||||
testdir = plugintester.testdir()
|
||||
def test_xfail(testdir, linecomp):
|
||||
p = testdir.makepyfile(test_one="""
|
||||
import py
|
||||
pytest_plugins="pytest_xfail",
|
||||
|
|
|
@ -9,6 +9,8 @@ def check_old_use(mod, modname):
|
|||
assert not hasattr(mod, clsname), (mod, clsname)
|
||||
|
||||
class PluginManager(object):
|
||||
class Error(Exception):
|
||||
"""signals a plugin specific error."""
|
||||
def __init__(self, comregistry=None):
|
||||
if comregistry is None:
|
||||
comregistry = py._com.Registry()
|
||||
|
@ -34,6 +36,7 @@ class PluginManager(object):
|
|||
assert name not in self.impname2plugin
|
||||
self.impname2plugin[name] = plugin
|
||||
self.hook.pytest_plugin_registered(plugin=plugin)
|
||||
self._checkplugin(plugin)
|
||||
self.comregistry.register(plugin)
|
||||
return True
|
||||
|
||||
|
@ -96,6 +99,46 @@ class PluginManager(object):
|
|||
check_old_use(mod, modname)
|
||||
self.register(mod)
|
||||
self.consider_module(mod)
|
||||
|
||||
def _checkplugin(self, plugin):
|
||||
# =====================================================
|
||||
# check plugin hooks
|
||||
# =====================================================
|
||||
methods = collectattr(plugin)
|
||||
hooks = collectattr(api.PluginHooks)
|
||||
stringio = py.std.StringIO.StringIO()
|
||||
def Print(*args):
|
||||
if args:
|
||||
stringio.write(" ".join(map(str, args)))
|
||||
stringio.write("\n")
|
||||
|
||||
fail = False
|
||||
while methods:
|
||||
name, method = methods.popitem()
|
||||
#print "checking", name
|
||||
if isgenerichook(name):
|
||||
continue
|
||||
if name not in hooks:
|
||||
Print("found unknown hook:", name)
|
||||
fail = True
|
||||
else:
|
||||
method_args = getargs(method)
|
||||
if '__call__' in method_args:
|
||||
method_args.remove('__call__')
|
||||
hook = hooks[name]
|
||||
hookargs = getargs(hook)
|
||||
for arg, hookarg in zip(method_args, hookargs):
|
||||
if arg != hookarg:
|
||||
Print("argument mismatch: %r != %r" %(arg, hookarg))
|
||||
Print("actual : %s" %(formatdef(method)))
|
||||
Print("required:", formatdef(hook))
|
||||
fail = True
|
||||
break
|
||||
#if not fail:
|
||||
# print "matching hook:", formatdef(method)
|
||||
if fail:
|
||||
name = getattr(plugin, '__name__', plugin)
|
||||
raise self.Error("%s:\n%s" %(name, stringio.getvalue()))
|
||||
#
|
||||
#
|
||||
# API for interacting with registered and instantiated plugin objects
|
||||
|
@ -169,3 +212,30 @@ def importplugin(importspec):
|
|||
#print "syspath:", py.std.sys.path
|
||||
#print "curdir:", py.std.os.getcwd()
|
||||
return __import__(importspec) # show the original exception
|
||||
|
||||
|
||||
|
||||
def isgenerichook(name):
|
||||
return name == "pytest_plugins" or \
|
||||
name.startswith("pytest_funcarg__") or \
|
||||
name.startswith("pytest_option_")
|
||||
|
||||
def getargs(func):
|
||||
args = py.std.inspect.getargs(func.func_code)[0]
|
||||
startindex = hasattr(func, 'im_self') and 1 or 0
|
||||
return args[startindex:]
|
||||
|
||||
def collectattr(obj, prefixes=("pytest_",)):
|
||||
methods = {}
|
||||
for apiname in dir(obj):
|
||||
for prefix in prefixes:
|
||||
if apiname.startswith(prefix):
|
||||
methods[apiname] = getattr(obj, apiname)
|
||||
return methods
|
||||
|
||||
def formatdef(func):
|
||||
return "%s%s" %(
|
||||
func.func_name,
|
||||
py.std.inspect.formatargspec(*py.std.inspect.getargspec(func))
|
||||
)
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import py, os
|
||||
from py.__.test.pluginmanager import PluginManager, canonical_importname
|
||||
from py.__.test.pluginmanager import PluginManager, canonical_importname, collectattr
|
||||
|
||||
class TestBootstrapping:
|
||||
def test_consider_env_fails_to_import(self, monkeypatch):
|
||||
|
@ -102,7 +102,8 @@ class TestBootstrapping:
|
|||
|
||||
def test_registry(self):
|
||||
pp = PluginManager()
|
||||
a1, a2 = object(), object()
|
||||
class A: pass
|
||||
a1, a2 = A(), A()
|
||||
pp.register(a1)
|
||||
assert pp.isregistered(a1)
|
||||
pp.register(a2)
|
||||
|
@ -126,6 +127,20 @@ class TestBootstrapping:
|
|||
#assert not pp.isregistered(mod2)
|
||||
assert pp.getplugins() == [mod] # does not actually modify plugins
|
||||
|
||||
def test_register_mismatch_method(self):
|
||||
pp = PluginManager()
|
||||
class hello:
|
||||
def pytest_gurgel(self):
|
||||
pass
|
||||
py.test.raises(pp.Error, "pp.register(hello())")
|
||||
|
||||
def test_register_mismatch_arg(self):
|
||||
pp = PluginManager()
|
||||
class hello:
|
||||
def pytest_configure(self, asd):
|
||||
pass
|
||||
excinfo = py.test.raises(pp.Error, "pp.register(hello())")
|
||||
|
||||
def test_canonical_importname(self):
|
||||
for name in 'xyz', 'pytest_xyz', 'pytest_Xyz', 'Xyz':
|
||||
impname = canonical_importname(name)
|
||||
|
@ -223,3 +238,14 @@ class TestPytestPluginInteractions:
|
|||
results = call.execute()
|
||||
assert results == [1,2,2]
|
||||
|
||||
def test_collectattr():
|
||||
class A:
|
||||
def pytest_hello(self):
|
||||
pass
|
||||
class B(A):
|
||||
def pytest_world(self):
|
||||
pass
|
||||
methods = py.builtin.sorted(collectattr(B))
|
||||
assert list(methods) == ['pytest_hello', 'pytest_world']
|
||||
methods = py.builtin.sorted(collectattr(B()))
|
||||
assert list(methods) == ['pytest_hello', 'pytest_world']
|
||||
|
|
Loading…
Reference in New Issue