merge _pytest into pytester self-testing plugin
--HG-- branch : trunk
This commit is contained in:
parent
07c835fdf3
commit
7453fc107c
|
@ -1,123 +0,0 @@
|
||||||
import py
|
|
||||||
|
|
||||||
from pytest._core import HookRelay
|
|
||||||
|
|
||||||
def pytest_funcarg___pytest(request):
|
|
||||||
return PytestArg(request)
|
|
||||||
|
|
||||||
class PytestArg:
|
|
||||||
def __init__(self, request):
|
|
||||||
self.request = request
|
|
||||||
|
|
||||||
def gethookrecorder(self, hook):
|
|
||||||
hookrecorder = HookRecorder(hook._registry)
|
|
||||||
hookrecorder.start_recording(hook._hookspecs)
|
|
||||||
self.request.addfinalizer(hookrecorder.finish_recording)
|
|
||||||
return hookrecorder
|
|
||||||
|
|
||||||
class ParsedCall:
|
|
||||||
def __init__(self, name, locals):
|
|
||||||
assert '_name' not in locals
|
|
||||||
self.__dict__.update(locals)
|
|
||||||
self.__dict__.pop('self')
|
|
||||||
self._name = name
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
d = self.__dict__.copy()
|
|
||||||
del d['_name']
|
|
||||||
return "<ParsedCall %r(**%r)>" %(self._name, d)
|
|
||||||
|
|
||||||
class HookRecorder:
|
|
||||||
def __init__(self, registry):
|
|
||||||
self._registry = registry
|
|
||||||
self.calls = []
|
|
||||||
self._recorders = {}
|
|
||||||
|
|
||||||
def start_recording(self, hookspecs):
|
|
||||||
if not isinstance(hookspecs, (list, tuple)):
|
|
||||||
hookspecs = [hookspecs]
|
|
||||||
for hookspec in hookspecs:
|
|
||||||
assert hookspec not in self._recorders
|
|
||||||
class RecordCalls:
|
|
||||||
_recorder = self
|
|
||||||
for name, method in vars(hookspec).items():
|
|
||||||
if name[0] != "_":
|
|
||||||
setattr(RecordCalls, name, self._makecallparser(method))
|
|
||||||
recorder = RecordCalls()
|
|
||||||
self._recorders[hookspec] = recorder
|
|
||||||
self._registry.register(recorder)
|
|
||||||
self.hook = HookRelay(hookspecs, registry=self._registry,
|
|
||||||
prefix="pytest_")
|
|
||||||
|
|
||||||
def finish_recording(self):
|
|
||||||
for recorder in self._recorders.values():
|
|
||||||
self._registry.unregister(recorder)
|
|
||||||
self._recorders.clear()
|
|
||||||
|
|
||||||
def _makecallparser(self, method):
|
|
||||||
name = method.__name__
|
|
||||||
args, varargs, varkw, default = py.std.inspect.getargspec(method)
|
|
||||||
if not args or args[0] != "self":
|
|
||||||
args.insert(0, 'self')
|
|
||||||
fspec = py.std.inspect.formatargspec(args, varargs, varkw, default)
|
|
||||||
# we use exec because we want to have early type
|
|
||||||
# errors on wrong input arguments, using
|
|
||||||
# *args/**kwargs delays this and gives errors
|
|
||||||
# elsewhere
|
|
||||||
exec (py.code.compile("""
|
|
||||||
def %(name)s%(fspec)s:
|
|
||||||
self._recorder.calls.append(
|
|
||||||
ParsedCall(%(name)r, locals()))
|
|
||||||
""" % locals()))
|
|
||||||
return locals()[name]
|
|
||||||
|
|
||||||
def getcalls(self, names):
|
|
||||||
if isinstance(names, str):
|
|
||||||
names = names.split()
|
|
||||||
for name in names:
|
|
||||||
for cls in self._recorders:
|
|
||||||
if name in vars(cls):
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise ValueError("callname %r not found in %r" %(
|
|
||||||
name, self._recorders.keys()))
|
|
||||||
l = []
|
|
||||||
for call in self.calls:
|
|
||||||
if call._name in names:
|
|
||||||
l.append(call)
|
|
||||||
return l
|
|
||||||
|
|
||||||
def contains(self, entries):
|
|
||||||
from py.builtin import print_
|
|
||||||
i = 0
|
|
||||||
entries = list(entries)
|
|
||||||
backlocals = py.std.sys._getframe(1).f_locals
|
|
||||||
while entries:
|
|
||||||
name, check = entries.pop(0)
|
|
||||||
for ind, call in enumerate(self.calls[i:]):
|
|
||||||
if call._name == name:
|
|
||||||
print_("NAMEMATCH", name, call)
|
|
||||||
if eval(check, backlocals, call.__dict__):
|
|
||||||
print_("CHECKERMATCH", repr(check), "->", call)
|
|
||||||
else:
|
|
||||||
print_("NOCHECKERMATCH", repr(check), "-", call)
|
|
||||||
continue
|
|
||||||
i += ind + 1
|
|
||||||
break
|
|
||||||
print_("NONAMEMATCH", name, "with", call)
|
|
||||||
else:
|
|
||||||
raise AssertionError("could not find %r in %r" %(
|
|
||||||
name, self.calls[i:]))
|
|
||||||
|
|
||||||
def popcall(self, name):
|
|
||||||
for i, call in enumerate(self.calls):
|
|
||||||
if call._name == name:
|
|
||||||
del self.calls[i]
|
|
||||||
return call
|
|
||||||
raise ValueError("could not find call %r" %(name, ))
|
|
||||||
|
|
||||||
def getcall(self, name):
|
|
||||||
l = self.getcalls(name)
|
|
||||||
assert len(l) == 1, (name, l)
|
|
||||||
return l[0]
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ from fnmatch import fnmatch
|
||||||
from pytest._core import Config as pytestConfig
|
from pytest._core import Config as pytestConfig
|
||||||
from pytest.plugin.session import Collection
|
from pytest.plugin.session import Collection
|
||||||
from py.builtin import print_
|
from py.builtin import print_
|
||||||
|
from pytest._core import HookRelay
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
group = parser.getgroup("pylib")
|
group = parser.getgroup("pylib")
|
||||||
|
@ -19,7 +20,125 @@ def pytest_addoption(parser):
|
||||||
help=("discover tools on PATH instead of going through py.cmdline.")
|
help=("discover tools on PATH instead of going through py.cmdline.")
|
||||||
)
|
)
|
||||||
|
|
||||||
pytest_plugins = '_pytest'
|
def pytest_funcarg___pytest(request):
|
||||||
|
return PytestArg(request)
|
||||||
|
|
||||||
|
class PytestArg:
|
||||||
|
def __init__(self, request):
|
||||||
|
self.request = request
|
||||||
|
|
||||||
|
def gethookrecorder(self, hook):
|
||||||
|
hookrecorder = HookRecorder(hook._registry)
|
||||||
|
hookrecorder.start_recording(hook._hookspecs)
|
||||||
|
self.request.addfinalizer(hookrecorder.finish_recording)
|
||||||
|
return hookrecorder
|
||||||
|
|
||||||
|
class ParsedCall:
|
||||||
|
def __init__(self, name, locals):
|
||||||
|
assert '_name' not in locals
|
||||||
|
self.__dict__.update(locals)
|
||||||
|
self.__dict__.pop('self')
|
||||||
|
self._name = name
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
d = self.__dict__.copy()
|
||||||
|
del d['_name']
|
||||||
|
return "<ParsedCall %r(**%r)>" %(self._name, d)
|
||||||
|
|
||||||
|
class HookRecorder:
|
||||||
|
def __init__(self, registry):
|
||||||
|
self._registry = registry
|
||||||
|
self.calls = []
|
||||||
|
self._recorders = {}
|
||||||
|
|
||||||
|
def start_recording(self, hookspecs):
|
||||||
|
if not isinstance(hookspecs, (list, tuple)):
|
||||||
|
hookspecs = [hookspecs]
|
||||||
|
for hookspec in hookspecs:
|
||||||
|
assert hookspec not in self._recorders
|
||||||
|
class RecordCalls:
|
||||||
|
_recorder = self
|
||||||
|
for name, method in vars(hookspec).items():
|
||||||
|
if name[0] != "_":
|
||||||
|
setattr(RecordCalls, name, self._makecallparser(method))
|
||||||
|
recorder = RecordCalls()
|
||||||
|
self._recorders[hookspec] = recorder
|
||||||
|
self._registry.register(recorder)
|
||||||
|
self.hook = HookRelay(hookspecs, registry=self._registry,
|
||||||
|
prefix="pytest_")
|
||||||
|
|
||||||
|
def finish_recording(self):
|
||||||
|
for recorder in self._recorders.values():
|
||||||
|
self._registry.unregister(recorder)
|
||||||
|
self._recorders.clear()
|
||||||
|
|
||||||
|
def _makecallparser(self, method):
|
||||||
|
name = method.__name__
|
||||||
|
args, varargs, varkw, default = py.std.inspect.getargspec(method)
|
||||||
|
if not args or args[0] != "self":
|
||||||
|
args.insert(0, 'self')
|
||||||
|
fspec = py.std.inspect.formatargspec(args, varargs, varkw, default)
|
||||||
|
# we use exec because we want to have early type
|
||||||
|
# errors on wrong input arguments, using
|
||||||
|
# *args/**kwargs delays this and gives errors
|
||||||
|
# elsewhere
|
||||||
|
exec (py.code.compile("""
|
||||||
|
def %(name)s%(fspec)s:
|
||||||
|
self._recorder.calls.append(
|
||||||
|
ParsedCall(%(name)r, locals()))
|
||||||
|
""" % locals()))
|
||||||
|
return locals()[name]
|
||||||
|
|
||||||
|
def getcalls(self, names):
|
||||||
|
if isinstance(names, str):
|
||||||
|
names = names.split()
|
||||||
|
for name in names:
|
||||||
|
for cls in self._recorders:
|
||||||
|
if name in vars(cls):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise ValueError("callname %r not found in %r" %(
|
||||||
|
name, self._recorders.keys()))
|
||||||
|
l = []
|
||||||
|
for call in self.calls:
|
||||||
|
if call._name in names:
|
||||||
|
l.append(call)
|
||||||
|
return l
|
||||||
|
|
||||||
|
def contains(self, entries):
|
||||||
|
from py.builtin import print_
|
||||||
|
i = 0
|
||||||
|
entries = list(entries)
|
||||||
|
backlocals = py.std.sys._getframe(1).f_locals
|
||||||
|
while entries:
|
||||||
|
name, check = entries.pop(0)
|
||||||
|
for ind, call in enumerate(self.calls[i:]):
|
||||||
|
if call._name == name:
|
||||||
|
print_("NAMEMATCH", name, call)
|
||||||
|
if eval(check, backlocals, call.__dict__):
|
||||||
|
print_("CHECKERMATCH", repr(check), "->", call)
|
||||||
|
else:
|
||||||
|
print_("NOCHECKERMATCH", repr(check), "-", call)
|
||||||
|
continue
|
||||||
|
i += ind + 1
|
||||||
|
break
|
||||||
|
print_("NONAMEMATCH", name, "with", call)
|
||||||
|
else:
|
||||||
|
raise AssertionError("could not find %r in %r" %(
|
||||||
|
name, self.calls[i:]))
|
||||||
|
|
||||||
|
def popcall(self, name):
|
||||||
|
for i, call in enumerate(self.calls):
|
||||||
|
if call._name == name:
|
||||||
|
del self.calls[i]
|
||||||
|
return call
|
||||||
|
raise ValueError("could not find call %r" %(name, ))
|
||||||
|
|
||||||
|
def getcall(self, name):
|
||||||
|
l = self.getcalls(name)
|
||||||
|
assert len(l) == 1, (name, l)
|
||||||
|
return l[0]
|
||||||
|
|
||||||
|
|
||||||
def pytest_funcarg__linecomp(request):
|
def pytest_funcarg__linecomp(request):
|
||||||
return LineComp()
|
return LineComp()
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
import py
|
|
||||||
import os, sys
|
|
||||||
from pytest.plugin._pytest import HookRecorder
|
|
||||||
from pytest._core import Registry
|
|
||||||
|
|
||||||
def test_hookrecorder_basic():
|
|
||||||
rec = HookRecorder(Registry())
|
|
||||||
class ApiClass:
|
|
||||||
def pytest_xyz(self, arg):
|
|
||||||
"x"
|
|
||||||
rec.start_recording(ApiClass)
|
|
||||||
rec.hook.pytest_xyz(arg=123)
|
|
||||||
call = rec.popcall("pytest_xyz")
|
|
||||||
assert call.arg == 123
|
|
||||||
assert call._name == "pytest_xyz"
|
|
||||||
py.test.raises(ValueError, "rec.popcall('abc')")
|
|
||||||
|
|
||||||
def test_hookrecorder_basic_no_args_hook():
|
|
||||||
rec = HookRecorder(Registry())
|
|
||||||
apimod = type(os)('api')
|
|
||||||
def pytest_xyz():
|
|
||||||
"x"
|
|
||||||
apimod.pytest_xyz = pytest_xyz
|
|
||||||
rec.start_recording(apimod)
|
|
||||||
rec.hook.pytest_xyz()
|
|
||||||
call = rec.popcall("pytest_xyz")
|
|
||||||
assert call._name == "pytest_xyz"
|
|
||||||
|
|
||||||
def test_functional(testdir, linecomp):
|
|
||||||
reprec = testdir.inline_runsource("""
|
|
||||||
import py
|
|
||||||
from pytest._core import HookRelay, Registry
|
|
||||||
pytest_plugins="_pytest"
|
|
||||||
def test_func(_pytest):
|
|
||||||
class ApiClass:
|
|
||||||
def pytest_xyz(self, arg): "x"
|
|
||||||
hook = HookRelay([ApiClass], Registry())
|
|
||||||
rec = _pytest.gethookrecorder(hook)
|
|
||||||
class Plugin:
|
|
||||||
def pytest_xyz(self, arg):
|
|
||||||
return arg + 1
|
|
||||||
rec._registry.register(Plugin())
|
|
||||||
res = rec.hook.pytest_xyz(arg=41)
|
|
||||||
assert res == [42]
|
|
||||||
""")
|
|
||||||
reprec.assertoutcome(passed=1)
|
|
|
@ -1,5 +1,7 @@
|
||||||
import py
|
import py
|
||||||
from pytest.plugin.pytester import LineMatcher, LineComp
|
import os, sys
|
||||||
|
from pytest.plugin.pytester import LineMatcher, LineComp, HookRecorder
|
||||||
|
from pytest._core import Registry
|
||||||
|
|
||||||
def test_reportrecorder(testdir):
|
def test_reportrecorder(testdir):
|
||||||
item = testdir.getitem("def test_func(): pass")
|
item = testdir.getitem("def test_func(): pass")
|
||||||
|
@ -69,3 +71,44 @@ def test_testdir_runs_with_plugin(testdir):
|
||||||
"*1 passed*"
|
"*1 passed*"
|
||||||
])
|
])
|
||||||
|
|
||||||
|
def test_hookrecorder_basic():
|
||||||
|
rec = HookRecorder(Registry())
|
||||||
|
class ApiClass:
|
||||||
|
def pytest_xyz(self, arg):
|
||||||
|
"x"
|
||||||
|
rec.start_recording(ApiClass)
|
||||||
|
rec.hook.pytest_xyz(arg=123)
|
||||||
|
call = rec.popcall("pytest_xyz")
|
||||||
|
assert call.arg == 123
|
||||||
|
assert call._name == "pytest_xyz"
|
||||||
|
py.test.raises(ValueError, "rec.popcall('abc')")
|
||||||
|
|
||||||
|
def test_hookrecorder_basic_no_args_hook():
|
||||||
|
rec = HookRecorder(Registry())
|
||||||
|
apimod = type(os)('api')
|
||||||
|
def pytest_xyz():
|
||||||
|
"x"
|
||||||
|
apimod.pytest_xyz = pytest_xyz
|
||||||
|
rec.start_recording(apimod)
|
||||||
|
rec.hook.pytest_xyz()
|
||||||
|
call = rec.popcall("pytest_xyz")
|
||||||
|
assert call._name == "pytest_xyz"
|
||||||
|
|
||||||
|
def test_functional(testdir, linecomp):
|
||||||
|
reprec = testdir.inline_runsource("""
|
||||||
|
import py
|
||||||
|
from pytest._core import HookRelay, Registry
|
||||||
|
pytest_plugins="pytester"
|
||||||
|
def test_func(_pytest):
|
||||||
|
class ApiClass:
|
||||||
|
def pytest_xyz(self, arg): "x"
|
||||||
|
hook = HookRelay([ApiClass], Registry())
|
||||||
|
rec = _pytest.gethookrecorder(hook)
|
||||||
|
class Plugin:
|
||||||
|
def pytest_xyz(self, arg):
|
||||||
|
return arg + 1
|
||||||
|
rec._registry.register(Plugin())
|
||||||
|
res = rec.hook.pytest_xyz(arg=41)
|
||||||
|
assert res == [42]
|
||||||
|
""")
|
||||||
|
reprec.assertoutcome(passed=1)
|
||||||
|
|
Loading…
Reference in New Issue