* 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'] rsyncdirs = ['../doc']
import py import py
class PylibTestconfigPlugin: def pytest_addoption(parser):
def pytest_funcarg__specssh(self, request): group = parser.addgroup("pylib", "py lib testing options")
return getspecssh(request.config) group.addoption('--sshhost',
def pytest_funcarg__specsocket(self, request): action="store", dest="sshhost", default=None,
return getsocketspec(request.config) 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): def pytest_funcarg__specssh(request):
group = parser.addgroup("pylib", "py lib testing options") return getspecssh(request.config)
group.addoption('--sshhost', def pytest_funcarg__specsocket(request):
action="store", dest="sshhost", default=None, return getsocketspec(request.config)
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"))
ConftestPlugin = PylibTestconfigPlugin
# 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,8 +1,7 @@
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:
def __init__(self, request): def __init__(self, request):
@ -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,179 +1,178 @@
""" Plugin implementing defaults and general options. """
import py import py
class DefaultPlugin: def pytest_itemrun(item, pdb=None):
""" Plugin implementing defaults and general options. """ 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): def pytest_item_makereport(item, excinfo, when, outerr):
from py.__.test.runner import basic_run_report, forked_run_report from py.__.test import runner
if item.config.option.boxed: return runner.ItemTestReport(item, excinfo, when, outerr)
runner = forked_run_report
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: else:
runner = basic_run_report return False
report = runner(item, pdb=pdb) return True
item.config.hook.pytest_itemtestreport(rep=report)
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): def pytest_report_iteminfo(item):
from py.__.test import runner return item.reportinfo()
return runner.ItemTestReport(item, excinfo, when, outerr)
def pytest_item_runtest_finished(self, item, excinfo, outerr): def pytest_addoption(parser):
from py.__.test import runner group = parser.addgroup("general", "test collection and failure interaction options")
rep = runner.ItemTestReport(item, excinfo, "execute", outerr) group._addoption('-v', '--verbose', action="count",
item.config.hook.pytest_itemtestreport(rep=rep) 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): group = parser.addgroup("test process debugging")
pyfuncitem.obj(*args, **kwargs) 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): group = parser.addgroup("dist", "distributed testing") # see http://pytest.org/help/dist")
ext = path.ext group._addoption('--dist', metavar="distmode",
pb = path.purebasename action="store", choices=['load', 'each', 'no'],
if pb.startswith("test_") or pb.endswith("_test") or \ type="choice", dest="dist", default="no",
path in parent.config.args: help=("set mode for distributing tests to exec environments.\n\n"
if ext == ".py": "each: send each test to each available environment.\n\n"
return parent.Module(path, parent=parent) "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): def pytest_configure(config):
#excludelist = parent._config.getvalue_pathlist('dir_exclude', path) fixoptions(config)
#if excludelist and path in excludelist: setsession(config)
# return loadplugins(config)
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_report_iteminfo(self, item): def fixoptions(config):
return item.reportinfo() 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): def loadplugins(config):
group = parser.addgroup("general", "test collection and failure interaction options") for name in config.getvalue("plugin"):
group._addoption('-v', '--verbose', action="count", print "importing", name
dest="verbose", default=0, help="increase verbosity."), config.pluginmanager.import_plugin(name)
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.")
group = parser.addgroup("test process debugging") def setsession(config):
group.addoption('--collectonly', val = config.getvalue
action="store_true", dest="collectonly", if val("collectonly"):
help="only collect tests, don't execute them."), from py.__.test.session import Session
group.addoption('--traceconfig', config.setsessionclass(Session)
action="store_true", dest="traceconfig", default=False, else:
help="trace considerations of conftest.py files."), if val("looponfail"):
group._addoption('--nomagic', from py.__.test.looponfail.remote import LooponfailingSession
action="store_true", dest="nomagic", default=False, config.setsessionclass(LooponfailingSession)
help="don't reinterpret asserts, no traceback cutting. ") elif val("dist") != "no":
group._addoption('--fulltrace', from py.__.test.dist.dsession import DSession
action="store_true", dest="fulltrace", default=False, config.setsessionclass(DSession)
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 test_implied_different_sessions(tmpdir): def test_implied_different_sessions(tmpdir):
def x(*args): def x(*args):
@ -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,18 +1,17 @@
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)
if path.check(fnmatch="test_*.txt"): if path.check(fnmatch="test_*.txt"):
return DoctestTextfile(path, parent) return DoctestTextfile(path, parent)
from py.__.code.excinfo import Repr, ReprFileLocation from py.__.code.excinfo import Repr, ReprFileLocation
@ -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,65 +1,58 @@
import py import py
class FigleafPlugin: figleaf = py.test.importorskip("figleaf")
def pytest_addoption(self, parser): import figleaf.annotate_html
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_configure(self, config): def pytest_addoption(parser):
if config.getvalue('figleaf'): group = parser.addgroup('figleaf options')
try: group.addoption('-F', action='store_true', default=False,
import figleaf dest = 'figleaf',
import figleaf.annotate_html help=('trace coverage with figleaf and write HTML '
except ImportError: 'for files below the current working dir'))
raise config.Error('Could not import figleaf module') group.addoption('--figleaf-data', action='store', default='.figleaf',
self.figleaf = figleaf dest='figleafdata',
self.figleaf.start() 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): def pytest_configure(config):
if hasattr(self, 'figleaf'): figleaf.start()
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)
reportdir = py.path.local(config.getvalue('figleafhtml')) def pytest_terminal_summary(terminalreporter):
tw.line('Writing figleaf html to file://%s' % (reportdir)) config = terminalreporter.config
self.figleaf.annotate_html.prepare_reportdir(str(reportdir)) datafile = py.path.local(config.getvalue('figleafdata'))
exclude = [] tw = terminalreporter._tw
self.figleaf.annotate_html.report_as_html(coverage, tw.sep('-', 'figleaf')
str(reportdir), exclude, {}) 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): 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]
return coverage return coverage
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,16 +1,16 @@
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 = Capture(py.io.StdCaptureFD) """ capture writes to filedescriptors 1 and 2"""
request.addfinalizer(capture.finalize) capture = Capture(py.io.StdCaptureFD)
return capture request.addfinalizer(capture.finalize)
return capture
class Capture: class Capture:
def __init__(self, captureclass): def __init__(self, captureclass):
@ -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,11 +1,9 @@
import os import os
class MonkeypatchPlugin: def pytest_funcarg__monkeypatch(request):
""" setattr-monkeypatching with automatical reversal after test. """ monkeypatch = MonkeyPatch()
def pytest_funcarg__monkeypatch(self, request): request.addfinalizer(monkeypatch.finalize)
monkeypatch = MonkeyPatch() return monkeypatch
request.addfinalizer(monkeypatch.finalize)
return monkeypatch
notset = object() notset = object()

View File

@ -1,9 +1,8 @@
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() repr.toterminal(tw)
repr.toterminal(tw) post_mortem(excinfo._excinfo[2])
post_mortem(excinfo._excinfo[2])

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. """ return PluginTester(request)
def pytest_funcarg__plugintester(self, 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,50 +8,46 @@ 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. """ 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): def getproxy():
group = parser.addgroup("pocoo plugin") return py.std.xmlrpclib.ServerProxy(url.xmlrpc).pastes
group.addoption('-P', '--pocoo-sendfailures',
action='store_true', dest="pocoo_sendfailures",
help="send failures to %s paste service" %(url.base,))
def getproxy(self): def pytest_terminal_summary(terminalreporter):
return py.std.xmlrpclib.ServerProxy(url.xmlrpc).pastes if terminalreporter.config.option.pocoo_sendfailures:
tr = terminalreporter
def pytest_terminal_summary(self, terminalreporter): if 'failed' in tr.stats and tr.config.option.tbstyle != "no":
if terminalreporter.config.option.pocoo_sendfailures: terminalreporter.write_sep("=", "Sending failures to %s" %(url.base,))
tr = terminalreporter terminalreporter.write_line("xmlrpcurl: %s" %(url.xmlrpc,))
if 'failed' in tr.stats and tr.config.option.tbstyle != "no": #print self.__class__.getproxy
terminalreporter.write_sep("=", "Sending failures to %s" %(url.base,)) #print self.__class__, id(self.__class__)
terminalreporter.write_line("xmlrpcurl: %s" %(url.xmlrpc,)) serverproxy = getproxy()
#print self.__class__.getproxy for ev in terminalreporter.stats.get('failed'):
#print self.__class__, id(self.__class__) tw = py.io.TerminalWriter(stringio=True)
serverproxy = self.getproxy() ev.toterminal(tw)
for ev in terminalreporter.stats.get('failed'): s = tw.stringio.getvalue()
tw = py.io.TerminalWriter(stringio=True) # XXX add failure summary
ev.toterminal(tw) assert len(s)
s = tw.stringio.getvalue() terminalreporter.write_line("newpaste() ...")
# XXX add failure summary proxyid = serverproxy.newPaste("python", s)
assert len(s) terminalreporter.write_line("%s%s\n" % (url.show, proxyid))
terminalreporter.write_line("newpaste() ...") break
proxyid = serverproxy.newPaste("python", s)
terminalreporter.write_line("%s%s\n" % (url.show, proxyid))
break
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,32 +5,21 @@ XXX: Currently in progress, NOT IN WORKING STATE.
""" """
import py import py
class PylintPlugin: lint = py.test.importorskip("pylint")
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.')
def pytest_configure(self, config): def pytest_addoption(parser):
if config.getvalue('pylint'): group = parser.addgroup('pylint options')
try: group.addoption('--pylint', action='store_true',
from pylint import lint default=False, dest='pylint',
self.lint = lint help='Pylint coverate of test files.')
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_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): class PylintItem(py.test.collect.Item):
def __init__(self, path, parent, lintlib): 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 from pytest__pytest import CallRecorder
import api import api
def pytest_funcarg__linecomp(request):
return LineComp()
class PytesterPlugin: def pytest_funcarg__LineMatcher(request):
def pytest_funcarg__linecomp(self, request): return LineMatcher
return LineComp()
def pytest_funcarg__LineMatcher(self, request): def pytest_funcarg__testdir(request):
return LineMatcher tmptestdir = TmpTestdir(request)
return tmptestdir
def pytest_funcarg__testdir(self, request): def pytest_funcarg__eventrecorder(request):
tmptestdir = TmpTestdir(request) evrec = EventRecorder(py._com.comregistry)
return tmptestdir request.addfinalizer(lambda: evrec.comregistry.unregister(evrec))
return evrec
def pytest_funcarg__eventrecorder(self, request):
evrec = EventRecorder(py._com.comregistry)
request.addfinalizer(lambda: evrec.comregistry.unregister(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,12 +7,11 @@ 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) return warnings
return warnings
class RecordedWarning: class RecordedWarning:
def __init__(self, message, category, filename, lineno, line): def __init__(self, message, category, filename, lineno, line):

View File

@ -1,23 +1,22 @@
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, help="urlopen() remote links found in ReST text files.")
help="urlopen() remote links found in ReST text files.") group.addoption('--urltimeout', action="store", metavar="secs",
group.addoption('--urltimeout', action="store", metavar="secs", type="int", dest="urlcheck_timeout", default=5,
type="int", dest="urlcheck_timeout", default=5, help="timeout in seconds for remote urlchecks")
help="timeout in seconds for remote urlchecks") group.addoption('--forcegen',
group.addoption('--forcegen', 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:
return ReSTFile(path, parent=parent, project=project) return ReSTFile(path, parent=parent, project=project)
def getproject(path): def getproject(path):
for parent in path.parts(reverse=True): for parent in path.parts(reverse=True):
@ -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):
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: 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): 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. group = parser.addgroup("resultlog", "resultlog plugin options")
Useful for buildbot integration code. group.addoption('--resultlog', action="store", dest="resultlog", metavar="path",
""" help="path for machine-readable result log.")
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_unconfigure(self, config): def pytest_configure(config):
if hasattr(self, 'resultlog'): resultlog = config.option.resultlog
self.resultlog.logfile.close() if resultlog:
del self.resultlog logfile = open(resultlog, 'w', 1) # line buffered
#config.pluginmanager.unregister(self.resultlog) 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): 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. """ if config.option.collectonly:
def pytest_configure(self, config): reporter = CollectonlyReporter(config)
if config.option.collectonly: else:
self.reporter = CollectonlyReporter(config) reporter = TerminalReporter(config)
else: # XXX see remote.py's XXX
self.reporter = TerminalReporter(config) for attr in 'pytest_terminal_hasmarkup', 'pytest_terminal_fullwidth':
# XXX see remote.py's XXX if hasattr(config, attr):
for attr in 'pytest_terminal_hasmarkup', 'pytest_terminal_fullwidth': #print "SETTING TERMINAL OPTIONS", attr, getattr(config, attr)
if hasattr(config, attr): name = attr.split("_")[-1]
#print "SETTING TERMINAL OPTIONS", attr, getattr(config, attr) assert hasattr(self.reporter._tw, name), name
name = attr.split("_")[-1] setattr(reporter._tw, name, getattr(config, attr))
assert hasattr(self.reporter._tw, name), name config.pluginmanager.register(reporter)
setattr(self.reporter._tw, name, getattr(config, attr))
config.pluginmanager.register(self.reporter)
class TerminalReporter: class TerminalReporter:
def __init__(self, config, file=None): def __init__(self, config, file=None):
@ -142,7 +140,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":
@ -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,13 +11,9 @@ example:
""" """
import py import py
class TmpdirPlugin: def pytest_funcarg__tmpdir(request):
""" provide temporary directories to test functions and methods. name = request.function.__name__
""" return request.config.mktemp(name, numbered=True)
def pytest_funcarg__tmpdir(self, request):
name = request.function.__name__
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,12 +15,9 @@ $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. if py.std.inspect.isclass(obj) and issubclass(obj, py.std.unittest.TestCase):
""" return UnitTestCase(name, parent=collector)
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)
class UnitTestCase(py.test.collect.Class): class UnitTestCase(py.test.collect.Class):
def collect(self): def collect(self):
@ -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,48 +9,45 @@ 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. """ 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): def pytest_report_teststatus(rep):
if hasattr(item, 'obj') and hasattr(item.obj, 'func_dict'): """ return shortletter and verbose word. """
if 'xfail' in item.obj.func_dict: if 'xfail' in rep.keywords:
res = __call__.execute(firstresult=True) if rep.skipped:
if excinfo: return "xfailed", "x", "xfail"
res.skipped = True elif rep.failed:
res.failed = res.passed = False return "xpassed", "P", "xpass"
else:
res.skipped = res.passed = False
res.failed = True
return res
def pytest_report_teststatus(self, rep): # a hook implemented called by the terminalreporter instance/plugin
""" return shortletter and verbose word. """ def pytest_terminal_summary(terminalreporter):
if 'xfail' in rep.keywords: tr = terminalreporter
if rep.skipped: xfailed = tr.stats.get("xfailed")
return "xfailed", "x", "xfail" if xfailed:
elif rep.failed: tr.write_sep("_", "expected failures")
return "xpassed", "P", "xpass" 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 xpassed = terminalreporter.stats.get("xpassed")
def pytest_terminal_summary(self, terminalreporter): if xpassed:
tr = terminalreporter tr.write_sep("_", "UNEXPECTEDLY PASSING TESTS")
xfailed = tr.stats.get("xfailed") for event in xpassed:
if xfailed: tr._tw.line("%s: xpassed" %(event.colitem,))
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,))
# =============================================================================== # ===============================================================================
# #
@ -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):
self.hook.pytest_plugin_registered(plugin=plugin) if name is None:
import types name = getattr(plugin, '__name__', id(plugin))
self.comregistry.register(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): 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,10 +65,11 @@ 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,))
self.consider_module(conftestmodule) if self.register(conftestmodule, name=conftestmodule.__file__):
self.consider_module(conftestmodule)
def consider_module(self, mod): def consider_module(self, mod):
attr = getattr(mod, "pytest_plugins", ()) attr = getattr(mod, "pytest_plugins", ())
@ -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,9 +5,8 @@ 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)
assert result.ret != 0 assert result.ret != 0
@ -17,9 +16,8 @@ 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="""
import py import py

View File

@ -193,11 +193,10 @@ 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(path, parent):
def pytest_collect_recurse(self, path, parent): return parent.config.getvalue("XX")
return parent.config.getvalue("XX")
""") """)
testdir.mkdir("hello") testdir.mkdir("hello")
sorter = testdir.inline_run(testdir.tmpdir) sorter = testdir.inline_run(testdir.tmpdir)

View File

@ -19,9 +19,8 @@ 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")
exc = py.test.raises(LookupError, "funcargs.fillfuncargs(item)") exc = py.test.raises(LookupError, "funcargs.fillfuncargs(item)")
@ -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,10 +297,9 @@ 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))
""") """)
p = testdir.makepyfile(""" p = testdir.makepyfile("""
def pytest_generate_tests(metafunc): def pytest_generate_tests(metafunc):
@ -328,10 +340,9 @@ 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))
""") """)
p = testdir.makepyfile(""" p = testdir.makepyfile("""
def pytest_generate_tests(metafunc): def pytest_generate_tests(metafunc):

View File

@ -87,11 +87,10 @@ 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.")
""") """)
config = testdir.parseconfig("-G", "11") config = testdir.parseconfig("-G", "11")
assert config.option.gdest == 11 assert config.option.gdest == 11
@ -108,11 +107,10 @@ 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.")
""")) """))
config = testdir.parseconfig(tmp, "-G", "11") config = testdir.parseconfig(tmp, "-G", "11")
assert config.option.gdest == 11 assert config.option.gdest == 11

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,33 +113,16 @@ 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()
config._conftest.importconftest(p) config._conftest.importconftest(p)
@ -165,10 +137,9 @@ 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)
""") """)
config._conftest.importconftest(p) config._conftest.importconftest(p)
assert config.option.test123 assert config.option.test123

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,21 +9,20 @@ 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")
result = testdir.runpytest(p) result = testdir.runpytest(p)
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