* refactor plugin support to work directly with

modules, no classes required anymore.
* call funcarg hook if defined on class

--HG--
branch : trunk
This commit is contained in:
holger krekel 2009-05-18 23:26:16 +02:00
parent 4035fa6326
commit 191d02aef2
33 changed files with 606 additions and 628 deletions

View File

@ -3,13 +3,7 @@ pytest_plugins = '_pytest doctest pytester'.split()
rsyncdirs = ['../doc'] rsyncdirs = ['../doc']
import py import py
class PylibTestconfigPlugin: def pytest_addoption(parser):
def pytest_funcarg__specssh(self, request):
return getspecssh(request.config)
def pytest_funcarg__specsocket(self, request):
return getsocketspec(request.config)
def pytest_addoption(self, parser):
group = parser.addgroup("pylib", "py lib testing options") group = parser.addgroup("pylib", "py lib testing options")
group.addoption('--sshhost', group.addoption('--sshhost',
action="store", dest="sshhost", default=None, action="store", dest="sshhost", default=None,
@ -21,7 +15,11 @@ class PylibTestconfigPlugin:
action="store_true", dest="runslowtests", default=False, action="store_true", dest="runslowtests", default=False,
help=("run slow tests")) help=("run slow tests"))
ConftestPlugin = PylibTestconfigPlugin def pytest_funcarg__specssh(request):
return getspecssh(request.config)
def pytest_funcarg__specsocket(request):
return getsocketspec(request.config)
# configuration information for tests # configuration information for tests
def getgspecs(config=None): def getgspecs(config=None):

View File

@ -78,12 +78,15 @@ class FuncargRequest:
self.function = pyfuncitem.obj self.function = pyfuncitem.obj
self.module = pyfuncitem._getparent(py.test.collect.Module).obj self.module = pyfuncitem._getparent(py.test.collect.Module).obj
self.cls = getattr(self.function, 'im_class', None) self.cls = getattr(self.function, 'im_class', None)
self.instance = getattr(self.function, 'im_self', None)
self.config = pyfuncitem.config self.config = pyfuncitem.config
self.fspath = pyfuncitem.fspath self.fspath = pyfuncitem.fspath
if hasattr(pyfuncitem, '_requestparam'): if hasattr(pyfuncitem, '_requestparam'):
self.param = pyfuncitem._requestparam self.param = pyfuncitem._requestparam
self._plugins = self.config.pluginmanager.getplugins() self._plugins = self.config.pluginmanager.getplugins()
self._plugins.append(self.module) self._plugins.append(self.module)
if self.instance is not None:
self._plugins.append(self.instance)
self._provider = self.config.pluginmanager.listattr( self._provider = self.config.pluginmanager.listattr(
plugins=self._plugins, plugins=self._plugins,
attrname=self._argprefix + str(argname) attrname=self._argprefix + str(argname)
@ -126,7 +129,7 @@ class FuncargRequest:
def _raiselookupfailed(self): def _raiselookupfailed(self):
available = [] available = []
for plugin in self._plugins: for plugin in self._plugins:
for name in vars(plugin.__class__): for name in vars(plugin):
if name.startswith(self._argprefix): if name.startswith(self._argprefix):
name = name[len(self._argprefix):] name = name[len(self._argprefix):]
if name not in available: if name not in available:

View File

@ -2,9 +2,23 @@ import py
pytest_plugins = "pytester", "plugintester" pytest_plugins = "pytester", "plugintester"
class ConftestPlugin: def pytest_collect_file(path, parent):
def pytest_collect_file(self, path, parent):
if path.basename.startswith("pytest_") and path.ext == ".py": if path.basename.startswith("pytest_") and path.ext == ".py":
mod = parent.Module(path, parent=parent) mod = parent.Module(path, parent=parent)
return mod return mod
# decorate testdir to contain plugin under test
def pytest_funcarg__testdir(request):
testdir = request.call_next_provider()
#for obj in (request.cls, request.module):
# if hasattr(obj, 'testplugin'):
# testdir.plugins.append(obj.testplugin)
# break
#else:
basename = request.module.__name__.split(".")[-1]
if basename.startswith("pytest_"):
testdir.plugins.append(vars(request.module))
else:
pass # raise ValueError("need better support code")
return testdir

View File

@ -1,7 +1,6 @@
import py import py
class _pytestPlugin: def pytest_funcarg___pytest(request):
def pytest_funcarg___pytest(self, request):
return PytestArg(request) return PytestArg(request)
class PytestArg: class PytestArg:
@ -97,7 +96,7 @@ class CallRecorder:
return l return l
def test_generic(plugintester): def test_generic(plugintester):
plugintester.hookcheck(_pytestPlugin) plugintester.hookcheck()
def test_callrecorder_basic(): def test_callrecorder_basic():
comregistry = py._com.Registry() comregistry = py._com.Registry()

View File

@ -1,9 +1,8 @@
""" Plugin implementing defaults and general options. """
import py import py
class DefaultPlugin: def pytest_itemrun(item, pdb=None):
""" Plugin implementing defaults and general options. """
def pytest_itemrun(self, item, pdb=None):
from py.__.test.runner import basic_run_report, forked_run_report from py.__.test.runner import basic_run_report, forked_run_report
if item.config.option.boxed: if item.config.option.boxed:
runner = forked_run_report runner = forked_run_report
@ -13,19 +12,19 @@ class DefaultPlugin:
item.config.hook.pytest_itemtestreport(rep=report) item.config.hook.pytest_itemtestreport(rep=report)
return True return True
def pytest_item_makereport(self, item, excinfo, when, outerr): def pytest_item_makereport(item, excinfo, when, outerr):
from py.__.test import runner from py.__.test import runner
return runner.ItemTestReport(item, excinfo, when, outerr) return runner.ItemTestReport(item, excinfo, when, outerr)
def pytest_item_runtest_finished(self, item, excinfo, outerr): def pytest_item_runtest_finished(item, excinfo, outerr):
from py.__.test import runner from py.__.test import runner
rep = runner.ItemTestReport(item, excinfo, "execute", outerr) rep = runner.ItemTestReport(item, excinfo, "execute", outerr)
item.config.hook.pytest_itemtestreport(rep=rep) item.config.hook.pytest_itemtestreport(rep=rep)
def pytest_pyfunc_call(self, pyfuncitem, args, kwargs): def pytest_pyfunc_call(pyfuncitem, args, kwargs):
pyfuncitem.obj(*args, **kwargs) pyfuncitem.obj(*args, **kwargs)
def pytest_collect_file(self, path, parent): def pytest_collect_file(path, parent):
ext = path.ext ext = path.ext
pb = path.purebasename pb = path.purebasename
if pb.startswith("test_") or pb.endswith("_test") or \ if pb.startswith("test_") or pb.endswith("_test") or \
@ -33,7 +32,7 @@ class DefaultPlugin:
if ext == ".py": if ext == ".py":
return parent.Module(path, parent=parent) return parent.Module(path, parent=parent)
def pytest_collect_recurse(self, path, parent): def pytest_collect_recurse(path, parent):
#excludelist = parent._config.getvalue_pathlist('dir_exclude', path) #excludelist = parent._config.getvalue_pathlist('dir_exclude', path)
#if excludelist and path in excludelist: #if excludelist and path in excludelist:
# return # return
@ -46,7 +45,7 @@ class DefaultPlugin:
return False return False
return True return True
def pytest_collect_directory(self, path, parent): def pytest_collect_directory(path, parent):
# XXX reconsider the following comment # XXX reconsider the following comment
# not use parent.Directory here as we generally # not use parent.Directory here as we generally
# want dir/conftest.py to be able to # want dir/conftest.py to be able to
@ -54,10 +53,10 @@ class DefaultPlugin:
Directory = parent.config.getvalue('Directory', path) Directory = parent.config.getvalue('Directory', path)
return Directory(path, parent=parent) return Directory(path, parent=parent)
def pytest_report_iteminfo(self, item): def pytest_report_iteminfo(item):
return item.reportinfo() return item.reportinfo()
def pytest_addoption(self, parser): def pytest_addoption(parser):
group = parser.addgroup("general", "test collection and failure interaction options") group = parser.addgroup("general", "test collection and failure interaction options")
group._addoption('-v', '--verbose', action="count", group._addoption('-v', '--verbose', action="count",
dest="verbose", default=0, help="increase verbosity."), dest="verbose", default=0, help="increase verbosity."),
@ -140,12 +139,12 @@ class DefaultPlugin:
group.addoption('--rsyncdir', action="append", default=[], metavar="dir1", group.addoption('--rsyncdir', action="append", default=[], metavar="dir1",
help="add directory for rsyncing to remote tx nodes.") help="add directory for rsyncing to remote tx nodes.")
def pytest_configure(self, config): def pytest_configure(config):
self.fixoptions(config) fixoptions(config)
self.setsession(config) setsession(config)
self.loadplugins(config) loadplugins(config)
def fixoptions(self, config): def fixoptions(config):
if config.option.numprocesses: if config.option.numprocesses:
config.option.dist = "load" config.option.dist = "load"
config.option.tx = ['popen'] * int(config.option.numprocesses) config.option.tx = ['popen'] * int(config.option.numprocesses)
@ -157,12 +156,12 @@ class DefaultPlugin:
if config.option.dist != "no": if config.option.dist != "no":
raise config.Error("--pdb incomptaible with distributing tests.") raise config.Error("--pdb incomptaible with distributing tests.")
def loadplugins(self, config): def loadplugins(config):
for name in config.getvalue("plugin"): for name in config.getvalue("plugin"):
print "importing", name print "importing", name
config.pluginmanager.import_plugin(name) config.pluginmanager.import_plugin(name)
def setsession(self, config): def setsession(config):
val = config.getvalue val = config.getvalue
if val("collectonly"): if val("collectonly"):
from py.__.test.session import Session from py.__.test.session import Session
@ -190,7 +189,7 @@ def test_implied_different_sessions(tmpdir):
assert x('-f') == 'LooponfailingSession' assert x('-f') == 'LooponfailingSession'
def test_generic(plugintester): def test_generic(plugintester):
plugintester.hookcheck(DefaultPlugin) plugintester.hookcheck()
def test_plugin_specify(testdir): def test_plugin_specify(testdir):
testdir.chdir() testdir.chdir()
@ -253,14 +252,10 @@ def test_dist_options(testdir):
assert config.option.dist == "load" assert config.option.dist == "load"
def test_pytest_report_iteminfo(): def test_pytest_report_iteminfo():
plugin = DefaultPlugin()
class FakeItem(object): class FakeItem(object):
def reportinfo(self): def reportinfo(self):
return "-reportinfo-" return "-reportinfo-"
res = plugin.pytest_report_iteminfo(FakeItem()) res = pytest_report_iteminfo(FakeItem())
assert res == "-reportinfo-" assert res == "-reportinfo-"

View File

@ -1,13 +1,12 @@
import py import py
class DoctestPlugin: def pytest_addoption(parser):
def pytest_addoption(self, parser):
group = parser.addgroup("doctest options") group = parser.addgroup("doctest options")
group.addoption("--doctest-modules", group.addoption("--doctest-modules",
action="store_true", default=False, action="store_true", default=False,
dest="doctestmodules") dest="doctestmodules")
def pytest_collect_file(self, path, parent): def pytest_collect_file(path, parent):
if path.ext == ".py": if path.ext == ".py":
if parent.config.getvalue("doctestmodules"): if parent.config.getvalue("doctestmodules"):
return DoctestModule(path, parent) return DoctestModule(path, parent)
@ -76,8 +75,8 @@ class DoctestModule(DoctestItem):
# #
class TestDoctests: class TestDoctests:
def test_collect_testtextfile(self, testdir): def test_collect_testtextfile(self, testdir):
testdir.plugins.append(DoctestPlugin())
testdir.maketxtfile(whatever="") testdir.maketxtfile(whatever="")
checkfile = testdir.maketxtfile(test_something=""" checkfile = testdir.maketxtfile(test_something="""
alskdjalsdk alskdjalsdk
@ -92,7 +91,6 @@ class TestDoctests:
assert isinstance(items[0], DoctestTextfile) assert isinstance(items[0], DoctestTextfile)
def test_collect_module(self, testdir): def test_collect_module(self, testdir):
testdir.plugins.append(DoctestPlugin())
path = testdir.makepyfile(whatever="#") path = testdir.makepyfile(whatever="#")
for p in (path, testdir.tmpdir): for p in (path, testdir.tmpdir):
items, evrec = testdir.inline_genitems(p, '--doctest-modules') items, evrec = testdir.inline_genitems(p, '--doctest-modules')
@ -100,7 +98,6 @@ class TestDoctests:
assert isinstance(items[0], DoctestModule) assert isinstance(items[0], DoctestModule)
def test_simple_doctestfile(self, testdir): def test_simple_doctestfile(self, testdir):
testdir.plugins.append(DoctestPlugin())
p = testdir.maketxtfile(test_doc=""" p = testdir.maketxtfile(test_doc="""
>>> x = 1 >>> x = 1
>>> x == 1 >>> x == 1
@ -112,7 +109,6 @@ class TestDoctests:
def test_doctest_unexpected_exception(self, testdir): def test_doctest_unexpected_exception(self, testdir):
from py.__.test.outcome import Failed from py.__.test.outcome import Failed
testdir.plugins.append(DoctestPlugin())
p = testdir.maketxtfile(""" p = testdir.maketxtfile("""
>>> i = 0 >>> i = 0
>>> i = 1 >>> i = 1
@ -130,7 +126,6 @@ class TestDoctests:
#assert repr.reprlocation #assert repr.reprlocation
def test_doctestmodule(self, testdir): def test_doctestmodule(self, testdir):
testdir.plugins.append(DoctestPlugin())
p = testdir.makepyfile(""" p = testdir.makepyfile("""
''' '''
>>> x = 1 >>> x = 1
@ -143,7 +138,7 @@ class TestDoctests:
sorter.assertoutcome(failed=1) sorter.assertoutcome(failed=1)
def test_txtfile_failing(self, testdir): def test_txtfile_failing(self, testdir):
testdir.plugins.append('pytest_doctest') testdir.plugins.append("doctest")
p = testdir.maketxtfile(""" p = testdir.maketxtfile("""
>>> i = 0 >>> i = 0
>>> i + 1 >>> i + 1
@ -162,5 +157,5 @@ class TestDoctests:
def test_generic(plugintester): def test_generic(plugintester):
plugintester.hookcheck(DoctestPlugin) plugintester.hookcheck()

View File

@ -1,5 +1,8 @@
import py import py
""" expected to fail.
"""
class EventlogPlugin: class EventlogPlugin:
""" log pytest events to a file. """ """ log pytest events to a file. """
def pytest_addoption(self, parser): def pytest_addoption(self, parser):
@ -28,7 +31,7 @@ class EventlogPlugin:
@py.test.mark.xfail @py.test.mark.xfail
def test_generic(plugintester): def test_generic(plugintester):
plugintester.hookcheck(EventlogPlugin) plugintester.hookcheck()
testdir = plugintester.testdir() testdir = plugintester.testdir()
testdir.makepyfile(""" testdir.makepyfile("""

View File

@ -1,11 +1,13 @@
import py import py
class ExecnetcleanupPlugin: def pytest_configure(config):
_gateways = None debug = config.option.debug
_debug = None config.pluginmanager.register(Execnetcleanup(debug))
def pytest_configure(self, config): class Execnetcleanup:
self._debug = config.option.debug _gateways = None
def __init__(self, debug=False):
self._debug = debug
def trace(self, msg, *args): def trace(self, msg, *args):
if self._debug: if self._debug:
@ -43,7 +45,8 @@ class ExecnetcleanupPlugin:
return res return res
def test_generic(plugintester): def test_generic(plugintester):
plugintester.hookcheck(ExecnetcleanupPlugin) plugintester.hookcheck(cls=Execnetcleanup)
plugintester.hookcheck()
@py.test.mark.xfail("clarify plugin registration/unregistration") @py.test.mark.xfail("clarify plugin registration/unregistration")
def test_execnetplugin(testdir): def test_execnetplugin(testdir):

View File

@ -1,7 +1,9 @@
import py import py
class FigleafPlugin: figleaf = py.test.importorskip("figleaf")
def pytest_addoption(self, parser): import figleaf.annotate_html
def pytest_addoption(parser):
group = parser.addgroup('figleaf options') group = parser.addgroup('figleaf options')
group.addoption('-F', action='store_true', default=False, group.addoption('-F', action='store_true', default=False,
dest = 'figleaf', dest = 'figleaf',
@ -14,40 +16,31 @@ class FigleafPlugin:
dest='figleafhtml', dest='figleafhtml',
help='path to the coverage html dir.') help='path to the coverage html dir.')
def pytest_configure(self, config): def pytest_configure(config):
if config.getvalue('figleaf'): figleaf.start()
try:
import figleaf
import figleaf.annotate_html
except ImportError:
raise config.Error('Could not import figleaf module')
self.figleaf = figleaf
self.figleaf.start()
def pytest_terminal_summary(self, terminalreporter): def pytest_terminal_summary(terminalreporter):
if hasattr(self, 'figleaf'):
config = terminalreporter.config config = terminalreporter.config
datafile = py.path.local(config.getvalue('figleafdata')) datafile = py.path.local(config.getvalue('figleafdata'))
tw = terminalreporter._tw tw = terminalreporter._tw
tw.sep('-', 'figleaf') tw.sep('-', 'figleaf')
tw.line('Writing figleaf data to %s' % (datafile)) tw.line('Writing figleaf data to %s' % (datafile))
self.figleaf.stop() figleaf.stop()
self.figleaf.write_coverage(str(datafile)) figleaf.write_coverage(str(datafile))
coverage = self.get_coverage(datafile, config) coverage = get_coverage(datafile, config)
reportdir = py.path.local(config.getvalue('figleafhtml')) reportdir = py.path.local(config.getvalue('figleafhtml'))
tw.line('Writing figleaf html to file://%s' % (reportdir)) tw.line('Writing figleaf html to file://%s' % (reportdir))
self.figleaf.annotate_html.prepare_reportdir(str(reportdir)) figleaf.annotate_html.prepare_reportdir(str(reportdir))
exclude = [] exclude = []
self.figleaf.annotate_html.report_as_html(coverage, figleaf.annotate_html.report_as_html(coverage,
str(reportdir), exclude, {}) str(reportdir), exclude, {})
def get_coverage(self, datafile, config): def get_coverage(datafile, config):
# basepath = config.topdir # basepath = config.topdir
basepath = py.path.local() basepath = py.path.local()
data = self.figleaf.read_coverage(str(datafile)) data = figleaf.read_coverage(str(datafile))
d = {} d = {}
coverage = self.figleaf.combine_coverage(d, data) coverage = figleaf.combine_coverage(d, data)
for path in coverage.keys(): for path in coverage.keys():
if not py.path.local(path).relto(basepath): if not py.path.local(path).relto(basepath):
del coverage[path] del coverage[path]
@ -55,11 +48,11 @@ class FigleafPlugin:
def test_generic(plugintester): def test_generic(plugintester):
plugintester.hookcheck(FigleafPlugin) plugintester.hookcheck()
def test_functional(testdir): def test_functional(testdir):
py.test.importorskip("figleaf") py.test.importorskip("figleaf")
testdir.plugins.append('figleaf') testdir.plugins.append("figleaf")
testdir.makepyfile(""" testdir.makepyfile("""
def f(): def f():
x = 42 x = 42

View File

@ -1,13 +1,13 @@
import py import py
class IocapturePlugin: def pytest_funcarg__stdcapture(request):
""" capture sys.stdout/sys.stderr / fd1/fd2. """ """ capture writes to sys.stdout/sys.stderr. """
def pytest_funcarg__stdcapture(self, request):
capture = Capture(py.io.StdCapture) capture = Capture(py.io.StdCapture)
request.addfinalizer(capture.finalize) request.addfinalizer(capture.finalize)
return capture return capture
def pytest_funcarg__stdcapturefd(self, request): def pytest_funcarg__stdcapturefd(request):
""" capture writes to filedescriptors 1 and 2"""
capture = Capture(py.io.StdCaptureFD) capture = Capture(py.io.StdCaptureFD)
request.addfinalizer(capture.finalize) request.addfinalizer(capture.finalize)
return capture return capture
@ -26,11 +26,10 @@ class Capture:
return res return res
def test_generic(plugintester): def test_generic(plugintester):
plugintester.hookcheck(IocapturePlugin) plugintester.hookcheck()
class TestCapture: class TestCapture:
def test_std_functional(self, testdir): def test_std_functional(self, testdir):
testdir.plugins.append(IocapturePlugin())
evrec = testdir.inline_runsource(""" evrec = testdir.inline_runsource("""
def test_hello(stdcapture): def test_hello(stdcapture):
print 42 print 42
@ -40,7 +39,6 @@ class TestCapture:
evrec.assertoutcome(passed=1) evrec.assertoutcome(passed=1)
def test_stdfd_functional(self, testdir): def test_stdfd_functional(self, testdir):
testdir.plugins.append(IocapturePlugin())
evrec = testdir.inline_runsource(""" evrec = testdir.inline_runsource("""
def test_hello(stdcapturefd): def test_hello(stdcapturefd):
import os import os

View File

@ -1,8 +1,6 @@
import os import os
class MonkeypatchPlugin: def pytest_funcarg__monkeypatch(request):
""" setattr-monkeypatching with automatical reversal after test. """
def pytest_funcarg__monkeypatch(self, request):
monkeypatch = MonkeyPatch() monkeypatch = MonkeyPatch()
request.addfinalizer(monkeypatch.finalize) request.addfinalizer(monkeypatch.finalize)
return monkeypatch return monkeypatch

View File

@ -1,7 +1,6 @@
from py.__.test.custompdb import post_mortem from py.__.test.custompdb import post_mortem
class PdbPlugin: def pytest_item_runtest_finished(item, excinfo, outerr):
def pytest_item_runtest_finished(self, item, excinfo, outerr):
if excinfo and item.config.option.usepdb: if excinfo and item.config.option.usepdb:
tw = py.io.TerminalWriter() tw = py.io.TerminalWriter()
repr = excinfo.getrepr() repr = excinfo.getrepr()

View File

@ -4,32 +4,39 @@ plugin with support classes and functions for testing pytest functionality
import py import py
from py.__.test.plugin import api from py.__.test.plugin import api
class PlugintesterPlugin: def pytest_funcarg__plugintester(request):
""" test support code for testing pytest plugins. """
def pytest_funcarg__plugintester(self, request):
return PluginTester(request) return PluginTester(request)
class PluginTester: class PluginTester:
def __init__(self, request): def __init__(self, request):
self.request = request self.request = request
def testdir(self): def testdir(self, globs=None):
from pytest_pytester import TmpTestdir from pytest_pytester import TmpTestdir
crunner = TmpTestdir(self.request) testdir = TmpTestdir(self.request)
self.request.addfinalizer(crunner.finalize) 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(): #for colitem in self.request.listchain():
# if isinstance(colitem, py.test.collect.Module) and \ # if isinstance(colitem, py.test.collect.Module) and \
# colitem.name.startswith("pytest_"): # colitem.name.startswith("pytest_"):
# crunner.plugins.append(colitem.fspath.purebasename) # crunner.plugins.append(colitem.fspath.purebasename)
# break # break
return crunner return testdir
def hookcheck(self, pluginclass): def hookcheck(self, name=None, cls=None):
print "loading and checking", pluginclass 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 fail = False
pm = py.test._PluginManager() pm = py.test._PluginManager()
methods = collectattr(pluginclass) methods = collectattr(plugin)
hooks = collectattr(api.PluginHooks) hooks = collectattr(api.PluginHooks)
getargs = py.std.inspect.getargs getargs = py.std.inspect.getargs
@ -84,4 +91,4 @@ def formatdef(func):
# =============================================================================== # ===============================================================================
def test_generic(plugintester): def test_generic(plugintester):
plugintester.hookcheck(PlugintesterPlugin) plugintester.hookcheck()

View File

@ -8,19 +8,16 @@ class url:
xmlrpc = base + "/xmlrpc/" xmlrpc = base + "/xmlrpc/"
show = base + "/show/" show = base + "/show/"
class PocooPlugin: def pytest_addoption(parser):
""" report URLs from sending test failures to the pocoo paste service. """
def pytest_addoption(self, parser):
group = parser.addgroup("pocoo plugin") group = parser.addgroup("pocoo plugin")
group.addoption('-P', '--pocoo-sendfailures', group.addoption('-P', '--pocoo-sendfailures',
action='store_true', dest="pocoo_sendfailures", action='store_true', dest="pocoo_sendfailures",
help="send failures to %s paste service" %(url.base,)) help="send failures to %s paste service" %(url.base,))
def getproxy(self): def getproxy():
return py.std.xmlrpclib.ServerProxy(url.xmlrpc).pastes return py.std.xmlrpclib.ServerProxy(url.xmlrpc).pastes
def pytest_terminal_summary(self, terminalreporter): def pytest_terminal_summary(terminalreporter):
if terminalreporter.config.option.pocoo_sendfailures: if terminalreporter.config.option.pocoo_sendfailures:
tr = terminalreporter tr = terminalreporter
if 'failed' in tr.stats and tr.config.option.tbstyle != "no": if 'failed' in tr.stats and tr.config.option.tbstyle != "no":
@ -28,7 +25,7 @@ class PocooPlugin:
terminalreporter.write_line("xmlrpcurl: %s" %(url.xmlrpc,)) terminalreporter.write_line("xmlrpcurl: %s" %(url.xmlrpc,))
#print self.__class__.getproxy #print self.__class__.getproxy
#print self.__class__, id(self.__class__) #print self.__class__, id(self.__class__)
serverproxy = self.getproxy() serverproxy = getproxy()
for ev in terminalreporter.stats.get('failed'): for ev in terminalreporter.stats.get('failed'):
tw = py.io.TerminalWriter(stringio=True) tw = py.io.TerminalWriter(stringio=True)
ev.toterminal(tw) ev.toterminal(tw)
@ -42,16 +39,15 @@ class PocooPlugin:
def test_apicheck(plugintester): def test_apicheck(plugintester):
plugintester.hookcheck(PocooPlugin) plugintester.hookcheck()
def test_toproxy(testdir, monkeypatch): def test_toproxy(testdir, monkeypatch):
l = [] l = []
class MockProxy: class MockProxy:
def newPaste(self, language, code): def newPaste(self, language, code):
l.append((language, code)) l.append((language, code))
monkeypatch.setattr(PocooPlugin, 'getproxy', MockProxy) monkeypatch.setitem(globals(), 'getproxy', MockProxy)
testdir.plugins.insert(0, PocooPlugin()) testdir.plugins.insert(0, globals())
testdir.chdir()
testpath = testdir.makepyfile(""" testpath = testdir.makepyfile("""
import py import py
def test_pass(): def test_pass():

View File

@ -5,33 +5,22 @@ XXX: Currently in progress, NOT IN WORKING STATE.
""" """
import py import py
class PylintPlugin: lint = py.test.importorskip("pylint")
def pytest_addoption(self, parser):
def pytest_addoption(parser):
group = parser.addgroup('pylint options') group = parser.addgroup('pylint options')
group.addoption('--pylint', action='store_true', group.addoption('--pylint', action='store_true',
default=False, dest='pylint', default=False, dest='pylint',
help='Pylint coverate of test files.') help='Pylint coverate of test files.')
def pytest_configure(self, config): def pytest_collect_file(path, parent):
if config.getvalue('pylint'):
try:
from pylint import lint
self.lint = lint
except ImportError:
raise config.Error('Could not import pylint module')
print "trying to configure pytest"
def pytest_collect_file(self, path, parent):
if path.ext == ".py": if path.ext == ".py":
if parent.config.getvalue('pylint'): if parent.config.getvalue('pylint'):
return PylintItem(path, parent, self.lint) return PylintItem(path, parent, self.lint)
def pytest_terminal_summary(self, terminalreporter): def pytest_terminal_summary(terminalreporter):
if hasattr(self, 'lint'):
print 'placeholder for pylint output' print 'placeholder for pylint output'
class PylintItem(py.test.collect.Item): class PylintItem(py.test.collect.Item):
def __init__(self, path, parent, lintlib): def __init__(self, path, parent, lintlib):
name = self.__class__.__name__ + ":" + path.basename name = self.__class__.__name__ + ":" + path.basename

View File

@ -10,25 +10,23 @@ from py.__.test.config import Config as pytestConfig
from pytest__pytest import CallRecorder from pytest__pytest import CallRecorder
import api import api
def pytest_funcarg__linecomp(request):
class PytesterPlugin:
def pytest_funcarg__linecomp(self, request):
return LineComp() return LineComp()
def pytest_funcarg__LineMatcher(self, request): def pytest_funcarg__LineMatcher(request):
return LineMatcher return LineMatcher
def pytest_funcarg__testdir(self, request): def pytest_funcarg__testdir(request):
tmptestdir = TmpTestdir(request) tmptestdir = TmpTestdir(request)
return tmptestdir return tmptestdir
def pytest_funcarg__eventrecorder(self, request): def pytest_funcarg__eventrecorder(request):
evrec = EventRecorder(py._com.comregistry) evrec = EventRecorder(py._com.comregistry)
request.addfinalizer(lambda: evrec.comregistry.unregister(evrec)) request.addfinalizer(lambda: evrec.comregistry.unregister(evrec))
return evrec return evrec
def test_generic(plugintester): def test_generic(plugintester):
plugintester.hookcheck(PytesterPlugin) plugintester.hookcheck()
class RunResult: class RunResult:
def __init__(self, ret, outlines, errlines): def __init__(self, ret, outlines, errlines):
@ -163,7 +161,9 @@ class TmpTestdir:
for plugin in self.plugins: for plugin in self.plugins:
if isinstance(plugin, str): if isinstance(plugin, str):
config.pluginmanager.import_plugin(plugin) config.pluginmanager.import_plugin(plugin)
else: elif plugin:
if isinstance(plugin, dict):
plugin = PseudoPlugin(plugin)
config.pluginmanager.register(plugin) config.pluginmanager.register(plugin)
return config return config
@ -280,6 +280,11 @@ class TmpTestdir:
child.timeout = expect_timeout child.timeout = expect_timeout
return child return child
class PseudoPlugin:
def __init__(self, vars):
self.__dict__.update(vars)
class Event: class Event:
def __init__(self, name, args, kwargs): def __init__(self, name, args, kwargs):
self.name = name self.name = name

View File

@ -7,8 +7,7 @@ to a user. See the test at the bottom for an example.
import py import py
import os import os
class RecwarnPlugin: def pytest_funcarg__recwarn(request):
def pytest_funcarg__recwarn(self, request):
""" check that warnings have been raised. """ """ check that warnings have been raised. """
warnings = WarningsRecorder() warnings = WarningsRecorder()
request.addfinalizer(warnings.finalize) request.addfinalizer(warnings.finalize)

View File

@ -1,7 +1,6 @@
import py import py
class RestdocPlugin: def pytest_addoption(parser):
def pytest_addoption(self, parser):
group = parser.addgroup("ReST", "ReST documentation check options") group = parser.addgroup("ReST", "ReST documentation check options")
group.addoption('-R', '--urlcheck', group.addoption('-R', '--urlcheck',
action="store_true", dest="urlcheck", default=False, action="store_true", dest="urlcheck", default=False,
@ -13,7 +12,7 @@ class RestdocPlugin:
action="store_true", dest="forcegen", default=False, action="store_true", dest="forcegen", default=False,
help="force generation of html files.") help="force generation of html files.")
def pytest_collect_file(self, path, parent): def pytest_collect_file(path, parent):
if path.ext == ".txt": if path.ext == ".txt":
project = getproject(path) project = getproject(path)
if project is not None: if project is not None:
@ -346,7 +345,7 @@ def localrefcheck(tryfn, path, lineno):
# PLUGIN tests # PLUGIN tests
# #
def test_generic(plugintester): def test_generic(plugintester):
plugintester.hookcheck(RestdocPlugin) plugintester.hookcheck()
def test_deindent(): def test_deindent():
assert deindent('foo') == 'foo' assert deindent('foo') == 'foo'
@ -388,18 +387,18 @@ class TestApigenLinkRole:
"resolve_linkrole('source', 'py/foo/bar.py')") "resolve_linkrole('source', 'py/foo/bar.py')")
def pytest_funcarg__testdir(request): class TestDoctest:
def pytest_funcarg__testdir(self, request):
testdir = request.call_next_provider() testdir = request.call_next_provider()
assert request.module.__name__ == __name__
testdir.makepyfile(confrest="from py.__.misc.rest import Project") testdir.makepyfile(confrest="from py.__.misc.rest import Project")
testdir.plugins.append(RestdocPlugin())
count = 0
for p in testdir.plugins: for p in testdir.plugins:
if isinstance(p, RestdocPlugin): if p == globals():
count += 1 break
assert count < 2 else:
testdir.plugins.append(globals())
return testdir return testdir
class TestDoctest:
def test_doctest_extra_exec(self, testdir): def test_doctest_extra_exec(self, testdir):
xtxt = testdir.maketxtfile(x=""" xtxt = testdir.maketxtfile(x="""
hello:: hello::

View File

@ -365,7 +365,7 @@ class TestWithFunctionIntegration:
assert 'ValueError' in entry assert 'ValueError' in entry
def test_generic(plugintester): def test_generic(plugintester):
plugintester.hookcheck(ResultdbPlugin) plugintester.hookcheck()
testdir = plugintester.testdir() testdir = plugintester.testdir()
testdir.makepyfile(""" testdir.makepyfile("""
import py import py

View File

@ -1,26 +1,27 @@
"""resultlog plugin for machine-readable logging of test results.
Useful for buildbot integration code.
"""
import py import py
class ResultlogPlugin: def pytest_addoption(parser):
"""resultlog plugin for machine-readable logging of test results.
Useful for buildbot integration code.
"""
def pytest_addoption(self, parser):
group = parser.addgroup("resultlog", "resultlog plugin options") group = parser.addgroup("resultlog", "resultlog plugin options")
group.addoption('--resultlog', action="store", dest="resultlog", metavar="path", group.addoption('--resultlog', action="store", dest="resultlog", metavar="path",
help="path for machine-readable result log.") help="path for machine-readable result log.")
def pytest_configure(self, config): def pytest_configure(config):
resultlog = config.option.resultlog resultlog = config.option.resultlog
if resultlog: if resultlog:
logfile = open(resultlog, 'w', 1) # line buffered logfile = open(resultlog, 'w', 1) # line buffered
self.resultlog = ResultLog(logfile) config._resultlog = ResultLog(logfile)
config.pluginmanager.register(self.resultlog) config.pluginmanager.register(config._resultlog)
def pytest_unconfigure(self, config): def pytest_unconfigure(config):
if hasattr(self, 'resultlog'): resultlog = getattr(config, '_resultlog', None)
self.resultlog.logfile.close() if resultlog:
del self.resultlog resultlog.logfile.close()
#config.pluginmanager.unregister(self.resultlog) del config.resultlog
config.pluginmanager.unregister(resultlog)
def generic_path(item): def generic_path(item):
chain = item.listchain() chain = item.listchain()
@ -224,7 +225,7 @@ class TestWithFunctionIntegration:
assert 'ValueError' in entry assert 'ValueError' in entry
def test_generic(plugintester, LineMatcher): def test_generic(plugintester, LineMatcher):
plugintester.hookcheck(ResultlogPlugin) plugintester.hookcheck()
testdir = plugintester.testdir() testdir = plugintester.testdir()
testdir.plugins.append("resultlog") testdir.plugins.append("resultlog")
testdir.makepyfile(""" testdir.makepyfile("""

View File

@ -146,7 +146,7 @@ class SetupState(object):
# =============================================================================== # ===============================================================================
def test_generic(plugintester): def test_generic(plugintester):
plugintester.hookcheck(RunnerPlugin()) plugintester.hookcheck()
class TestSetupState: class TestSetupState:
def test_setup_prepare(self, testdir): def test_setup_prepare(self, testdir):

View File

@ -1,21 +1,19 @@
import py import py
import sys import sys
class TerminalPlugin(object): def pytest_configure(config):
""" Report a test run to a terminal. """
def pytest_configure(self, config):
if config.option.collectonly: if config.option.collectonly:
self.reporter = CollectonlyReporter(config) reporter = CollectonlyReporter(config)
else: else:
self.reporter = TerminalReporter(config) reporter = TerminalReporter(config)
# XXX see remote.py's XXX # XXX see remote.py's XXX
for attr in 'pytest_terminal_hasmarkup', 'pytest_terminal_fullwidth': for attr in 'pytest_terminal_hasmarkup', 'pytest_terminal_fullwidth':
if hasattr(config, attr): if hasattr(config, attr):
#print "SETTING TERMINAL OPTIONS", attr, getattr(config, attr) #print "SETTING TERMINAL OPTIONS", attr, getattr(config, attr)
name = attr.split("_")[-1] name = attr.split("_")[-1]
assert hasattr(self.reporter._tw, name), name assert hasattr(self.reporter._tw, name), name
setattr(self.reporter._tw, name, getattr(config, attr)) setattr(reporter._tw, name, getattr(config, attr))
config.pluginmanager.register(self.reporter) config.pluginmanager.register(reporter)
class TerminalReporter: class TerminalReporter:
def __init__(self, config, file=None): def __init__(self, config, file=None):
@ -143,7 +141,6 @@ class TerminalReporter:
def pytest_deselected(self, items): def pytest_deselected(self, items):
self.stats.setdefault('deselected', []).append(items) self.stats.setdefault('deselected', []).append(items)
def pytest_itemstart(self, item, node=None): def pytest_itemstart(self, item, node=None):
if self.config.option.dist != "no": if self.config.option.dist != "no":
# for dist-testing situations itemstart means we # for dist-testing situations itemstart means we
@ -749,6 +746,6 @@ def test_repr_python_version(monkeypatch):
assert repr_pythonversion() == str(x) assert repr_pythonversion() == str(x)
def test_generic(plugintester): def test_generic(plugintester):
plugintester.hookcheck(TerminalPlugin) plugintester.hookcheck()
plugintester.hookcheck(TerminalReporter) plugintester.hookcheck(cls=TerminalReporter)
plugintester.hookcheck(CollectonlyReporter) plugintester.hookcheck(cls=CollectonlyReporter)

View File

@ -1,4 +1,6 @@
""" """
provide temporary directories to test functions and methods.
example: example:
pytest_plugins = "pytest_tmpdir" pytest_plugins = "pytest_tmpdir"
@ -9,11 +11,7 @@ example:
""" """
import py import py
class TmpdirPlugin: def pytest_funcarg__tmpdir(request):
""" provide temporary directories to test functions and methods.
"""
def pytest_funcarg__tmpdir(self, request):
name = request.function.__name__ name = request.function.__name__
return request.config.mktemp(name, numbered=True) return request.config.mktemp(name, numbered=True)
@ -24,13 +22,12 @@ class TmpdirPlugin:
# =============================================================================== # ===============================================================================
# #
def test_generic(plugintester): def test_generic(plugintester):
plugintester.hookcheck(TmpdirPlugin) plugintester.hookcheck()
def test_funcarg(testdir): def test_funcarg(testdir):
from py.__.test.funcargs import FuncargRequest from py.__.test.funcargs import FuncargRequest
item = testdir.getitem("def test_func(tmpdir): pass") item = testdir.getitem("def test_func(tmpdir): pass")
plugin = TmpdirPlugin() p = pytest_funcarg__tmpdir(FuncargRequest(item, "tmpdir"))
p = plugin.pytest_funcarg__tmpdir(FuncargRequest(item, "tmpdir"))
assert p.check() assert p.check()
bn = p.basename.strip("0123456789-") bn = p.basename.strip("0123456789-")
assert bn.endswith("test_func") assert bn.endswith("test_func")

View File

@ -1,5 +1,5 @@
""" """
automatically collect and run traditional "unittest.py" style tests. automatically discover and run traditional "unittest.py" style tests.
you can mix unittest TestCase subclasses and you can mix unittest TestCase subclasses and
py.test style tests in one test module. py.test style tests in one test module.
@ -15,10 +15,7 @@ $Id: conftest.py 60979 2009-01-14 22:29:32Z hpk $
""" """
import py import py
class UnittestPlugin: def pytest_pycollect_obj(collector, name, obj):
""" discover and integrate traditional ``unittest.py`` tests.
"""
def pytest_pycollect_obj(self, collector, name, obj):
if py.std.inspect.isclass(obj) and issubclass(obj, py.std.unittest.TestCase): if py.std.inspect.isclass(obj) and issubclass(obj, py.std.unittest.TestCase):
return UnitTestCase(name, parent=collector) return UnitTestCase(name, parent=collector)
@ -71,7 +68,7 @@ class UnitTestFunction(py.test.collect.Function):
def test_generic(plugintester): def test_generic(plugintester):
plugintester.hookcheck(UnittestPlugin) plugintester.hookcheck()
def test_simple_unittest(testdir): def test_simple_unittest(testdir):
testpath = testdir.makepyfile(""" testpath = testdir.makepyfile("""

View File

@ -1,4 +1,6 @@
""" """
mark and report specially about "expected to fail" tests.
for marking and reporting "expected to fail" tests. for marking and reporting "expected to fail" tests.
@py.test.mark.xfail("needs refactoring") @py.test.mark.xfail("needs refactoring")
def test_hello(): def test_hello():
@ -7,10 +9,7 @@ for marking and reporting "expected to fail" tests.
""" """
import py import py
class XfailPlugin(object): def pytest_item_makereport(__call__, item, excinfo, when, outerr):
""" mark and report specially about "expected to fail" tests. """
def pytest_item_makereport(self, __call__, item, excinfo, when, outerr):
if hasattr(item, 'obj') and hasattr(item.obj, 'func_dict'): if hasattr(item, 'obj') and hasattr(item.obj, 'func_dict'):
if 'xfail' in item.obj.func_dict: if 'xfail' in item.obj.func_dict:
res = __call__.execute(firstresult=True) res = __call__.execute(firstresult=True)
@ -22,7 +21,7 @@ class XfailPlugin(object):
res.failed = True res.failed = True
return res return res
def pytest_report_teststatus(self, rep): def pytest_report_teststatus(rep):
""" return shortletter and verbose word. """ """ return shortletter and verbose word. """
if 'xfail' in rep.keywords: if 'xfail' in rep.keywords:
if rep.skipped: if rep.skipped:
@ -30,8 +29,8 @@ class XfailPlugin(object):
elif rep.failed: elif rep.failed:
return "xpassed", "P", "xpass" return "xpassed", "P", "xpass"
# a hook implemented called by the terminalreporter instance/plugin # a hook implemented called by the terminalreporter instance/plugin
def pytest_terminal_summary(self, terminalreporter): def pytest_terminal_summary(terminalreporter):
tr = terminalreporter tr = terminalreporter
xfailed = tr.stats.get("xfailed") xfailed = tr.stats.get("xfailed")
if xfailed: if xfailed:
@ -57,7 +56,7 @@ class XfailPlugin(object):
# =============================================================================== # ===============================================================================
def test_generic(plugintester): def test_generic(plugintester):
plugintester.hookcheck(XfailPlugin) plugintester.hookcheck()
def test_xfail(plugintester, linecomp): def test_xfail(plugintester, linecomp):
testdir = plugintester.testdir() testdir = plugintester.testdir()

View File

@ -4,6 +4,10 @@ managing loading and interacting with pytest plugins.
import py import py
from py.__.test.plugin import api from py.__.test.plugin import api
def check_old_use(mod, modname):
clsname = modname[len('pytest_'):].capitalize() + "Plugin"
assert not hasattr(mod, clsname), (mod, clsname)
class PluginManager(object): class PluginManager(object):
def __init__(self, comregistry=None): def __init__(self, comregistry=None):
if comregistry is None: if comregistry is None:
@ -16,14 +20,21 @@ class PluginManager(object):
hookspecs=api.PluginHooks, hookspecs=api.PluginHooks,
registry=self.comregistry) registry=self.comregistry)
def register(self, plugin): def register(self, plugin, name=None):
if name is None:
name = getattr(plugin, '__name__', id(plugin))
if name not in self.impname2plugin:
self.impname2plugin[name] = plugin
self.hook.pytest_plugin_registered(plugin=plugin) self.hook.pytest_plugin_registered(plugin=plugin)
import types
self.comregistry.register(plugin) self.comregistry.register(plugin)
return True
def unregister(self, plugin): def unregister(self, plugin):
self.hook.pytest_plugin_unregistered(plugin=plugin) self.hook.pytest_plugin_unregistered(plugin=plugin)
self.comregistry.unregister(plugin) self.comregistry.unregister(plugin)
for name, value in self.impname2plugin.items():
if value == plugin:
del self.impname2plugin[name]
def isregistered(self, plugin): def isregistered(self, plugin):
return self.comregistry.isregistered(plugin) return self.comregistry.isregistered(plugin)
@ -34,7 +45,7 @@ class PluginManager(object):
# API for bootstrapping # API for bootstrapping
# #
def getplugin(self, importname): def getplugin(self, importname):
impname, clsname = canonical_names(importname) impname = canonical_importname(importname)
return self.impname2plugin[impname] return self.impname2plugin[impname]
def _envlist(self, varname): def _envlist(self, varname):
@ -54,9 +65,10 @@ class PluginManager(object):
def consider_conftest(self, conftestmodule): def consider_conftest(self, conftestmodule):
cls = getattr(conftestmodule, 'ConftestPlugin', None) cls = getattr(conftestmodule, 'ConftestPlugin', None)
if cls is not None and cls not in self.impname2plugin: if cls is not None:
self.impname2plugin[cls] = True raise ValueError("%r: 'ConftestPlugins' only existed till 1.0.0b1, "
self.register(cls()) "were removed in 1.0.0b2" % (cls,))
if self.register(conftestmodule, name=conftestmodule.__file__):
self.consider_module(conftestmodule) self.consider_module(conftestmodule)
def consider_module(self, mod): def consider_module(self, mod):
@ -69,12 +81,12 @@ class PluginManager(object):
def import_plugin(self, spec): def import_plugin(self, spec):
assert isinstance(spec, str) assert isinstance(spec, str)
modname, clsname = canonical_names(spec) modname = canonical_importname(spec)
if modname in self.impname2plugin: if modname in self.impname2plugin:
return return
mod = importplugin(modname) mod = importplugin(modname)
plugin = registerplugin(self.register, mod, clsname) check_old_use(mod, modname)
self.impname2plugin[modname] = plugin self.register(mod)
self.consider_module(mod) self.consider_module(mod)
# #
# #
@ -131,19 +143,12 @@ class PluginManager(object):
# #
# XXX old code to automatically load classes # XXX old code to automatically load classes
# #
def canonical_names(importspec): def canonical_importname(name):
importspec = importspec.lower() name = name.lower()
modprefix = "pytest_" modprefix = "pytest_"
if not importspec.startswith(modprefix): if not name.startswith(modprefix):
importspec = modprefix + importspec name = modprefix + name
clsname = importspec[len(modprefix):].capitalize() + "Plugin" return name
return importspec, clsname
def registerplugin(registerfunc, mod, clsname):
pluginclass = getattr(mod, clsname)
plugin = pluginclass()
registerfunc(plugin)
return plugin
def importplugin(importspec): def importplugin(importspec):
try: try:

View File

@ -5,8 +5,7 @@ EXPECTTIMEOUT=10.0
class TestGeneralUsage: class TestGeneralUsage:
def test_config_error(self, testdir): def test_config_error(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
class ConftestPlugin: def pytest_configure(config):
def pytest_configure(self, config):
raise config.Error("hello") raise config.Error("hello")
""") """)
result = testdir.runpytest(testdir.tmpdir) result = testdir.runpytest(testdir.tmpdir)
@ -17,8 +16,7 @@ class TestGeneralUsage:
def test_config_preparse_plugin_option(self, testdir): def test_config_preparse_plugin_option(self, testdir):
testdir.makepyfile(pytest_xyz=""" testdir.makepyfile(pytest_xyz="""
class XyzPlugin: def pytest_addoption(parser):
def pytest_addoption(self, parser):
parser.addoption("--xyz", dest="xyz", action="store") parser.addoption("--xyz", dest="xyz", action="store")
""") """)
testdir.makepyfile(test_one=""" testdir.makepyfile(test_one="""

View File

@ -193,10 +193,9 @@ class TestCustomConftests:
def test_avoid_directory_on_option(self, testdir): def test_avoid_directory_on_option(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
class ConftestPlugin: def pytest_addoption(parser):
def pytest_addoption(self, parser):
parser.addoption("--XX", action="store_true", default=False) parser.addoption("--XX", action="store_true", default=False)
def pytest_collect_recurse(self, path, parent): def pytest_collect_recurse(path, parent):
return parent.config.getvalue("XX") return parent.config.getvalue("XX")
""") """)
testdir.mkdir("hello") testdir.mkdir("hello")

View File

@ -19,8 +19,7 @@ def test_getfuncargnames():
class TestFillFuncArgs: class TestFillFuncArgs:
def test_funcarg_lookupfails(self, testdir): def test_funcarg_lookupfails(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
class ConftestPlugin: def pytest_funcarg__xyzsomething(request):
def pytest_funcarg__xyzsomething(self, request):
return 42 return 42
""") """)
item = testdir.getitem("def test_func(some): pass") item = testdir.getitem("def test_func(some): pass")
@ -67,6 +66,19 @@ class TestFillFuncArgs:
funcargs.fillfuncargs(item2) funcargs.fillfuncargs(item2)
assert item2.funcargs['something'] == "test_func" assert item2.funcargs['something'] == "test_func"
def test_funcarg_lookup_classlevel(self, testdir):
p = testdir.makepyfile("""
class TestClass:
def pytest_funcarg__something(self, request):
return request.instance
def test_method(self, something):
assert something is self
""")
result = testdir.runpytest(p)
assert result.stdout.fnmatch_lines([
"*1 passed*"
])
class TestRequest: class TestRequest:
def test_request_attributes(self, testdir): def test_request_attributes(self, testdir):
item = testdir.getitem(""" item = testdir.getitem("""
@ -90,6 +102,7 @@ class TestRequest:
""") """)
req = funcargs.FuncargRequest(item, argname="something") req = funcargs.FuncargRequest(item, argname="something")
assert req.cls.__name__ == "TestB" assert req.cls.__name__ == "TestB"
assert req.instance.__class__ == req.cls
def test_request_contains_funcargs_provider(self, testdir): def test_request_contains_funcargs_provider(self, testdir):
modcol = testdir.getmodulecol(""" modcol = testdir.getmodulecol("""
@ -284,8 +297,7 @@ class TestGenfuncFunctional:
def test_addcall_with_funcargs_two(self, testdir): def test_addcall_with_funcargs_two(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
class ConftestPlugin: def pytest_generate_tests(metafunc):
def pytest_generate_tests(self, metafunc):
assert "arg1" in metafunc.funcargnames assert "arg1" in metafunc.funcargnames
metafunc.addcall(funcargs=dict(arg1=1, arg2=2)) metafunc.addcall(funcargs=dict(arg1=1, arg2=2))
""") """)
@ -328,8 +340,7 @@ class TestGenfuncFunctional:
def test_generate_plugin_and_module(self, testdir): def test_generate_plugin_and_module(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
class ConftestPlugin: def pytest_generate_tests(metafunc):
def pytest_generate_tests(self, metafunc):
assert "arg1" in metafunc.funcargnames assert "arg1" in metafunc.funcargnames
metafunc.addcall(id="world", param=(2,100)) metafunc.addcall(id="world", param=(2,100))
""") """)

View File

@ -87,8 +87,7 @@ class TestConfigPickling:
def test_config_pickling_customoption(self, testdir): def test_config_pickling_customoption(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
class ConftestPlugin: def pytest_addoption(parser):
def pytest_addoption(self, parser):
group = parser.addgroup("testing group") group = parser.addgroup("testing group")
group.addoption('-G', '--glong', action="store", default=42, group.addoption('-G', '--glong', action="store", default=42,
type="int", dest="gdest", help="g value.") type="int", dest="gdest", help="g value.")
@ -108,8 +107,7 @@ class TestConfigPickling:
tmp = testdir.tmpdir.ensure("w1", "w2", dir=1) tmp = testdir.tmpdir.ensure("w1", "w2", dir=1)
tmp.ensure("__init__.py") tmp.ensure("__init__.py")
tmp.join("conftest.py").write(py.code.Source(""" tmp.join("conftest.py").write(py.code.Source("""
class ConftestPlugin: def pytest_addoption(parser):
def pytest_addoption(self, parser):
group = parser.addgroup("testing group") group = parser.addgroup("testing group")
group.addoption('-G', '--glong', action="store", default=42, group.addoption('-G', '--glong', action="store", default=42,
type="int", dest="gdest", help="g value.") type="int", dest="gdest", help="g value.")

View File

@ -1,6 +1,5 @@
import py, os import py, os
from py.__.test.pluginmanager import PluginManager, canonical_names from py.__.test.pluginmanager import PluginManager, canonical_importname
from py.__.test.pluginmanager import registerplugin, importplugin
class TestBootstrapping: class TestBootstrapping:
def test_consider_env_fails_to_import(self, monkeypatch): def test_consider_env_fails_to_import(self, monkeypatch):
@ -17,7 +16,7 @@ class TestBootstrapping:
def test_consider_env_plugin_instantiation(self, testdir, monkeypatch): def test_consider_env_plugin_instantiation(self, testdir, monkeypatch):
pluginmanager = PluginManager() pluginmanager = PluginManager()
testdir.syspathinsert() testdir.syspathinsert()
testdir.makepyfile(pytest_xy123="class Xy123Plugin: pass") testdir.makepyfile(pytest_xy123="#")
monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'xy123') monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'xy123')
l1 = len(pluginmanager.getplugins()) l1 = len(pluginmanager.getplugins())
pluginmanager.consider_env() pluginmanager.consider_env()
@ -29,7 +28,7 @@ class TestBootstrapping:
assert l2 == l3 assert l2 == l3
def test_pluginmanager_ENV_startup(self, testdir, monkeypatch): def test_pluginmanager_ENV_startup(self, testdir, monkeypatch):
x500 = testdir.makepyfile(pytest_x500="class X500Plugin: pass") x500 = testdir.makepyfile(pytest_x500="#")
p = testdir.makepyfile(""" p = testdir.makepyfile("""
import py import py
def test_hello(): def test_hello():
@ -48,59 +47,49 @@ class TestBootstrapping:
reset = testdir.syspathinsert() reset = testdir.syspathinsert()
pluginname = "pytest_hello" pluginname = "pytest_hello"
testdir.makepyfile(**{pluginname: """ testdir.makepyfile(**{pluginname: ""})
class HelloPlugin:
pass
"""})
pluginmanager.import_plugin("hello") pluginmanager.import_plugin("hello")
len1 = len(pluginmanager.getplugins()) len1 = len(pluginmanager.getplugins())
pluginmanager.import_plugin("pytest_hello") pluginmanager.import_plugin("pytest_hello")
len2 = len(pluginmanager.getplugins()) len2 = len(pluginmanager.getplugins())
assert len1 == len2 assert len1 == len2
plugin1 = pluginmanager.getplugin("pytest_hello") plugin1 = pluginmanager.getplugin("pytest_hello")
assert plugin1.__class__.__name__ == 'HelloPlugin' assert plugin1.__name__.endswith('pytest_hello')
plugin2 = pluginmanager.getplugin("hello") plugin2 = pluginmanager.getplugin("hello")
assert plugin2 is plugin1 assert plugin2 is plugin1
def test_consider_module(self, testdir): def test_consider_module(self, testdir):
pluginmanager = PluginManager() pluginmanager = PluginManager()
testdir.syspathinsert() testdir.syspathinsert()
testdir.makepyfile(pytest_plug1="class Plug1Plugin: pass") testdir.makepyfile(pytest_plug1="#")
testdir.makepyfile(pytest_plug2="class Plug2Plugin: pass") testdir.makepyfile(pytest_plug2="#")
mod = py.std.new.module("temp") mod = py.std.new.module("temp")
mod.pytest_plugins = ["pytest_plug1", "pytest_plug2"] mod.pytest_plugins = ["pytest_plug1", "pytest_plug2"]
pluginmanager.consider_module(mod) pluginmanager.consider_module(mod)
assert pluginmanager.getplugin("plug1").__class__.__name__ == "Plug1Plugin" assert pluginmanager.getplugin("plug1").__name__ == "pytest_plug1"
assert pluginmanager.getplugin("plug2").__class__.__name__ == "Plug2Plugin" assert pluginmanager.getplugin("plug2").__name__ == "pytest_plug2"
def test_consider_module_import_module(self, testdir): def test_consider_module_import_module(self, testdir):
mod = py.std.new.module("x") mod = py.std.new.module("x")
mod.pytest_plugins = "pytest_a" mod.pytest_plugins = "pytest_a"
aplugin = testdir.makepyfile(pytest_a="""class APlugin: pass""") aplugin = testdir.makepyfile(pytest_a="#")
pluginmanager = PluginManager() pluginmanager = PluginManager()
sorter = testdir.geteventrecorder(pluginmanager) sorter = testdir.geteventrecorder(pluginmanager)
#syspath.prepend(aplugin.dirpath()) #syspath.prepend(aplugin.dirpath())
py.std.sys.path.insert(0, str(aplugin.dirpath())) py.std.sys.path.insert(0, str(aplugin.dirpath()))
pluginmanager.consider_module(mod) pluginmanager.consider_module(mod)
call = sorter.getcall(pluginmanager.hook.pytest_plugin_registered.name) call = sorter.getcall(pluginmanager.hook.pytest_plugin_registered.name)
assert call.plugin.__class__.__name__ == "APlugin" assert call.plugin.__name__ == "pytest_a"
# check that it is not registered twice # check that it is not registered twice
pluginmanager.consider_module(mod) pluginmanager.consider_module(mod)
l = sorter.getcalls("plugin_registered") l = sorter.getcalls("plugin_registered")
assert len(l) == 1 assert len(l) == 1
def test_consider_conftest(self, testdir): def test_consider_conftest_deprecated(self, testdir):
pp = PluginManager() pp = PluginManager()
mod = testdir.makepyfile("class ConftestPlugin: hello = 1").pyimport() mod = testdir.makepyfile("class ConftestPlugin: pass").pyimport()
pp.consider_conftest(mod) call = py.test.raises(ValueError, pp.consider_conftest, mod)
l = [x for x in pp.getplugins() if isinstance(x, mod.ConftestPlugin)]
assert len(l) == 1
assert l[0].hello == 1
pp.consider_conftest(mod)
l = [x for x in pp.getplugins() if isinstance(x, mod.ConftestPlugin)]
assert len(l) == 1
def test_config_sets_conftesthandle_onimport(self, testdir): def test_config_sets_conftesthandle_onimport(self, testdir):
config = testdir.parseconfig([]) config = testdir.parseconfig([])
@ -124,32 +113,15 @@ class TestBootstrapping:
pp.unregister(a2) pp.unregister(a2)
assert not pp.isregistered(a2) assert not pp.isregistered(a2)
def test_canonical_names(self): def test_canonical_importname(self):
for name in 'xyz', 'pytest_xyz', 'pytest_Xyz', 'Xyz': for name in 'xyz', 'pytest_xyz', 'pytest_Xyz', 'Xyz':
impname, clsname = canonical_names(name) impname = canonical_importname(name)
assert impname == "pytest_xyz"
assert clsname == "XyzPlugin"
def test_registerplugin(self):
l = []
registerfunc = l.append
registerplugin(registerfunc, py.io, "TerminalWriter")
assert len(l) == 1
assert isinstance(l[0], py.io.TerminalWriter)
def test_importplugin(self):
assert importplugin("py") == py
py.test.raises(ImportError, "importplugin('laksjd.qwe')")
mod = importplugin("pytest_terminal")
assert mod is py.__.test.plugin.pytest_terminal
class TestPytestPluginInteractions: class TestPytestPluginInteractions:
def test_do_option_conftestplugin(self, testdir): def test_do_option_conftestplugin(self, testdir):
from py.__.test.config import Config from py.__.test.config import Config
p = testdir.makepyfile(""" p = testdir.makepyfile("""
class ConftestPlugin: def pytest_addoption(parser):
def pytest_addoption(self, parser):
parser.addoption('--test123', action="store_true") parser.addoption('--test123', action="store_true")
""") """)
config = Config() config = Config()
@ -165,8 +137,7 @@ class TestPytestPluginInteractions:
config.pluginmanager.do_configure(config=config) config.pluginmanager.do_configure(config=config)
assert not hasattr(config.option, 'test123') assert not hasattr(config.option, 'test123')
p = testdir.makepyfile(""" p = testdir.makepyfile("""
class ConftestPlugin: def pytest_addoption(parser):
def pytest_addoption(self, parser):
parser.addoption('--test123', action="store_true", parser.addoption('--test123', action="store_true",
default=True) default=True)
""") """)

View File

@ -255,6 +255,19 @@ class TestFunction:
assert f5 != f5b assert f5 != f5b
assert not (f5 == f5b) assert not (f5 == f5b)
class callspec1:
param = 1
funcargs = {}
class callspec2:
param = 2
funcargs = {}
f5 = py.test.collect.Function(name="name", config=config,
callspec=callspec1, callobj=isinstance)
f5b = py.test.collect.Function(name="name", config=config,
callspec=callspec2, callobj=isinstance)
assert f5 != f5b
assert not (f5 == f5b)
class TestSorting: class TestSorting:
def test_check_equality_and_cmp_basic(self, testdir): def test_check_equality_and_cmp_basic(self, testdir):
modcol = testdir.getmodulecol(""" modcol = testdir.getmodulecol("""

View File

@ -9,8 +9,7 @@ class TestTracebackCutting:
def test_traceback_argsetup(self, testdir): def test_traceback_argsetup(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
class ConftestPlugin: def pytest_funcarg__hello(request):
def pytest_funcarg__hello(self, request):
raise ValueError("xyz") raise ValueError("xyz")
""") """)
p = testdir.makepyfile("def test(hello): pass") p = testdir.makepyfile("def test(hello): pass")
@ -18,12 +17,12 @@ class TestTracebackCutting:
assert result.ret != 0 assert result.ret != 0
out = result.stdout.str() out = result.stdout.str()
assert out.find("xyz") != -1 assert out.find("xyz") != -1
assert out.find("conftest.py:3: ValueError") != -1 assert out.find("conftest.py:2: ValueError") != -1
numentries = out.count("_ _ _") # separator for traceback entries numentries = out.count("_ _ _") # separator for traceback entries
assert numentries == 0 assert numentries == 0
result = testdir.runpytest("--fulltrace", p) result = testdir.runpytest("--fulltrace", p)
out = result.stdout.str() out = result.stdout.str()
assert out.find("conftest.py:3: ValueError") != -1 assert out.find("conftest.py:2: ValueError") != -1
numentries = out.count("_ _ _ _") # separator for traceback entries numentries = out.count("_ _ _ _") # separator for traceback entries
assert numentries >3 assert numentries >3