From 191d02aef23a7a0c5f5086aa9a88e06f84b67b29 Mon Sep 17 00:00:00 2001 From: holger krekel Date: Mon, 18 May 2009 23:26:16 +0200 Subject: [PATCH] * refactor plugin support to work directly with modules, no classes required anymore. * call funcarg hook if defined on class --HG-- branch : trunk --- py/conftest.py | 32 ++- py/test/funcargs.py | 5 +- py/test/plugin/conftest.py | 24 +- py/test/plugin/pytest__pytest.py | 7 +- py/test/plugin/pytest_default.py | 335 ++++++++++++------------ py/test/plugin/pytest_doctest.py | 33 +-- py/test/plugin/pytest_eventlog.py | 5 +- py/test/plugin/pytest_execnetcleanup.py | 15 +- py/test/plugin/pytest_figleaf.py | 93 +++---- py/test/plugin/pytest_iocapture.py | 24 +- py/test/plugin/pytest_monkeypatch.py | 10 +- py/test/plugin/pytest_pdb.py | 13 +- py/test/plugin/pytest_plugintester.py | 31 ++- py/test/plugin/pytest_pocoo.py | 62 ++--- py/test/plugin/pytest_pylint.py | 35 +-- py/test/plugin/pytest_pytester.py | 35 +-- py/test/plugin/pytest_recwarn.py | 11 +- py/test/plugin/pytest_restdoc.py | 57 ++-- py/test/plugin/pytest_resultdb.py | 2 +- py/test/plugin/pytest_resultlog.py | 43 +-- py/test/plugin/pytest_runner.py | 2 +- py/test/plugin/pytest_terminal.py | 35 ++- py/test/plugin/pytest_tmpdir.py | 17 +- py/test/plugin/pytest_unittest.py | 13 +- py/test/plugin/pytest_xfail.py | 79 +++--- py/test/pluginmanager.py | 53 ++-- py/test/testing/acceptance_test.py | 10 +- py/test/testing/test_collect.py | 9 +- py/test/testing/test_funcargs.py | 33 ++- py/test/testing/test_pickling.py | 18 +- py/test/testing/test_pluginmanager.py | 71 ++--- py/test/testing/test_pycollect.py | 13 + py/test/testing/test_traceback.py | 9 +- 33 files changed, 606 insertions(+), 628 deletions(-) diff --git a/py/conftest.py b/py/conftest.py index 4a1011f66..11e8b1e1a 100644 --- a/py/conftest.py +++ b/py/conftest.py @@ -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): diff --git a/py/test/funcargs.py b/py/test/funcargs.py index 656ccd6ba..84531ad34 100644 --- a/py/test/funcargs.py +++ b/py/test/funcargs.py @@ -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: diff --git a/py/test/plugin/conftest.py b/py/test/plugin/conftest.py index ef6dbd2c8..12f639454 100644 --- a/py/test/plugin/conftest.py +++ b/py/test/plugin/conftest.py @@ -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 diff --git a/py/test/plugin/pytest__pytest.py b/py/test/plugin/pytest__pytest.py index 763e07293..0aa15ead7 100644 --- a/py/test/plugin/pytest__pytest.py +++ b/py/test/plugin/pytest__pytest.py @@ -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() diff --git a/py/test/plugin/pytest_default.py b/py/test/plugin/pytest_default.py index b3a50f5de..5253851b5 100644 --- a/py/test/plugin/pytest_default.py +++ b/py/test/plugin/pytest_default.py @@ -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-" - diff --git a/py/test/plugin/pytest_doctest.py b/py/test/plugin/pytest_doctest.py index b23cbca21..17cdb2d8a 100644 --- a/py/test/plugin/pytest_doctest.py +++ b/py/test/plugin/pytest_doctest.py @@ -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() diff --git a/py/test/plugin/pytest_eventlog.py b/py/test/plugin/pytest_eventlog.py index e397ebee4..07268ed0e 100644 --- a/py/test/plugin/pytest_eventlog.py +++ b/py/test/plugin/pytest_eventlog.py @@ -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(""" diff --git a/py/test/plugin/pytest_execnetcleanup.py b/py/test/plugin/pytest_execnetcleanup.py index a170db879..a31036892 100644 --- a/py/test/plugin/pytest_execnetcleanup.py +++ b/py/test/plugin/pytest_execnetcleanup.py @@ -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): diff --git a/py/test/plugin/pytest_figleaf.py b/py/test/plugin/pytest_figleaf.py index 504213240..d23345eb1 100644 --- a/py/test/plugin/pytest_figleaf.py +++ b/py/test/plugin/pytest_figleaf.py @@ -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 diff --git a/py/test/plugin/pytest_iocapture.py b/py/test/plugin/pytest_iocapture.py index 7aba3898e..16106866e 100644 --- a/py/test/plugin/pytest_iocapture.py +++ b/py/test/plugin/pytest_iocapture.py @@ -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 diff --git a/py/test/plugin/pytest_monkeypatch.py b/py/test/plugin/pytest_monkeypatch.py index b81b53ac5..25684632e 100644 --- a/py/test/plugin/pytest_monkeypatch.py +++ b/py/test/plugin/pytest_monkeypatch.py @@ -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() diff --git a/py/test/plugin/pytest_pdb.py b/py/test/plugin/pytest_pdb.py index c7aea0af5..6320903d3 100644 --- a/py/test/plugin/pytest_pdb.py +++ b/py/test/plugin/pytest_pdb.py @@ -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]) diff --git a/py/test/plugin/pytest_plugintester.py b/py/test/plugin/pytest_plugintester.py index 9e8914be4..b518cfab1 100644 --- a/py/test/plugin/pytest_plugintester.py +++ b/py/test/plugin/pytest_plugintester.py @@ -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() diff --git a/py/test/plugin/pytest_pocoo.py b/py/test/plugin/pytest_pocoo.py index 40d5cca3c..b4bf1bae2 100644 --- a/py/test/plugin/pytest_pocoo.py +++ b/py/test/plugin/pytest_pocoo.py @@ -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(): diff --git a/py/test/plugin/pytest_pylint.py b/py/test/plugin/pytest_pylint.py index 835c5daca..d7dc7529a 100644 --- a/py/test/plugin/pytest_pylint.py +++ b/py/test/plugin/pytest_pylint.py @@ -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): diff --git a/py/test/plugin/pytest_pytester.py b/py/test/plugin/pytest_pytester.py index 46c0ace98..4d52d42c4 100644 --- a/py/test/plugin/pytest_pytester.py +++ b/py/test/plugin/pytest_pytester.py @@ -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 diff --git a/py/test/plugin/pytest_recwarn.py b/py/test/plugin/pytest_recwarn.py index 5099d24f9..e56ac5eb2 100644 --- a/py/test/plugin/pytest_recwarn.py +++ b/py/test/plugin/pytest_recwarn.py @@ -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): diff --git a/py/test/plugin/pytest_restdoc.py b/py/test/plugin/pytest_restdoc.py index cb04ce238..6492fbe22 100644 --- a/py/test/plugin/pytest_restdoc.py +++ b/py/test/plugin/pytest_restdoc.py @@ -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:: diff --git a/py/test/plugin/pytest_resultdb.py b/py/test/plugin/pytest_resultdb.py index e92d8ecd5..59b1660f8 100644 --- a/py/test/plugin/pytest_resultdb.py +++ b/py/test/plugin/pytest_resultdb.py @@ -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 diff --git a/py/test/plugin/pytest_resultlog.py b/py/test/plugin/pytest_resultlog.py index 96d22bcbc..2a8ab2dbe 100644 --- a/py/test/plugin/pytest_resultlog.py +++ b/py/test/plugin/pytest_resultlog.py @@ -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(""" diff --git a/py/test/plugin/pytest_runner.py b/py/test/plugin/pytest_runner.py index 3126c5d42..c9e54f923 100644 --- a/py/test/plugin/pytest_runner.py +++ b/py/test/plugin/pytest_runner.py @@ -146,7 +146,7 @@ class SetupState(object): # =============================================================================== def test_generic(plugintester): - plugintester.hookcheck(RunnerPlugin()) + plugintester.hookcheck() class TestSetupState: def test_setup_prepare(self, testdir): diff --git a/py/test/plugin/pytest_terminal.py b/py/test/plugin/pytest_terminal.py index e003055f0..4598024ae 100644 --- a/py/test/plugin/pytest_terminal.py +++ b/py/test/plugin/pytest_terminal.py @@ -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) diff --git a/py/test/plugin/pytest_tmpdir.py b/py/test/plugin/pytest_tmpdir.py index 2a1d511c2..7c77eb308 100644 --- a/py/test/plugin/pytest_tmpdir.py +++ b/py/test/plugin/pytest_tmpdir.py @@ -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") diff --git a/py/test/plugin/pytest_unittest.py b/py/test/plugin/pytest_unittest.py index 86602c643..ff2c54d3e 100644 --- a/py/test/plugin/pytest_unittest.py +++ b/py/test/plugin/pytest_unittest.py @@ -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(""" diff --git a/py/test/plugin/pytest_xfail.py b/py/test/plugin/pytest_xfail.py index 9277e2e4d..84cbb58ac 100644 --- a/py/test/plugin/pytest_xfail.py +++ b/py/test/plugin/pytest_xfail.py @@ -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() diff --git a/py/test/pluginmanager.py b/py/test/pluginmanager.py index 47171fee0..e746a0546 100644 --- a/py/test/pluginmanager.py +++ b/py/test/pluginmanager.py @@ -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: diff --git a/py/test/testing/acceptance_test.py b/py/test/testing/acceptance_test.py index 7edf4a413..4a68a1233 100644 --- a/py/test/testing/acceptance_test.py +++ b/py/test/testing/acceptance_test.py @@ -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 diff --git a/py/test/testing/test_collect.py b/py/test/testing/test_collect.py index 6d4c50415..2e25662e6 100644 --- a/py/test/testing/test_collect.py +++ b/py/test/testing/test_collect.py @@ -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) diff --git a/py/test/testing/test_funcargs.py b/py/test/testing/test_funcargs.py index 6aaed0aa0..6b3940826 100644 --- a/py/test/testing/test_funcargs.py +++ b/py/test/testing/test_funcargs.py @@ -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): diff --git a/py/test/testing/test_pickling.py b/py/test/testing/test_pickling.py index 1d4ea06d5..24af46e49 100644 --- a/py/test/testing/test_pickling.py +++ b/py/test/testing/test_pickling.py @@ -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 diff --git a/py/test/testing/test_pluginmanager.py b/py/test/testing/test_pluginmanager.py index 4abed9552..d040c2e55 100644 --- a/py/test/testing/test_pluginmanager.py +++ b/py/test/testing/test_pluginmanager.py @@ -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 diff --git a/py/test/testing/test_pycollect.py b/py/test/testing/test_pycollect.py index 3a8e11d72..b662cabba 100644 --- a/py/test/testing/test_pycollect.py +++ b/py/test/testing/test_pycollect.py @@ -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(""" diff --git a/py/test/testing/test_traceback.py b/py/test/testing/test_traceback.py index 3dfa79fb4..9fd1230a7 100644 --- a/py/test/testing/test_traceback.py +++ b/py/test/testing/test_traceback.py @@ -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