* 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,25 +3,23 @@ pytest_plugins = '_pytest doctest pytester'.split()
rsyncdirs = ['../doc']
import py
class PylibTestconfigPlugin:
def pytest_funcarg__specssh(self, request):
return getspecssh(request.config)
def pytest_funcarg__specsocket(self, request):
return getsocketspec(request.config)
def pytest_addoption(parser):
group = parser.addgroup("pylib", "py lib testing options")
group.addoption('--sshhost',
action="store", dest="sshhost", default=None,
help=("ssh xspec for ssh functional tests. "))
group.addoption('--gx',
action="append", dest="gspecs", default=None,
help=("add a global test environment, XSpec-syntax. "))
group.addoption('--runslowtests',
action="store_true", dest="runslowtests", default=False,
help=("run slow tests"))
def pytest_addoption(self, parser):
group = parser.addgroup("pylib", "py lib testing options")
group.addoption('--sshhost',
action="store", dest="sshhost", default=None,
help=("ssh xspec for ssh functional tests. "))
group.addoption('--gx',
action="append", dest="gspecs", default=None,
help=("add a global test environment, XSpec-syntax. "))
group.addoption('--runslowtests',
action="store_true", dest="runslowtests", default=False,
help=("run slow tests"))
def pytest_funcarg__specssh(request):
return getspecssh(request.config)
def pytest_funcarg__specsocket(request):
return getsocketspec(request.config)
ConftestPlugin = PylibTestconfigPlugin
# configuration information for tests
def getgspecs(config=None):

View File

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

View File

@ -2,9 +2,23 @@ import py
pytest_plugins = "pytester", "plugintester"
class ConftestPlugin:
def pytest_collect_file(self, path, parent):
if path.basename.startswith("pytest_") and path.ext == ".py":
mod = parent.Module(path, parent=parent)
return mod
def pytest_collect_file(path, parent):
if path.basename.startswith("pytest_") and path.ext == ".py":
mod = parent.Module(path, parent=parent)
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,8 +1,7 @@
import py
class _pytestPlugin:
def pytest_funcarg___pytest(self, request):
return PytestArg(request)
def pytest_funcarg___pytest(request):
return PytestArg(request)
class PytestArg:
def __init__(self, request):
@ -97,7 +96,7 @@ class CallRecorder:
return l
def test_generic(plugintester):
plugintester.hookcheck(_pytestPlugin)
plugintester.hookcheck()
def test_callrecorder_basic():
comregistry = py._com.Registry()

View File

@ -1,179 +1,178 @@
""" Plugin implementing defaults and general options. """
import py
class DefaultPlugin:
""" Plugin implementing defaults and general options. """
def pytest_itemrun(item, pdb=None):
from py.__.test.runner import basic_run_report, forked_run_report
if item.config.option.boxed:
runner = forked_run_report
else:
runner = basic_run_report
report = runner(item, pdb=pdb)
item.config.hook.pytest_itemtestreport(rep=report)
return True
def pytest_itemrun(self, item, pdb=None):
from py.__.test.runner import basic_run_report, forked_run_report
if item.config.option.boxed:
runner = forked_run_report
def pytest_item_makereport(item, excinfo, when, outerr):
from py.__.test import runner
return runner.ItemTestReport(item, excinfo, when, outerr)
def pytest_item_runtest_finished(item, excinfo, outerr):
from py.__.test import runner
rep = runner.ItemTestReport(item, excinfo, "execute", outerr)
item.config.hook.pytest_itemtestreport(rep=rep)
def pytest_pyfunc_call(pyfuncitem, args, kwargs):
pyfuncitem.obj(*args, **kwargs)
def pytest_collect_file(path, parent):
ext = path.ext
pb = path.purebasename
if pb.startswith("test_") or pb.endswith("_test") or \
path in parent.config.args:
if ext == ".py":
return parent.Module(path, parent=parent)
def pytest_collect_recurse(path, parent):
#excludelist = parent._config.getvalue_pathlist('dir_exclude', path)
#if excludelist and path in excludelist:
# return
if not parent.recfilter(path):
# check if cmdline specified this dir or a subdir directly
for arg in parent.config.args:
if path == arg or arg.relto(path):
break
else:
runner = basic_run_report
report = runner(item, pdb=pdb)
item.config.hook.pytest_itemtestreport(rep=report)
return True
return False
return True
def pytest_collect_directory(path, parent):
# XXX reconsider the following comment
# not use parent.Directory here as we generally
# want dir/conftest.py to be able to
# define Directory(dir) already
Directory = parent.config.getvalue('Directory', path)
return Directory(path, parent=parent)
def pytest_item_makereport(self, item, excinfo, when, outerr):
from py.__.test import runner
return runner.ItemTestReport(item, excinfo, when, outerr)
def pytest_report_iteminfo(item):
return item.reportinfo()
def pytest_item_runtest_finished(self, item, excinfo, outerr):
from py.__.test import runner
rep = runner.ItemTestReport(item, excinfo, "execute", outerr)
item.config.hook.pytest_itemtestreport(rep=rep)
def pytest_addoption(parser):
group = parser.addgroup("general", "test collection and failure interaction options")
group._addoption('-v', '--verbose', action="count",
dest="verbose", default=0, help="increase verbosity."),
group._addoption('-x', '--exitfirst',
action="store_true", dest="exitfirst", default=False,
help="exit instantly on first error or failed test."),
group._addoption('-k',
action="store", dest="keyword", default='',
help="only run test items matching the given "
"space separated keywords. precede a keyword with '-' to negate. "
"Terminate the expression with ':' to treat a match as a signal "
"to run all subsequent tests. ")
group._addoption('-l', '--showlocals',
action="store_true", dest="showlocals", default=False,
help="show locals in tracebacks (disabled by default).")
#group._addoption('--showskipsummary',
# action="store_true", dest="showskipsummary", default=False,
# help="always show summary of skipped tests")
group._addoption('--pdb',
action="store_true", dest="usepdb", default=False,
help="start pdb (the Python debugger) on errors.")
group._addoption('--tb', metavar="style",
action="store", dest="tbstyle", default='long',
type="choice", choices=['long', 'short', 'no'],
help="traceback verboseness (long/short/no).")
group._addoption('-s',
action="store_true", dest="nocapture", default=False,
help="disable catching of stdout/stderr during test run.")
group.addoption('--boxed',
action="store_true", dest="boxed", default=False,
help="box each test run in a separate process")
group._addoption('-p', action="append", dest="plugin", default = [],
help=("load the specified plugin after command line parsing. "
"Example: '-p hello' will trigger 'import pytest_hello' "
"and instantiate 'HelloPlugin' from the module."))
group._addoption('-f', '--looponfail',
action="store_true", dest="looponfail", default=False,
help="run tests, re-run failing test set until all pass.")
def pytest_pyfunc_call(self, pyfuncitem, args, kwargs):
pyfuncitem.obj(*args, **kwargs)
group = parser.addgroup("test process debugging")
group.addoption('--collectonly',
action="store_true", dest="collectonly",
help="only collect tests, don't execute them."),
group.addoption('--traceconfig',
action="store_true", dest="traceconfig", default=False,
help="trace considerations of conftest.py files."),
group._addoption('--nomagic',
action="store_true", dest="nomagic", default=False,
help="don't reinterpret asserts, no traceback cutting. ")
group._addoption('--fulltrace',
action="store_true", dest="fulltrace", default=False,
help="don't cut any tracebacks (default is to cut).")
group.addoption('--basetemp', dest="basetemp", default=None, metavar="dir",
help="base temporary directory for this test run.")
group._addoption('--iocapture', action="store", default="fd", metavar="method",
type="choice", choices=['fd', 'sys', 'no'],
help="set iocapturing method: fd|sys|no.")
group.addoption('--debug',
action="store_true", dest="debug", default=False,
help="generate and show debugging information.")
def pytest_collect_file(self, path, parent):
ext = path.ext
pb = path.purebasename
if pb.startswith("test_") or pb.endswith("_test") or \
path in parent.config.args:
if ext == ".py":
return parent.Module(path, parent=parent)
group = parser.addgroup("dist", "distributed testing") # see http://pytest.org/help/dist")
group._addoption('--dist', metavar="distmode",
action="store", choices=['load', 'each', 'no'],
type="choice", dest="dist", default="no",
help=("set mode for distributing tests to exec environments.\n\n"
"each: send each test to each available environment.\n\n"
"load: send each test to available environment.\n\n"
"(default) no: run tests inprocess, don't distribute."))
group._addoption('--tx', dest="tx", action="append", default=[], metavar="xspec",
help=("add a test execution environment. some examples: "
"--tx popen//python=python2.5 --tx socket=192.168.1.102:8888 "
"--tx ssh=user@codespeak.net//chdir=testcache"))
group._addoption('-d',
action="store_true", dest="distload", default=False,
help="load-balance tests. shortcut for '--dist=load'")
group._addoption('-n', dest="numprocesses", metavar="numprocesses",
action="store", type="int",
help="shortcut for '--dist=load --tx=NUM*popen'")
group.addoption('--rsyncdir', action="append", default=[], metavar="dir1",
help="add directory for rsyncing to remote tx nodes.")
def pytest_collect_recurse(self, path, parent):
#excludelist = parent._config.getvalue_pathlist('dir_exclude', path)
#if excludelist and path in excludelist:
# return
if not parent.recfilter(path):
# check if cmdline specified this dir or a subdir directly
for arg in parent.config.args:
if path == arg or arg.relto(path):
break
else:
return False
return True
def pytest_collect_directory(self, path, parent):
# XXX reconsider the following comment
# not use parent.Directory here as we generally
# want dir/conftest.py to be able to
# define Directory(dir) already
Directory = parent.config.getvalue('Directory', path)
return Directory(path, parent=parent)
def pytest_configure(config):
fixoptions(config)
setsession(config)
loadplugins(config)
def pytest_report_iteminfo(self, item):
return item.reportinfo()
def fixoptions(config):
if config.option.numprocesses:
config.option.dist = "load"
config.option.tx = ['popen'] * int(config.option.numprocesses)
if config.option.distload:
config.option.dist = "load"
if config.getvalue("usepdb"):
if config.getvalue("looponfail"):
raise config.Error("--pdb incompatible with --looponfail.")
if config.option.dist != "no":
raise config.Error("--pdb incomptaible with distributing tests.")
def pytest_addoption(self, parser):
group = parser.addgroup("general", "test collection and failure interaction options")
group._addoption('-v', '--verbose', action="count",
dest="verbose", default=0, help="increase verbosity."),
group._addoption('-x', '--exitfirst',
action="store_true", dest="exitfirst", default=False,
help="exit instantly on first error or failed test."),
group._addoption('-k',
action="store", dest="keyword", default='',
help="only run test items matching the given "
"space separated keywords. precede a keyword with '-' to negate. "
"Terminate the expression with ':' to treat a match as a signal "
"to run all subsequent tests. ")
group._addoption('-l', '--showlocals',
action="store_true", dest="showlocals", default=False,
help="show locals in tracebacks (disabled by default).")
#group._addoption('--showskipsummary',
# action="store_true", dest="showskipsummary", default=False,
# help="always show summary of skipped tests")
group._addoption('--pdb',
action="store_true", dest="usepdb", default=False,
help="start pdb (the Python debugger) on errors.")
group._addoption('--tb', metavar="style",
action="store", dest="tbstyle", default='long',
type="choice", choices=['long', 'short', 'no'],
help="traceback verboseness (long/short/no).")
group._addoption('-s',
action="store_true", dest="nocapture", default=False,
help="disable catching of stdout/stderr during test run.")
group.addoption('--boxed',
action="store_true", dest="boxed", default=False,
help="box each test run in a separate process")
group._addoption('-p', action="append", dest="plugin", default = [],
help=("load the specified plugin after command line parsing. "
"Example: '-p hello' will trigger 'import pytest_hello' "
"and instantiate 'HelloPlugin' from the module."))
group._addoption('-f', '--looponfail',
action="store_true", dest="looponfail", default=False,
help="run tests, re-run failing test set until all pass.")
def loadplugins(config):
for name in config.getvalue("plugin"):
print "importing", name
config.pluginmanager.import_plugin(name)
group = parser.addgroup("test process debugging")
group.addoption('--collectonly',
action="store_true", dest="collectonly",
help="only collect tests, don't execute them."),
group.addoption('--traceconfig',
action="store_true", dest="traceconfig", default=False,
help="trace considerations of conftest.py files."),
group._addoption('--nomagic',
action="store_true", dest="nomagic", default=False,
help="don't reinterpret asserts, no traceback cutting. ")
group._addoption('--fulltrace',
action="store_true", dest="fulltrace", default=False,
help="don't cut any tracebacks (default is to cut).")
group.addoption('--basetemp', dest="basetemp", default=None, metavar="dir",
help="base temporary directory for this test run.")
group._addoption('--iocapture', action="store", default="fd", metavar="method",
type="choice", choices=['fd', 'sys', 'no'],
help="set iocapturing method: fd|sys|no.")
group.addoption('--debug',
action="store_true", dest="debug", default=False,
help="generate and show debugging information.")
group = parser.addgroup("dist", "distributed testing") # see http://pytest.org/help/dist")
group._addoption('--dist', metavar="distmode",
action="store", choices=['load', 'each', 'no'],
type="choice", dest="dist", default="no",
help=("set mode for distributing tests to exec environments.\n\n"
"each: send each test to each available environment.\n\n"
"load: send each test to available environment.\n\n"
"(default) no: run tests inprocess, don't distribute."))
group._addoption('--tx', dest="tx", action="append", default=[], metavar="xspec",
help=("add a test execution environment. some examples: "
"--tx popen//python=python2.5 --tx socket=192.168.1.102:8888 "
"--tx ssh=user@codespeak.net//chdir=testcache"))
group._addoption('-d',
action="store_true", dest="distload", default=False,
help="load-balance tests. shortcut for '--dist=load'")
group._addoption('-n', dest="numprocesses", metavar="numprocesses",
action="store", type="int",
help="shortcut for '--dist=load --tx=NUM*popen'")
group.addoption('--rsyncdir', action="append", default=[], metavar="dir1",
help="add directory for rsyncing to remote tx nodes.")
def pytest_configure(self, config):
self.fixoptions(config)
self.setsession(config)
self.loadplugins(config)
def fixoptions(self, config):
if config.option.numprocesses:
config.option.dist = "load"
config.option.tx = ['popen'] * int(config.option.numprocesses)
if config.option.distload:
config.option.dist = "load"
if config.getvalue("usepdb"):
if config.getvalue("looponfail"):
raise config.Error("--pdb incompatible with --looponfail.")
if config.option.dist != "no":
raise config.Error("--pdb incomptaible with distributing tests.")
def loadplugins(self, config):
for name in config.getvalue("plugin"):
print "importing", name
config.pluginmanager.import_plugin(name)
def setsession(self, config):
val = config.getvalue
if val("collectonly"):
from py.__.test.session import Session
config.setsessionclass(Session)
else:
if val("looponfail"):
from py.__.test.looponfail.remote import LooponfailingSession
config.setsessionclass(LooponfailingSession)
elif val("dist") != "no":
from py.__.test.dist.dsession import DSession
config.setsessionclass(DSession)
def setsession(config):
val = config.getvalue
if val("collectonly"):
from py.__.test.session import Session
config.setsessionclass(Session)
else:
if val("looponfail"):
from py.__.test.looponfail.remote import LooponfailingSession
config.setsessionclass(LooponfailingSession)
elif val("dist") != "no":
from py.__.test.dist.dsession import DSession
config.setsessionclass(DSession)
def test_implied_different_sessions(tmpdir):
def x(*args):
@ -190,7 +189,7 @@ def test_implied_different_sessions(tmpdir):
assert x('-f') == 'LooponfailingSession'
def test_generic(plugintester):
plugintester.hookcheck(DefaultPlugin)
plugintester.hookcheck()
def test_plugin_specify(testdir):
testdir.chdir()
@ -253,14 +252,10 @@ def test_dist_options(testdir):
assert config.option.dist == "load"
def test_pytest_report_iteminfo():
plugin = DefaultPlugin()
class FakeItem(object):
def reportinfo(self):
return "-reportinfo-"
res = plugin.pytest_report_iteminfo(FakeItem())
res = pytest_report_iteminfo(FakeItem())
assert res == "-reportinfo-"

View File

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

View File

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

View File

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

View File

@ -1,65 +1,58 @@
import py
class FigleafPlugin:
def pytest_addoption(self, parser):
group = parser.addgroup('figleaf options')
group.addoption('-F', action='store_true', default=False,
dest = 'figleaf',
help=('trace coverage with figleaf and write HTML '
'for files below the current working dir'))
group.addoption('--figleaf-data', action='store', default='.figleaf',
dest='figleafdata',
help='path coverage tracing file.')
group.addoption('--figleaf-html', action='store', default='html',
dest='figleafhtml',
help='path to the coverage html dir.')
figleaf = py.test.importorskip("figleaf")
import figleaf.annotate_html
def pytest_configure(self, config):
if config.getvalue('figleaf'):
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_addoption(parser):
group = parser.addgroup('figleaf options')
group.addoption('-F', action='store_true', default=False,
dest = 'figleaf',
help=('trace coverage with figleaf and write HTML '
'for files below the current working dir'))
group.addoption('--figleaf-data', action='store', default='.figleaf',
dest='figleafdata',
help='path coverage tracing file.')
group.addoption('--figleaf-html', action='store', default='html',
dest='figleafhtml',
help='path to the coverage html dir.')
def pytest_terminal_summary(self, terminalreporter):
if hasattr(self, 'figleaf'):
config = terminalreporter.config
datafile = py.path.local(config.getvalue('figleafdata'))
tw = terminalreporter._tw
tw.sep('-', 'figleaf')
tw.line('Writing figleaf data to %s' % (datafile))
self.figleaf.stop()
self.figleaf.write_coverage(str(datafile))
coverage = self.get_coverage(datafile, config)
def pytest_configure(config):
figleaf.start()
reportdir = py.path.local(config.getvalue('figleafhtml'))
tw.line('Writing figleaf html to file://%s' % (reportdir))
self.figleaf.annotate_html.prepare_reportdir(str(reportdir))
exclude = []
self.figleaf.annotate_html.report_as_html(coverage,
str(reportdir), exclude, {})
def pytest_terminal_summary(terminalreporter):
config = terminalreporter.config
datafile = py.path.local(config.getvalue('figleafdata'))
tw = terminalreporter._tw
tw.sep('-', 'figleaf')
tw.line('Writing figleaf data to %s' % (datafile))
figleaf.stop()
figleaf.write_coverage(str(datafile))
coverage = get_coverage(datafile, config)
reportdir = py.path.local(config.getvalue('figleafhtml'))
tw.line('Writing figleaf html to file://%s' % (reportdir))
figleaf.annotate_html.prepare_reportdir(str(reportdir))
exclude = []
figleaf.annotate_html.report_as_html(coverage,
str(reportdir), exclude, {})
def get_coverage(self, datafile, config):
# basepath = config.topdir
basepath = py.path.local()
data = self.figleaf.read_coverage(str(datafile))
d = {}
coverage = self.figleaf.combine_coverage(d, data)
for path in coverage.keys():
if not py.path.local(path).relto(basepath):
del coverage[path]
return coverage
def get_coverage(datafile, config):
# basepath = config.topdir
basepath = py.path.local()
data = figleaf.read_coverage(str(datafile))
d = {}
coverage = figleaf.combine_coverage(d, data)
for path in coverage.keys():
if not py.path.local(path).relto(basepath):
del coverage[path]
return coverage
def test_generic(plugintester):
plugintester.hookcheck(FigleafPlugin)
plugintester.hookcheck()
def test_functional(testdir):
py.test.importorskip("figleaf")
testdir.plugins.append('figleaf')
testdir.plugins.append("figleaf")
testdir.makepyfile("""
def f():
x = 42

View File

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

View File

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

View File

@ -1,9 +1,8 @@
from py.__.test.custompdb import post_mortem
class PdbPlugin:
def pytest_item_runtest_finished(self, item, excinfo, outerr):
if excinfo and item.config.option.usepdb:
tw = py.io.TerminalWriter()
repr = excinfo.getrepr()
repr.toterminal(tw)
post_mortem(excinfo._excinfo[2])
def pytest_item_runtest_finished(item, excinfo, outerr):
if excinfo and item.config.option.usepdb:
tw = py.io.TerminalWriter()
repr = excinfo.getrepr()
repr.toterminal(tw)
post_mortem(excinfo._excinfo[2])

View File

@ -4,32 +4,39 @@ plugin with support classes and functions for testing pytest functionality
import py
from py.__.test.plugin import api
class PlugintesterPlugin:
""" test support code for testing pytest plugins. """
def pytest_funcarg__plugintester(self, request):
return PluginTester(request)
def pytest_funcarg__plugintester(request):
return PluginTester(request)
class PluginTester:
def __init__(self, request):
self.request = request
def testdir(self):
def testdir(self, globs=None):
from pytest_pytester import TmpTestdir
crunner = TmpTestdir(self.request)
self.request.addfinalizer(crunner.finalize)
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 crunner
return testdir
def hookcheck(self, pluginclass):
print "loading and checking", pluginclass
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(pluginclass)
methods = collectattr(plugin)
hooks = collectattr(api.PluginHooks)
getargs = py.std.inspect.getargs
@ -84,4 +91,4 @@ def formatdef(func):
# ===============================================================================
def test_generic(plugintester):
plugintester.hookcheck(PlugintesterPlugin)
plugintester.hookcheck()

View File

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

View File

@ -5,32 +5,21 @@ XXX: Currently in progress, NOT IN WORKING STATE.
"""
import py
class PylintPlugin:
def pytest_addoption(self, parser):
group = parser.addgroup('pylint options')
group.addoption('--pylint', action='store_true',
default=False, dest='pylint',
help='Pylint coverate of test files.')
lint = py.test.importorskip("pylint")
def pytest_configure(self, config):
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 parent.config.getvalue('pylint'):
return PylintItem(path, parent, self.lint)
def pytest_terminal_summary(self, terminalreporter):
if hasattr(self, 'lint'):
print 'placeholder for pylint output'
def pytest_addoption(parser):
group = parser.addgroup('pylint options')
group.addoption('--pylint', action='store_true',
default=False, dest='pylint',
help='Pylint coverate of test files.')
def pytest_collect_file(path, parent):
if path.ext == ".py":
if parent.config.getvalue('pylint'):
return PylintItem(path, parent, self.lint)
def pytest_terminal_summary(terminalreporter):
print 'placeholder for pylint output'
class PylintItem(py.test.collect.Item):
def __init__(self, path, parent, lintlib):

View File

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

View File

@ -7,12 +7,11 @@ to a user. See the test at the bottom for an example.
import py
import os
class RecwarnPlugin:
def pytest_funcarg__recwarn(self, request):
""" check that warnings have been raised. """
warnings = WarningsRecorder()
request.addfinalizer(warnings.finalize)
return warnings
def pytest_funcarg__recwarn(request):
""" check that warnings have been raised. """
warnings = WarningsRecorder()
request.addfinalizer(warnings.finalize)
return warnings
class RecordedWarning:
def __init__(self, message, category, filename, lineno, line):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,6 @@
"""
provide temporary directories to test functions and methods.
example:
pytest_plugins = "pytest_tmpdir"
@ -9,13 +11,9 @@ example:
"""
import py
class TmpdirPlugin:
""" provide temporary directories to test functions and methods.
"""
def pytest_funcarg__tmpdir(self, request):
name = request.function.__name__
return request.config.mktemp(name, numbered=True)
def pytest_funcarg__tmpdir(request):
name = request.function.__name__
return request.config.mktemp(name, numbered=True)
# ===============================================================================
#
@ -24,13 +22,12 @@ class TmpdirPlugin:
# ===============================================================================
#
def test_generic(plugintester):
plugintester.hookcheck(TmpdirPlugin)
plugintester.hookcheck()
def test_funcarg(testdir):
from py.__.test.funcargs import FuncargRequest
item = testdir.getitem("def test_func(tmpdir): pass")
plugin = TmpdirPlugin()
p = plugin.pytest_funcarg__tmpdir(FuncargRequest(item, "tmpdir"))
p = pytest_funcarg__tmpdir(FuncargRequest(item, "tmpdir"))
assert p.check()
bn = p.basename.strip("0123456789-")
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
py.test style tests in one test module.
@ -15,12 +15,9 @@ $Id: conftest.py 60979 2009-01-14 22:29:32Z hpk $
"""
import py
class UnittestPlugin:
""" 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):
return UnitTestCase(name, parent=collector)
def pytest_pycollect_obj(collector, name, obj):
if py.std.inspect.isclass(obj) and issubclass(obj, py.std.unittest.TestCase):
return UnitTestCase(name, parent=collector)
class UnitTestCase(py.test.collect.Class):
def collect(self):
@ -71,7 +68,7 @@ class UnitTestFunction(py.test.collect.Function):
def test_generic(plugintester):
plugintester.hookcheck(UnittestPlugin)
plugintester.hookcheck()
def test_simple_unittest(testdir):
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.
@py.test.mark.xfail("needs refactoring")
def test_hello():
@ -7,48 +9,45 @@ for marking and reporting "expected to fail" tests.
"""
import py
class XfailPlugin(object):
""" mark and report specially about "expected to fail" tests. """
def pytest_item_makereport(__call__, item, excinfo, when, outerr):
if hasattr(item, 'obj') and hasattr(item.obj, 'func_dict'):
if 'xfail' in item.obj.func_dict:
res = __call__.execute(firstresult=True)
if excinfo:
res.skipped = True
res.failed = res.passed = False
else:
res.skipped = res.passed = False
res.failed = True
return res
def pytest_item_makereport(self, __call__, item, excinfo, when, outerr):
if hasattr(item, 'obj') and hasattr(item.obj, 'func_dict'):
if 'xfail' in item.obj.func_dict:
res = __call__.execute(firstresult=True)
if excinfo:
res.skipped = True
res.failed = res.passed = False
else:
res.skipped = res.passed = False
res.failed = True
return res
def pytest_report_teststatus(rep):
""" return shortletter and verbose word. """
if 'xfail' in rep.keywords:
if rep.skipped:
return "xfailed", "x", "xfail"
elif rep.failed:
return "xpassed", "P", "xpass"
def pytest_report_teststatus(self, rep):
""" return shortletter and verbose word. """
if 'xfail' in rep.keywords:
if rep.skipped:
return "xfailed", "x", "xfail"
elif rep.failed:
return "xpassed", "P", "xpass"
# a hook implemented called by the terminalreporter instance/plugin
def pytest_terminal_summary(terminalreporter):
tr = terminalreporter
xfailed = tr.stats.get("xfailed")
if xfailed:
tr.write_sep("_", "expected failures")
for event in xfailed:
entry = event.longrepr.reprcrash
key = entry.path, entry.lineno, entry.message
reason = event.longrepr.reprcrash.message
modpath = event.colitem.getmodpath(includemodule=True)
#tr._tw.line("%s %s:%d: %s" %(modpath, entry.path, entry.lineno, entry.message))
tr._tw.line("%s %s:%d: " %(modpath, entry.path, entry.lineno))
# a hook implemented called by the terminalreporter instance/plugin
def pytest_terminal_summary(self, terminalreporter):
tr = terminalreporter
xfailed = tr.stats.get("xfailed")
if xfailed:
tr.write_sep("_", "expected failures")
for event in xfailed:
entry = event.longrepr.reprcrash
key = entry.path, entry.lineno, entry.message
reason = event.longrepr.reprcrash.message
modpath = event.colitem.getmodpath(includemodule=True)
#tr._tw.line("%s %s:%d: %s" %(modpath, entry.path, entry.lineno, entry.message))
tr._tw.line("%s %s:%d: " %(modpath, entry.path, entry.lineno))
xpassed = terminalreporter.stats.get("xpassed")
if xpassed:
tr.write_sep("_", "UNEXPECTEDLY PASSING TESTS")
for event in xpassed:
tr._tw.line("%s: xpassed" %(event.colitem,))
xpassed = terminalreporter.stats.get("xpassed")
if xpassed:
tr.write_sep("_", "UNEXPECTEDLY PASSING TESTS")
for event in xpassed:
tr._tw.line("%s: xpassed" %(event.colitem,))
# ===============================================================================
#
@ -57,7 +56,7 @@ class XfailPlugin(object):
# ===============================================================================
def test_generic(plugintester):
plugintester.hookcheck(XfailPlugin)
plugintester.hookcheck()
def test_xfail(plugintester, linecomp):
testdir = plugintester.testdir()

View File

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

View File

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

View File

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

View File

@ -19,9 +19,8 @@ def test_getfuncargnames():
class TestFillFuncArgs:
def test_funcarg_lookupfails(self, testdir):
testdir.makeconftest("""
class ConftestPlugin:
def pytest_funcarg__xyzsomething(self, request):
return 42
def pytest_funcarg__xyzsomething(request):
return 42
""")
item = testdir.getitem("def test_func(some): pass")
exc = py.test.raises(LookupError, "funcargs.fillfuncargs(item)")
@ -67,6 +66,19 @@ class TestFillFuncArgs:
funcargs.fillfuncargs(item2)
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:
def test_request_attributes(self, testdir):
item = testdir.getitem("""
@ -90,6 +102,7 @@ class TestRequest:
""")
req = funcargs.FuncargRequest(item, argname="something")
assert req.cls.__name__ == "TestB"
assert req.instance.__class__ == req.cls
def test_request_contains_funcargs_provider(self, testdir):
modcol = testdir.getmodulecol("""
@ -284,10 +297,9 @@ class TestGenfuncFunctional:
def test_addcall_with_funcargs_two(self, testdir):
testdir.makeconftest("""
class ConftestPlugin:
def pytest_generate_tests(self, metafunc):
assert "arg1" in metafunc.funcargnames
metafunc.addcall(funcargs=dict(arg1=1, arg2=2))
def pytest_generate_tests(metafunc):
assert "arg1" in metafunc.funcargnames
metafunc.addcall(funcargs=dict(arg1=1, arg2=2))
""")
p = testdir.makepyfile("""
def pytest_generate_tests(metafunc):
@ -328,10 +340,9 @@ class TestGenfuncFunctional:
def test_generate_plugin_and_module(self, testdir):
testdir.makeconftest("""
class ConftestPlugin:
def pytest_generate_tests(self, metafunc):
assert "arg1" in metafunc.funcargnames
metafunc.addcall(id="world", param=(2,100))
def pytest_generate_tests(metafunc):
assert "arg1" in metafunc.funcargnames
metafunc.addcall(id="world", param=(2,100))
""")
p = testdir.makepyfile("""
def pytest_generate_tests(metafunc):

View File

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

View File

@ -1,6 +1,5 @@
import py, os
from py.__.test.pluginmanager import PluginManager, canonical_names
from py.__.test.pluginmanager import registerplugin, importplugin
from py.__.test.pluginmanager import PluginManager, canonical_importname
class TestBootstrapping:
def test_consider_env_fails_to_import(self, monkeypatch):
@ -17,7 +16,7 @@ class TestBootstrapping:
def test_consider_env_plugin_instantiation(self, testdir, monkeypatch):
pluginmanager = PluginManager()
testdir.syspathinsert()
testdir.makepyfile(pytest_xy123="class Xy123Plugin: pass")
testdir.makepyfile(pytest_xy123="#")
monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'xy123')
l1 = len(pluginmanager.getplugins())
pluginmanager.consider_env()
@ -29,7 +28,7 @@ class TestBootstrapping:
assert l2 == l3
def test_pluginmanager_ENV_startup(self, testdir, monkeypatch):
x500 = testdir.makepyfile(pytest_x500="class X500Plugin: pass")
x500 = testdir.makepyfile(pytest_x500="#")
p = testdir.makepyfile("""
import py
def test_hello():
@ -48,59 +47,49 @@ class TestBootstrapping:
reset = testdir.syspathinsert()
pluginname = "pytest_hello"
testdir.makepyfile(**{pluginname: """
class HelloPlugin:
pass
"""})
testdir.makepyfile(**{pluginname: ""})
pluginmanager.import_plugin("hello")
len1 = len(pluginmanager.getplugins())
pluginmanager.import_plugin("pytest_hello")
len2 = len(pluginmanager.getplugins())
assert len1 == len2
plugin1 = pluginmanager.getplugin("pytest_hello")
assert plugin1.__class__.__name__ == 'HelloPlugin'
assert plugin1.__name__.endswith('pytest_hello')
plugin2 = pluginmanager.getplugin("hello")
assert plugin2 is plugin1
def test_consider_module(self, testdir):
pluginmanager = PluginManager()
testdir.syspathinsert()
testdir.makepyfile(pytest_plug1="class Plug1Plugin: pass")
testdir.makepyfile(pytest_plug2="class Plug2Plugin: pass")
testdir.makepyfile(pytest_plug1="#")
testdir.makepyfile(pytest_plug2="#")
mod = py.std.new.module("temp")
mod.pytest_plugins = ["pytest_plug1", "pytest_plug2"]
pluginmanager.consider_module(mod)
assert pluginmanager.getplugin("plug1").__class__.__name__ == "Plug1Plugin"
assert pluginmanager.getplugin("plug2").__class__.__name__ == "Plug2Plugin"
assert pluginmanager.getplugin("plug1").__name__ == "pytest_plug1"
assert pluginmanager.getplugin("plug2").__name__ == "pytest_plug2"
def test_consider_module_import_module(self, testdir):
mod = py.std.new.module("x")
mod.pytest_plugins = "pytest_a"
aplugin = testdir.makepyfile(pytest_a="""class APlugin: pass""")
aplugin = testdir.makepyfile(pytest_a="#")
pluginmanager = PluginManager()
sorter = testdir.geteventrecorder(pluginmanager)
#syspath.prepend(aplugin.dirpath())
py.std.sys.path.insert(0, str(aplugin.dirpath()))
pluginmanager.consider_module(mod)
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
pluginmanager.consider_module(mod)
l = sorter.getcalls("plugin_registered")
assert len(l) == 1
def test_consider_conftest(self, testdir):
def test_consider_conftest_deprecated(self, testdir):
pp = PluginManager()
mod = testdir.makepyfile("class ConftestPlugin: hello = 1").pyimport()
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
mod = testdir.makepyfile("class ConftestPlugin: pass").pyimport()
call = py.test.raises(ValueError, pp.consider_conftest, mod)
def test_config_sets_conftesthandle_onimport(self, testdir):
config = testdir.parseconfig([])
@ -124,33 +113,16 @@ class TestBootstrapping:
pp.unregister(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':
impname, clsname = canonical_names(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
impname = canonical_importname(name)
class TestPytestPluginInteractions:
def test_do_option_conftestplugin(self, testdir):
from py.__.test.config import Config
p = testdir.makepyfile("""
class ConftestPlugin:
def pytest_addoption(self, parser):
parser.addoption('--test123', action="store_true")
def pytest_addoption(parser):
parser.addoption('--test123', action="store_true")
""")
config = Config()
config._conftest.importconftest(p)
@ -165,10 +137,9 @@ class TestPytestPluginInteractions:
config.pluginmanager.do_configure(config=config)
assert not hasattr(config.option, 'test123')
p = testdir.makepyfile("""
class ConftestPlugin:
def pytest_addoption(self, parser):
parser.addoption('--test123', action="store_true",
default=True)
def pytest_addoption(parser):
parser.addoption('--test123', action="store_true",
default=True)
""")
config._conftest.importconftest(p)
assert config.option.test123

View File

@ -255,6 +255,19 @@ class TestFunction:
assert 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:
def test_check_equality_and_cmp_basic(self, testdir):
modcol = testdir.getmodulecol("""

View File

@ -9,21 +9,20 @@ class TestTracebackCutting:
def test_traceback_argsetup(self, testdir):
testdir.makeconftest("""
class ConftestPlugin:
def pytest_funcarg__hello(self, request):
raise ValueError("xyz")
def pytest_funcarg__hello(request):
raise ValueError("xyz")
""")
p = testdir.makepyfile("def test(hello): pass")
result = testdir.runpytest(p)
assert result.ret != 0
out = result.stdout.str()
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
assert numentries == 0
result = testdir.runpytest("--fulltrace", p)
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
assert numentries >3