merge 1.0.x changes back to trunk

--HG--
branch : trunk
This commit is contained in:
holger krekel 2009-08-14 18:08:48 +02:00
commit 58d7f236a7
35 changed files with 565 additions and 249 deletions

View File

@ -1,18 +1,31 @@
Changes between 1.0.0 and 1.0.1 Changes between 1.0.0 and 1.0.1
===================================== =====================================
* various unicode fixes: capturing and prints of unicode strings now * added a 'pytest_nose' plugin which handles nose.SkipTest,
work within tests, they are encoded as "utf8" by default, terminalwriting nose-style function/method/generator setup/teardown and
tries to report functions correctly.
* unicode fixes: capturing and unicode writes to sys.stdout
(through e.g a print statement) now work within tests,
they are encoded as "utf8" by default, also terminalwriting
was adapted and somewhat unified between windows and linux was adapted and somewhat unified between windows and linux
* fix issue #27: better reporting on non-collectable items given on commandline * fix issue #27: better reporting on non-collectable items given on commandline
(e.g. pyc files) (e.g. pyc files)
* "Test" prefixed classes with an __init__ method are *not* collected by default anymore * fix issue #33: added --version flag (thanks Benjamin Peterson)
* terser reporting of collection error tracebacks * fix issue #32: adding support for "incomplete" paths to wcpath.status()
* renaming of arguments to some special rather internal hooks * "Test" prefixed classes are *not* collected by default anymore if they
have an __init__ method
* monkeypatch setenv() now accepts a "prepend" parameter
* improved reporting of collection error tracebacks
* simplified multicall mechanism and plugin architecture,
renamed some internal methods and argnames
Changes between 1.0.0b9 and 1.0.0 Changes between 1.0.0b9 and 1.0.0
===================================== =====================================

View File

@ -0,0 +1,8 @@
import py
def pytest_runtest_call(item, __multicall__):
cap = py.io.StdCapture()
try:
return __multicall__.execute()
finally:
outerr = cap.reset()

View File

@ -90,13 +90,13 @@ Available py.test hooks
==================================== ====================================
py.test calls hooks functions to implement its `test collection`_, running and py.test calls hooks functions to implement its `test collection`_, running and
reporting process. Upon loading of a plugin py.test performs reporting process. When py.test loads a plugin it validates that all hook functions
strict checking on contained hook functions. Function and argument names conform to the `hook definition specification`_. The hook function name and its
need to match exactly one of `hook definition specification`_. It thus argument names need to match exactly but it is allowed for an implementation
provides useful error reporting on mistyped hook or argument names to accept *less* parameters. You'll get useful errors on mistyped hook or
and minimizes version incompatibilites. Below you find some introductory argument names. Read on for some introductory information on particular
information on particular hooks. It's sensible to look at existing hooks. It's sensible to look at existing plugins so see example usages
plugins so see example usages and start off with your own plugin. and start off with your own plugin.
.. _`hook definition specification`: plugin/hookspec.html .. _`hook definition specification`: plugin/hookspec.html

64
doc/test/plugin/nose.txt Normal file
View File

@ -0,0 +1,64 @@
pytest_nose plugin
==================
nose-compatibility plugin: allow to run nose test suites natively.
.. contents::
:local:
This is an experimental plugin for allowing to run tests written
in the 'nosetests' style with py.test.
nosetests is a popular clone
of py.test and thus shares some philosophy. This plugin is an
attempt to understand and neutralize differences. It allows to
run nosetests' own test suite and a number of other test suites
without problems.
Usage
-------------
If you type::
py.test -p nose
where you would type ``nosetests``, you can run your nose style tests.
You might also try to run without the nose plugin to see where your test
suite is incompatible to the default py.test.
To avoid the need for specifying a command line option you can set an environment
variable::
PYTEST_PLUGINS=nose
or create a ``conftest.py`` file in your test directory or below::
# conftest.py
pytest_plugins = "nose",
If you find issues or have suggestions you may run::
py.test -p nose --pastebin=all
to create a URL of a test run session and send it with comments to the issue
tracker or mailing list.
Known issues
------------------
- nose-style doctests are not collected and executed correctly,
also fixtures don't work.
Start improving this plugin in 30 seconds
=========================================
Do you find the above documentation or the plugin itself lacking?
1. Download `pytest_nose.py`_ plugin source code
2. put it somewhere as ``pytest_nose.py`` into your import path
3. a subsequent ``py.test`` run will use your local version
Further information: extend_ documentation, other plugins_ or contact_.
.. include:: links.txt

View File

@ -7,7 +7,7 @@ plugins = [
('Plugins related to Python test functions and programs', ('Plugins related to Python test functions and programs',
'xfail figleaf monkeypatch capture recwarn',), 'xfail figleaf monkeypatch capture recwarn',),
('Plugins for other testing styles and languages', ('Plugins for other testing styles and languages',
'unittest doctest oejskit restdoc'), 'oejskit unittest nose doctest restdoc'),
('Plugins for generic reporting and failure logging', ('Plugins for generic reporting and failure logging',
'pastebin resultlog terminal',), 'pastebin resultlog terminal',),
('internal plugins / core functionality', ('internal plugins / core functionality',

View File

@ -52,7 +52,7 @@ initpkg(__name__,
'_com.Registry' : ('./_com.py', 'Registry'), '_com.Registry' : ('./_com.py', 'Registry'),
'_com.MultiCall' : ('./_com.py', 'MultiCall'), '_com.MultiCall' : ('./_com.py', 'MultiCall'),
'_com.comregistry' : ('./_com.py', 'comregistry'), '_com.comregistry' : ('./_com.py', 'comregistry'),
'_com.Hooks' : ('./_com.py', 'Hooks'), '_com.HookRelay' : ('./_com.py', 'HookRelay'),
# py lib cmdline tools # py lib cmdline tools
'cmdline.pytest' : ('./cmdline/pytest.py', 'main',), 'cmdline.pytest' : ('./cmdline/pytest.py', 'main',),

View File

@ -5,77 +5,53 @@ py lib plugins and plugin call management
import py import py
class MultiCall: class MultiCall:
""" Manage a specific call into many python functions/methods. """ execute a call into multiple python functions/methods. """
Simple example: def __init__(self, methods, kwargs, firstresult=False):
MultiCall([list1.append, list2.append], 42).execute()
"""
def __init__(self, methods, *args, **kwargs):
self.methods = methods[:] self.methods = methods[:]
self.args = args self.kwargs = kwargs.copy()
self.kwargs = kwargs self.kwargs['__multicall__'] = self
self.results = [] self.results = []
self.firstresult = firstresult
def __repr__(self): def __repr__(self):
args = [] status = "%d results, %d meths" % (len(self.results), len(self.methods))
if self.args: return "<MultiCall %s, kwargs=%r>" %(status, self.kwargs)
args.append("posargs=%r" %(self.args,))
kw = self.kwargs
args.append(", ".join(["%s=%r" % x for x in self.kwargs.items()]))
args = " ".join(args)
status = "results: %r, rmethods: %r" % (self.results, self.methods)
return "<MultiCall %s %s>" %(args, status)
def execute(self, firstresult=False): def execute(self):
while self.methods: while self.methods:
currentmethod = self.methods.pop() method = self.methods.pop()
res = self.execute_method(currentmethod) kwargs = self.getkwargs(method)
if hasattr(self, '_ex1'): res = method(**kwargs)
self.results = [res]
break
if res is not None: if res is not None:
self.results.append(res) self.results.append(res)
if firstresult: if self.firstresult:
break return res
if not firstresult: if not self.firstresult:
return self.results return self.results
if self.results:
return self.results[-1]
def execute_method(self, currentmethod): def getkwargs(self, method):
self.currentmethod = currentmethod kwargs = {}
# provide call introspection if "__call__" is the first positional argument for argname in varnames(method):
if hasattr(currentmethod, 'im_self'):
varnames = currentmethod.im_func.func_code.co_varnames
needscall = varnames[1:2] == ('__call__',)
else:
try: try:
varnames = currentmethod.func_code.co_varnames kwargs[argname] = self.kwargs[argname]
except AttributeError: except KeyError:
# builtin function pass # might be optional param
varnames = () return kwargs
needscall = varnames[:1] == ('__call__',)
if needscall:
return currentmethod(self, *self.args, **self.kwargs)
else:
#try:
return currentmethod(*self.args, **self.kwargs)
#except TypeError:
# print currentmethod.__module__, currentmethod.__name__, self.args, self.kwargs
# raise
def exclude_other_results(self):
self._ex1 = True
def varnames(rawcode):
ismethod = hasattr(rawcode, 'im_self')
rawcode = getattr(rawcode, 'im_func', rawcode)
rawcode = getattr(rawcode, 'func_code', rawcode)
try:
return rawcode.co_varnames[ismethod:]
except AttributeError:
return ()
class Registry: class Registry:
""" """
Manage Plugins: Load plugins and manage calls to plugins. Manage Plugins: register/unregister call calls to plugins.
""" """
logfile = None
MultiCall = MultiCall
def __init__(self, plugins=None): def __init__(self, plugins=None):
if plugins is None: if plugins is None:
plugins = [] plugins = []
@ -83,6 +59,7 @@ class Registry:
def register(self, plugin): def register(self, plugin):
assert not isinstance(plugin, str) assert not isinstance(plugin, str)
assert not plugin in self._plugins
self._plugins.append(plugin) self._plugins.append(plugin)
def unregister(self, plugin): def unregister(self, plugin):
@ -107,45 +84,39 @@ class Registry:
l.reverse() l.reverse()
return l return l
class Hooks: class HookRelay:
def __init__(self, hookspecs, registry=None): def __init__(self, hookspecs, registry):
self._hookspecs = hookspecs self._hookspecs = hookspecs
if registry is None: self._registry = registry
registry = py._com.comregistry
self.registry = registry
for name, method in vars(hookspecs).items(): for name, method in vars(hookspecs).items():
if name[:1] != "_": if name[:1] != "_":
firstresult = getattr(method, 'firstresult', False) setattr(self, name, self._makecall(name))
mm = HookCall(registry, name, firstresult=firstresult)
setattr(self, name, mm)
def __repr__(self):
return "<Hooks %r %r>" %(self._hookspecs, self.registry)
class HookCall: def _makecall(self, name, extralookup=None):
def __init__(self, registry, name, firstresult, extralookup=None): hookspecmethod = getattr(self._hookspecs, name)
self.registry = registry firstresult = getattr(hookspecmethod, 'firstresult', False)
return HookCaller(self, name, firstresult=firstresult,
extralookup=extralookup)
def _getmethods(self, name, extralookup=()):
return self._registry.listattr(name, extra=extralookup)
def _performcall(self, name, multicall):
return multicall.execute()
class HookCaller:
def __init__(self, hookrelay, name, firstresult, extralookup=()):
self.hookrelay = hookrelay
self.name = name self.name = name
self.firstresult = firstresult self.firstresult = firstresult
self.extralookup = extralookup and [extralookup] or () self.extralookup = extralookup and [extralookup] or ()
def clone(self, extralookup):
return HookCall(self.registry, self.name, self.firstresult, extralookup)
def __repr__(self): def __repr__(self):
mode = self.firstresult and "firstresult" or "each" return "<HookCaller %r>" %(self.name,)
return "<HookCall %r mode=%s %s>" %(self.name, mode, self.registry)
def __call__(self, *args, **kwargs): def __call__(self, **kwargs):
if args: methods = self.hookrelay._getmethods(self.name, self.extralookup)
raise TypeError("only keyword arguments allowed " mc = MultiCall(methods, kwargs, firstresult=self.firstresult)
"for api call to %r" % self.name) return self.hookrelay._performcall(self.name, mc)
attr = self.registry.listattr(self.name, extra=self.extralookup)
mc = MultiCall(attr, **kwargs) comregistry = Registry([])
# XXX this should be doable from a hook impl:
if self.registry.logfile:
self.registry.logfile.write("%s(**%s) # firstresult=%s\n" %
(self.name, kwargs, self.firstresult))
self.registry.logfile.flush()
return mc.execute(firstresult=self.firstresult)
comregistry = Registry()

View File

@ -39,8 +39,7 @@ class ExceptionInfo(object):
""" """
lines = py.std.traceback.format_exception_only(self.type, self.value) lines = py.std.traceback.format_exception_only(self.type, self.value)
text = ''.join(lines) text = ''.join(lines)
if text.endswith('\n'): text = text.rstrip()
text = text[:-1]
if tryshort: if tryshort:
if text.startswith(self._striptext): if text.startswith(self._striptext):
text = text[len(self._striptext):] text = text[len(self._striptext):]

View File

@ -200,6 +200,11 @@ def test_tbentry_reinterpret():
def test_excinfo_exconly(): def test_excinfo_exconly():
excinfo = py.test.raises(ValueError, h) excinfo = py.test.raises(ValueError, h)
assert excinfo.exconly().startswith('ValueError') assert excinfo.exconly().startswith('ValueError')
excinfo = py.test.raises(ValueError,
"raise ValueError('hello\\nworld')")
msg = excinfo.exconly(tryshort=True)
assert msg.startswith('ValueError')
assert msg.endswith("world")
def test_excinfo_repr(): def test_excinfo_repr():
excinfo = py.test.raises(ValueError, h) excinfo = py.test.raises(ValueError, h)
@ -242,7 +247,6 @@ def test_entrysource_Queue_example():
assert s.startswith("def get") assert s.startswith("def get")
def test_codepath_Queue_example(): def test_codepath_Queue_example():
py.test.skip("try harder to get at the paths of code objects.")
import Queue import Queue
try: try:
Queue.Queue().get(timeout=0.001) Queue.Queue().get(timeout=0.001)

View File

@ -173,8 +173,8 @@ class TestSourceParsingAndCompiling:
assert len(source) == 6 assert len(source) == 6
assert source.getstatementrange(2) == (1, 4) assert source.getstatementrange(2) == (1, 4)
@py.test.mark.xfail
def test_getstatementrange_bug2(self): def test_getstatementrange_bug2(self):
py.test.skip("fix me (issue19)")
source = Source("""\ source = Source("""\
assert ( assert (
33 33
@ -300,8 +300,8 @@ def test_deindent():
lines = deindent(source.splitlines()) lines = deindent(source.splitlines())
assert lines == ['', 'def f():', ' def g():', ' pass', ' '] assert lines == ['', 'def f():', ' def g():', ' pass', ' ']
@py.test.mark.xfail
def test_source_of_class_at_eof_without_newline(): def test_source_of_class_at_eof_without_newline():
py.test.skip("CPython's inspect.getsource is buggy")
# this test fails because the implicit inspect.getsource(A) below # this test fails because the implicit inspect.getsource(A) below
# does not return the "x = 1" last line. # does not return the "x = 1" last line.
tmpdir = py.test.ensuretemp("source_write_read") tmpdir = py.test.ensuretemp("source_write_read")

View File

@ -88,8 +88,8 @@ class Gateway(object):
self._channelfactory = ChannelFactory(self, _startcount) self._channelfactory = ChannelFactory(self, _startcount)
self._cleanup.register(self) self._cleanup.register(self)
if _startcount == 1: # only import 'py' on the "client" side if _startcount == 1: # only import 'py' on the "client" side
from py._com import Hooks import py
self.hook = Hooks(ExecnetAPI) self.hook = py._com.HookRelay(ExecnetAPI, py._com.comregistry)
else: else:
self.hook = ExecnetAPI() self.hook = ExecnetAPI()

View File

@ -21,7 +21,8 @@ class GatewayManager:
if not spec.chdir and not spec.popen: if not spec.chdir and not spec.popen:
spec.chdir = defaultchdir spec.chdir = defaultchdir
self.specs.append(spec) self.specs.append(spec)
self.hook = py._com.Hooks(py.execnet._HookSpecs) self.hook = py._com.HookRelay(
py.execnet._HookSpecs, py._com.comregistry)
def makegateways(self): def makegateways(self):
assert not self.gateways assert not self.gateways

View File

@ -1,15 +1,22 @@
import py import py
import os import os
from py._com import Registry, MultiCall from py.__._com import Registry, MultiCall, HookRelay, varnames
from py._com import Hooks
pytest_plugins = "xfail"
def test_varnames():
def f(x):
pass
class A:
def f(self, y):
pass
assert varnames(f) == ("x",)
assert varnames(A.f) == ('y',)
assert varnames(A().f) == ('y',)
class TestMultiCall: class TestMultiCall:
def test_uses_copy_of_methods(self): def test_uses_copy_of_methods(self):
l = [lambda: 42] l = [lambda: 42]
mc = MultiCall(l) mc = MultiCall(l, {})
repr(mc) repr(mc)
l[:] = [] l[:] = []
res = mc.execute() res = mc.execute()
@ -17,23 +24,20 @@ class TestMultiCall:
def test_call_passing(self): def test_call_passing(self):
class P1: class P1:
def m(self, __call__, x): def m(self, __multicall__, x):
assert __call__.currentmethod == self.m assert len(__multicall__.results) == 1
assert len(__call__.results) == 1 assert not __multicall__.methods
assert not __call__.methods
return 17 return 17
class P2: class P2:
def m(self, __call__, x): def m(self, __multicall__, x):
assert __call__.currentmethod == self.m assert __multicall__.results == []
assert __call__.args assert __multicall__.methods
assert __call__.results == []
assert __call__.methods
return 23 return 23
p1 = P1() p1 = P1()
p2 = P2() p2 = P2()
multicall = MultiCall([p1.m, p2.m], 23) multicall = MultiCall([p1.m, p2.m], {'x': 23})
assert "23" in repr(multicall) assert "23" in repr(multicall)
reslist = multicall.execute() reslist = multicall.execute()
assert len(reslist) == 2 assert len(reslist) == 2
@ -43,62 +47,43 @@ class TestMultiCall:
def test_keyword_args(self): def test_keyword_args(self):
def f(x): def f(x):
return x + 1 return x + 1
multicall = MultiCall([f], x=23) class A:
assert "x=23" in repr(multicall) def f(self, x, y):
return x + y
multicall = MultiCall([f, A().f], dict(x=23, y=24))
assert "'x': 23" in repr(multicall)
assert "'y': 24" in repr(multicall)
reslist = multicall.execute() reslist = multicall.execute()
assert reslist == [24] assert reslist == [24+23, 24]
assert "24" in repr(multicall) assert "2 results" in repr(multicall)
def test_keywords_call_error(self):
multicall = MultiCall([lambda x: x], {})
py.test.raises(TypeError, "multicall.execute()")
def test_optionalcallarg(self):
class P1:
def m(self, x):
return x
call = MultiCall([P1().m], 23)
assert "23" in repr(call)
assert call.execute() == [23]
assert call.execute(firstresult=True) == 23
def test_call_subexecute(self): def test_call_subexecute(self):
def m(__call__): def m(__multicall__):
subresult = __call__.execute(firstresult=True) subresult = __multicall__.execute()
return subresult + 1 return subresult + 1
def n(): def n():
return 1 return 1
call = MultiCall([n, m]) call = MultiCall([n, m], {}, firstresult=True)
res = call.execute(firstresult=True)
assert res == 2
def test_call_exclude_other_results(self):
def m(__call__):
__call__.exclude_other_results()
return 10
def n():
return 1
call = MultiCall([n, n, m, n])
res = call.execute() res = call.execute()
assert res == [10] assert res == 2
# doesn't really make sense for firstresult-mode - because
# we might not have had a chance to run at all.
#res = call.execute(firstresult=True)
#assert res == 10
def test_call_none_is_no_result(self): def test_call_none_is_no_result(self):
def m1(): def m1():
return 1 return 1
def m2(): def m2():
return None return None
mc = MultiCall([m1, m2]) res = MultiCall([m1, m2], {}, firstresult=True).execute()
res = mc.execute(firstresult=True)
assert res == 1 assert res == 1
res = MultiCall([m1, m2], {}).execute()
assert res == [1]
class TestRegistry: class TestRegistry:
def test_MultiCall(self):
plugins = Registry()
assert hasattr(plugins, "MultiCall")
def test_register(self): def test_register(self):
registry = Registry() registry = Registry()
@ -142,14 +127,14 @@ class TestRegistry:
def test_api_and_defaults(): def test_api_and_defaults():
assert isinstance(py._com.comregistry, Registry) assert isinstance(py._com.comregistry, Registry)
class TestHooks: class TestHookRelay:
def test_happypath(self): def test_happypath(self):
registry = Registry() registry = Registry()
class Api: class Api:
def hello(self, arg): def hello(self, arg):
pass pass
mcm = Hooks(hookspecs=Api, registry=registry) mcm = HookRelay(hookspecs=Api, registry=registry)
assert hasattr(mcm, 'hello') assert hasattr(mcm, 'hello')
assert repr(mcm.hello).find("hello") != -1 assert repr(mcm.hello).find("hello") != -1
class Plugin: class Plugin:
@ -160,23 +145,21 @@ class TestHooks:
assert l == [4] assert l == [4]
assert not hasattr(mcm, 'world') assert not hasattr(mcm, 'world')
def test_needskeywordargs(self): def test_only_kwargs(self):
registry = Registry() registry = Registry()
class Api: class Api:
def hello(self, arg): def hello(self, arg):
pass pass
mcm = Hooks(hookspecs=Api, registry=registry) mcm = HookRelay(hookspecs=Api, registry=registry)
excinfo = py.test.raises(TypeError, "mcm.hello(3)") py.test.raises(TypeError, "mcm.hello(3)")
assert str(excinfo.value).find("only keyword arguments") != -1
assert str(excinfo.value).find("hello(self, arg)")
def test_firstresult(self): def test_firstresult_definition(self):
registry = Registry() registry = Registry()
class Api: class Api:
def hello(self, arg): pass def hello(self, arg): pass
hello.firstresult = True hello.firstresult = True
mcm = Hooks(hookspecs=Api, registry=registry) mcm = HookRelay(hookspecs=Api, registry=registry)
class Plugin: class Plugin:
def hello(self, arg): def hello(self, arg):
return arg + 1 return arg + 1
@ -186,15 +169,16 @@ class TestHooks:
def test_default_plugins(self): def test_default_plugins(self):
class Api: pass class Api: pass
mcm = Hooks(hookspecs=Api) mcm = HookRelay(hookspecs=Api, registry=py._com.comregistry)
assert mcm.registry == py._com.comregistry assert mcm._registry == py._com.comregistry
def test_hooks_extra_plugins(self): def test_hooks_extra_plugins(self):
registry = Registry() registry = Registry()
class Api: class Api:
def hello(self, arg): def hello(self, arg):
pass pass
hook_hello = Hooks(hookspecs=Api, registry=registry).hello hookrelay = HookRelay(hookspecs=Api, registry=registry)
hook_hello = hookrelay.hello
class Plugin: class Plugin:
def hello(self, arg): def hello(self, arg):
return arg + 1 return arg + 1
@ -202,7 +186,7 @@ class TestHooks:
class Plugin2: class Plugin2:
def hello(self, arg): def hello(self, arg):
return arg + 2 return arg + 2
newhook = hook_hello.clone(extralookup=Plugin2()) newhook = hookrelay._makecall("hello", extralookup=Plugin2())
l = newhook(arg=3) l = newhook(arg=3)
assert l == [5, 4] assert l == [5, 4]
l2 = hook_hello(arg=3) l2 = hook_hello(arg=3)

View File

@ -225,6 +225,12 @@ class TestWCSvnCommandPath(CommonSvnTests):
''' '''
XMLWCStatus.fromstring(xml, self.root) XMLWCStatus.fromstring(xml, self.root)
def test_status_wrong_xml(self):
# testing for XML without author - this used to raise an exception
xml = u'<entry path="/home/jean/zope/venv/projectdb/parts/development-products/DataGridField">\n<wc-status item="incomplete" props="none" revision="784">\n</wc-status>\n</entry>'
st = XMLWCStatus.fromstring(xml, self.root)
assert len(st.incomplete) == 1
def test_diff(self): def test_diff(self):
p = self.root / 'anotherfile' p = self.root / 'anotherfile'
out = p.diff(rev=2) out = p.diff(rev=2)

View File

@ -671,6 +671,10 @@ class XMLWCStatus(WCStatus):
wcpath = rootwcpath.join(path, abs=1) wcpath = rootwcpath.join(path, abs=1)
rootstatus.ignored.append(wcpath) rootstatus.ignored.append(wcpath)
continue continue
elif itemstatus == 'incomplete':
wcpath = rootwcpath.join(path, abs=1)
rootstatus.incomplete.append(wcpath)
continue
rev = statusel.getAttribute('revision') rev = statusel.getAttribute('revision')
if itemstatus == 'added' or itemstatus == 'none': if itemstatus == 'added' or itemstatus == 'none':

View File

@ -395,7 +395,12 @@ class Directory(FSCollector):
def _ignore(self, path): def _ignore(self, path):
ignore_paths = self.config.getconftest_pathlist("collect_ignore", path=path) ignore_paths = self.config.getconftest_pathlist("collect_ignore", path=path)
return ignore_paths and path in ignore_paths return ignore_paths and path in ignore_paths
# XXX more refined would be:
if ignore_paths:
for p in ignore_paths:
if path == p or path.relto(p):
return True
def consider(self, path): def consider(self, path):
if self._ignore(path): if self._ignore(path):

View File

@ -77,8 +77,8 @@ class DSession(Session):
self.item2nodes = {} self.item2nodes = {}
super(DSession, self).__init__(config=config) super(DSession, self).__init__(config=config)
#def pytest_configure(self, __call__, config): #def pytest_configure(self, __multicall__, config):
# __call__.execute() # __multicall__.execute()
# try: # try:
# config.getxspecs() # config.getxspecs()
# except config.Error: # except config.Error:

View File

@ -15,10 +15,15 @@ def pytest_funcarg__testdir(request):
# testdir.plugins.append(obj.testplugin) # testdir.plugins.append(obj.testplugin)
# break # break
#else: #else:
basename = request.module.__name__.split(".")[-1] modname = request.module.__name__.split(".")[-1]
if basename.startswith("pytest_"): if modname.startswith("pytest_"):
testdir.plugins.append(vars(request.module)) testdir.plugins.append(vars(request.module))
testdir.plugins.append(basename) testdir.plugins.append(modname)
#elif modname.startswith("test_pytest"):
# pname = modname[5:]
# assert pname not in testdir.plugins
# testdir.plugins.append(pname)
# #testdir.plugins.append(vars(request.module))
else: else:
pass # raise ValueError("need better support code") pass # raise ValueError("need better support code")
return testdir return testdir

View File

@ -47,7 +47,7 @@ class HookRecorder:
recorder = RecordCalls() recorder = RecordCalls()
self._recorders[hookspecs] = recorder self._recorders[hookspecs] = recorder
self._comregistry.register(recorder) self._comregistry.register(recorder)
self.hook = py._com.Hooks(hookspecs, registry=self._comregistry) self.hook = py._com.HookRelay(hookspecs, registry=self._comregistry)
def finish_recording(self): def finish_recording(self):
for recorder in self._recorders.values(): for recorder in self._recorders.values():

View File

@ -181,11 +181,11 @@ class CaptureManager:
capfuncarg._finalize() capfuncarg._finalize()
del self._capturing_funcargs del self._capturing_funcargs
def pytest_make_collect_report(self, __call__, collector): def pytest_make_collect_report(self, __multicall__, collector):
method = self._getmethod(collector.config, collector.fspath) method = self._getmethod(collector.config, collector.fspath)
self.resumecapture(method) self.resumecapture(method)
try: try:
rep = __call__.execute(firstresult=True) rep = __multicall__.execute()
finally: finally:
outerr = self.suspendcapture() outerr = self.suspendcapture()
addouterr(rep, outerr) addouterr(rep, outerr)
@ -204,11 +204,11 @@ class CaptureManager:
def pytest_runtest_teardown(self, item): def pytest_runtest_teardown(self, item):
self.resumecapture_item(item) self.resumecapture_item(item)
def pytest__teardown_final(self, __call__, session): def pytest__teardown_final(self, __multicall__, session):
method = self._getmethod(session.config, None) method = self._getmethod(session.config, None)
self.resumecapture(method) self.resumecapture(method)
try: try:
rep = __call__.execute(firstresult=True) rep = __multicall__.execute()
finally: finally:
outerr = self.suspendcapture() outerr = self.suspendcapture()
if rep: if rep:
@ -219,9 +219,9 @@ class CaptureManager:
if hasattr(self, '_capturing'): if hasattr(self, '_capturing'):
self.suspendcapture() self.suspendcapture()
def pytest_runtest_makereport(self, __call__, item, call): def pytest_runtest_makereport(self, __multicall__, item, call):
self.deactivate_funcargs() self.deactivate_funcargs()
rep = __call__.execute(firstresult=True) rep = __multicall__.execute()
outerr = self.suspendcapture() outerr = self.suspendcapture()
outerr = (item.outerr[0] + outerr[0], item.outerr[1] + outerr[1]) outerr = (item.outerr[0] + outerr[0], item.outerr[1] + outerr[1])
if not rep.passed: if not rep.passed:

View File

@ -1,9 +1,10 @@
""" default hooks and general py.test options. """ """ default hooks and general py.test options. """
import sys
import py import py
def pytest_pyfunc_call(__call__, pyfuncitem): def pytest_pyfunc_call(__multicall__, pyfuncitem):
if not __call__.execute(firstresult=True): if not __multicall__.execute():
testfunction = pyfuncitem.obj testfunction = pyfuncitem.obj
if pyfuncitem._isyieldedfunction(): if pyfuncitem._isyieldedfunction():
testfunction(*pyfuncitem._args) testfunction(*pyfuncitem._args)
@ -90,10 +91,17 @@ def pytest_addoption(parser):
help="shortcut for '--dist=load --tx=NUM*popen'") help="shortcut for '--dist=load --tx=NUM*popen'")
group.addoption('--rsyncdir', action="append", default=[], metavar="dir1", group.addoption('--rsyncdir', action="append", default=[], metavar="dir1",
help="add directory for rsyncing to remote tx nodes.") help="add directory for rsyncing to remote tx nodes.")
group.addoption('--version', action="store_true",
help="display version information")
def pytest_configure(config): def pytest_configure(config):
fixoptions(config) fixoptions(config)
setsession(config) setsession(config)
if config.option.version:
p = py.path.local(py.__file__).dirpath()
print "This is py.test version %s, imported from %s" % (
py.__version__, p)
sys.exit(0)
#xxxloadplugins(config) #xxxloadplugins(config)
def fixoptions(config): def fixoptions(config):

View File

@ -32,10 +32,10 @@ class Execnetcleanup:
#for gw in l: #for gw in l:
# gw.join() # gw.join()
def pytest_pyfunc_call(self, __call__, pyfuncitem): def pytest_pyfunc_call(self, __multicall__, pyfuncitem):
if self._gateways is not None: if self._gateways is not None:
gateways = self._gateways[:] gateways = self._gateways[:]
res = __call__.execute(firstresult=True) res = __multicall__.execute()
while len(self._gateways) > len(gateways): while len(self._gateways) > len(gateways):
self._gateways[-1].exit() self._gateways[-1].exit()
return res return res

View File

@ -8,14 +8,27 @@ def pytest_addoption(parser):
def pytest_configure(config): def pytest_configure(config):
hooklog = config.getvalue("hooklog") hooklog = config.getvalue("hooklog")
if hooklog: if hooklog:
assert not config.pluginmanager.comregistry.logfile config._hooklogfile = open(hooklog, 'w', 0)
config.pluginmanager.comregistry.logfile = open(hooklog, 'w') config._hooklog_oldperformcall = config.hook._performcall
config.hook._performcall = (lambda name, multicall:
logged_call(name=name, multicall=multicall, config=config))
def logged_call(name, multicall, config):
f = config._hooklogfile
f.write("%s(**%s)\n" % (name, multicall.kwargs))
try:
res = config._hooklog_oldperformcall(name=name, multicall=multicall)
except:
f.write("-> exception")
raise
f.write("-> %r" % (res,))
return res
def pytest_unconfigure(config): def pytest_unconfigure(config):
f = config.pluginmanager.comregistry.logfile try:
if f: del config.hook.__dict__['_performcall']
f.close() except KeyError:
config.pluginmanager.comregistry.logfile = None pass
# =============================================================================== # ===============================================================================
# plugin tests # plugin tests

View File

@ -4,7 +4,7 @@ safely patch object attributes, dicts and environment variables.
Usage Usage
---------------- ----------------
Use the `monkeypatch funcarg`_ to safely patch the environment Use the `monkeypatch funcarg`_ to safely patch environment
variables, object attributes or dictionaries. For example, if you want variables, object attributes or dictionaries. For example, if you want
to set the environment variable ``ENV1`` and patch the to set the environment variable ``ENV1`` and patch the
``os.path.abspath`` function to return a particular value during a test ``os.path.abspath`` function to return a particular value during a test
@ -20,7 +20,16 @@ function execution you can write it down like this:
The function argument will do the modifications and memorize the The function argument will do the modifications and memorize the
old state. After the test function finished execution all old state. After the test function finished execution all
modifications will be reverted. See the `monkeypatch blog post`_ modifications will be reverted. See the `monkeypatch blog post`_
for an extensive discussion. for an extensive discussion.
To add to a possibly existing environment parameter you
can use this example:
.. sourcecode:: python
def test_mypath_finding(monkeypatch):
monkeypatch.setenv('PATH', 'x/y', prepend=":")
# x/y will be at the beginning of $PATH
.. _`monkeypatch blog post`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/ .. _`monkeypatch blog post`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/
""" """
@ -57,8 +66,11 @@ class MonkeyPatch:
self._setitem.insert(0, (dictionary, name, dictionary.get(name, notset))) self._setitem.insert(0, (dictionary, name, dictionary.get(name, notset)))
dictionary[name] = value dictionary[name] = value
def setenv(self, name, value): def setenv(self, name, value, prepend=None):
self.setitem(os.environ, name, str(value)) value = str(value)
if prepend and name in os.environ:
value = value + prepend + os.environ[name]
self.setitem(os.environ, name, value)
def finalize(self): def finalize(self):
for obj, name, value in self._setattr: for obj, name, value in self._setattr:
@ -111,6 +123,16 @@ def test_setenv():
monkeypatch.finalize() monkeypatch.finalize()
assert 'XYZ123' not in os.environ assert 'XYZ123' not in os.environ
def test_setenv_prepend():
import os
monkeypatch = MonkeyPatch()
monkeypatch.setenv('XYZ123', 2, prepend="-")
assert os.environ['XYZ123'] == "2"
monkeypatch.setenv('XYZ123', 3, prepend="-")
assert os.environ['XYZ123'] == "3-2"
monkeypatch.finalize()
assert 'XYZ123' not in os.environ
def test_monkeypatch_plugin(testdir): def test_monkeypatch_plugin(testdir):
reprec = testdir.inline_runsource(""" reprec = testdir.inline_runsource("""
pytest_plugins = 'pytest_monkeypatch', pytest_plugins = 'pytest_monkeypatch',

View File

@ -0,0 +1,97 @@
"""nose-compatibility plugin: allow to run nose test suites natively.
This is an experimental plugin for allowing to run tests written
in the 'nosetests' style with py.test.
nosetests is a popular clone
of py.test and thus shares some philosophy. This plugin is an
attempt to understand and neutralize differences. It allows to
run nosetests' own test suite and a number of other test suites
without problems.
Usage
-------------
If you type::
py.test -p nose
where you would type ``nosetests``, you can run your nose style tests.
You might also try to run without the nose plugin to see where your test
suite is incompatible to the default py.test.
To avoid the need for specifying a command line option you can set an environment
variable::
PYTEST_PLUGINS=nose
or create a ``conftest.py`` file in your test directory or below::
# conftest.py
pytest_plugins = "nose",
If you find issues or have suggestions you may run::
py.test -p nose --pastebin=all
to create a URL of a test run session and send it with comments to the issue
tracker or mailing list.
Known issues
------------------
- nose-style doctests are not collected and executed correctly,
also fixtures don't work.
"""
import py
import inspect
import sys
def pytest_runtest_makereport(__multicall__, item, call):
SkipTest = getattr(sys.modules.get('nose', None), 'SkipTest', None)
if SkipTest:
if call.excinfo and call.excinfo.errisinstance(SkipTest):
# let's substitute the excinfo with a py.test.skip one
call2 = call.__class__(lambda: py.test.skip(str(call.excinfo.value)), call.when)
call.excinfo = call2.excinfo
def pytest_report_iteminfo(item):
# nose 0.11.1 uses decorators for "raises" and other helpers.
# for reporting progress by filename we fish for the filename
if isinstance(item, py.test.collect.Function):
obj = item.obj
if hasattr(obj, 'compat_co_firstlineno'):
fn = sys.modules[obj.__module__].__file__
if fn.endswith(".pyc"):
fn = fn[:-1]
#assert 0
#fn = inspect.getsourcefile(obj) or inspect.getfile(obj)
lineno = obj.compat_co_firstlineno
return py.path.local(fn), lineno, obj.__module__
def pytest_runtest_setup(item):
if isinstance(item, (py.test.collect.Function)):
if isinstance(item.parent, py.test.collect.Generator):
gen = item.parent
if not hasattr(gen, '_nosegensetup'):
call_optional(gen.obj, 'setup')
if isinstance(gen.parent, py.test.collect.Instance):
call_optional(gen.parent.obj, 'setup')
gen._nosegensetup = True
call_optional(item.obj, 'setup')
def pytest_runtest_teardown(item):
if isinstance(item, py.test.collect.Function):
call_optional(item.obj, 'teardown')
#if hasattr(item.parent, '_nosegensetup'):
# #call_optional(item._nosegensetup, 'teardown')
# del item.parent._nosegensetup
def pytest_make_collect_report(collector):
if isinstance(collector, py.test.collect.Generator):
call_optional(collector.obj, 'setup')
def call_optional(obj, name):
method = getattr(obj, name, None)
if method:
method()

View File

@ -33,9 +33,9 @@ def pytest_addoption(parser):
type="choice", choices=['failed', 'all'], type="choice", choices=['failed', 'all'],
help="send failed|all info to Pocoo pastebin service.") help="send failed|all info to Pocoo pastebin service.")
def pytest_configure(__call__, config): def pytest_configure(__multicall__, config):
import tempfile import tempfile
__call__.execute() __multicall__.execute()
if config.option.pastebin == "all": if config.option.pastebin == "all":
config._pastebinfile = tempfile.TemporaryFile() config._pastebinfile = tempfile.TemporaryFile()
tr = config.pluginmanager.impname2plugin['terminalreporter'] tr = config.pluginmanager.impname2plugin['terminalreporter']

View File

@ -241,22 +241,17 @@ class TerminalReporter:
if self.config.option.traceconfig: if self.config.option.traceconfig:
plugins = [] plugins = []
for plugin in self.config.pluginmanager.comregistry: for plugin in self.config.pluginmanager.comregistry:
name = plugin.__class__.__name__ name = getattr(plugin, '__name__', None)
if name.endswith("Plugin"): if name is None:
name = name[:-6] name = plugin.__class__.__name__
#if name == "Conftest": plugins.append(name)
# XXX get filename
plugins.append(name)
else:
plugins.append(str(plugin))
plugins = ", ".join(plugins) plugins = ", ".join(plugins)
self.write_line("active plugins: %s" %(plugins,)) self.write_line("active plugins: %s" %(plugins,))
for i, testarg in py.builtin.enumerate(self.config.args): for i, testarg in py.builtin.enumerate(self.config.args):
self.write_line("test object %d: %s" %(i+1, testarg)) self.write_line("test object %d: %s" %(i+1, testarg))
def pytest_sessionfinish(self, __call__, session, exitstatus): def pytest_sessionfinish(self, exitstatus, __multicall__):
__call__.execute() __multicall__.execute()
self._tw.line("") self._tw.line("")
if exitstatus in (0, 1, 2): if exitstatus in (0, 1, 2):
self.summary_errors() self.summary_errors()

View File

@ -55,6 +55,9 @@ class UnitTestFunction(py.test.collect.Function):
if obj is not _dummy: if obj is not _dummy:
self._obj = obj self._obj = obj
self._sort_value = sort_value self._sort_value = sort_value
if hasattr(self.parent, 'newinstance'):
self.parent.newinstance()
self.obj = self._getobj()
def runtest(self): def runtest(self):
target = self.obj target = self.obj
@ -87,7 +90,6 @@ def test_simple_unittest(testdir):
def test_setup(testdir): def test_setup(testdir):
testpath = testdir.makepyfile(test_two=""" testpath = testdir.makepyfile(test_two="""
import unittest import unittest
pytest_plugins = "pytest_unittest" # XXX
class MyTestCase(unittest.TestCase): class MyTestCase(unittest.TestCase):
def setUp(self): def setUp(self):
self.foo = 1 self.foo = 1
@ -98,6 +100,18 @@ def test_setup(testdir):
rep = reprec.matchreport("test_setUp") rep = reprec.matchreport("test_setUp")
assert rep.passed assert rep.passed
def test_new_instances(testdir):
testpath = testdir.makepyfile("""
import unittest
class MyTestCase(unittest.TestCase):
def test_func1(self):
self.x = 2
def test_func2(self):
assert not hasattr(self, 'x')
""")
reprec = testdir.inline_run(testpath)
reprec.assertoutcome(passed=2)
def test_teardown(testdir): def test_teardown(testdir):
testpath = testdir.makepyfile(test_three=""" testpath = testdir.makepyfile(test_three="""
import unittest import unittest

View File

@ -19,14 +19,12 @@ when it fails. Instead terminal reporting will list it in the
import py import py
pytest_plugins = ['keyword'] def pytest_runtest_makereport(__multicall__, item, call):
def pytest_runtest_makereport(__call__, item, call):
if call.when != "call": if call.when != "call":
return return
if hasattr(item, 'obj') and hasattr(item.obj, 'func_dict'): if hasattr(item, 'obj') and hasattr(item.obj, 'func_dict'):
if 'xfail' in item.obj.func_dict: if 'xfail' in item.obj.func_dict:
res = __call__.execute(firstresult=True) res = __multicall__.execute()
if call.excinfo: if call.excinfo:
res.skipped = True res.skipped = True
res.failed = res.passed = False res.failed = res.passed = False
@ -53,6 +51,9 @@ def pytest_terminal_summary(terminalreporter):
modpath = rep.item.getmodpath(includemodule=True) modpath = rep.item.getmodpath(includemodule=True)
pos = "%s %s:%d: " %(modpath, entry.path, entry.lineno) pos = "%s %s:%d: " %(modpath, entry.path, entry.lineno)
reason = rep.longrepr.reprcrash.message reason = rep.longrepr.reprcrash.message
i = reason.find("\n")
if i != -1:
reason = reason[:i]
tr._tw.line("%s %s" %(pos, reason)) tr._tw.line("%s %s" %(pos, reason))
xpassed = terminalreporter.stats.get("xpassed") xpassed = terminalreporter.stats.get("xpassed")
@ -89,3 +90,4 @@ def test_xfail(testdir):
"*test_that*", "*test_that*",
]) ])
assert result.ret == 1 assert result.ret == 1

View File

@ -0,0 +1,87 @@
import py
py.test.importorskip("nose")
def test_nose_setup(testdir):
p = testdir.makepyfile("""
l = []
def test_hello():
assert l == [1]
def test_world():
assert l == [1,2]
test_hello.setup = lambda: l.append(1)
test_hello.teardown = lambda: l.append(2)
""")
result = testdir.runpytest(p, '-p', 'nose')
result.stdout.fnmatch_lines([
"*2 passed*"
])
def test_nose_test_generator_fixtures(testdir):
p = testdir.makepyfile("""
# taken from nose-0.11.1 unit_tests/test_generator_fixtures.py
from nose.tools import eq_
called = []
def outer_setup():
called.append('outer_setup')
def outer_teardown():
called.append('outer_teardown')
def inner_setup():
called.append('inner_setup')
def inner_teardown():
called.append('inner_teardown')
def test_gen():
called[:] = []
for i in range(0, 5):
yield check, i
def check(i):
expect = ['outer_setup']
for x in range(0, i):
expect.append('inner_setup')
expect.append('inner_teardown')
expect.append('inner_setup')
eq_(called, expect)
test_gen.setup = outer_setup
test_gen.teardown = outer_teardown
check.setup = inner_setup
check.teardown = inner_teardown
class TestClass(object):
def setup(self):
print "setup called in", self
self.called = ['setup']
def teardown(self):
print "teardown called in", self
eq_(self.called, ['setup'])
self.called.append('teardown')
def test(self):
print "test called in", self
for i in range(0, 5):
yield self.check, i
def check(self, i):
print "check called in", self
expect = ['setup']
#for x in range(0, i):
# expect.append('setup')
# expect.append('teardown')
#expect.append('setup')
eq_(self.called, expect)
""")
result = testdir.runpytest(p, '-p', 'nose')
result.stdout.fnmatch_lines([
"*10 passed*"
])

View File

@ -16,10 +16,9 @@ class PluginManager(object):
if comregistry is None: if comregistry is None:
comregistry = py._com.Registry() comregistry = py._com.Registry()
self.comregistry = comregistry self.comregistry = comregistry
self.MultiCall = self.comregistry.MultiCall
self.impname2plugin = {} self.impname2plugin = {}
self.hook = py._com.Hooks( self.hook = py._com.HookRelay(
hookspecs=hookspec, hookspecs=hookspec,
registry=self.comregistry) registry=self.comregistry)
@ -135,15 +134,16 @@ class PluginManager(object):
fail = True fail = True
else: else:
method_args = getargs(method) method_args = getargs(method)
if '__call__' in method_args: if '__multicall__' in method_args:
method_args.remove('__call__') method_args.remove('__multicall__')
hook = hooks[name] hook = hooks[name]
hookargs = getargs(hook) hookargs = getargs(hook)
for arg, hookarg in zip(method_args, hookargs): for arg in method_args:
if arg != hookarg: if arg not in hookargs:
Print("argument mismatch: %r != %r" %(arg, hookarg)) Print("argument %r not available" %(arg, ))
Print("actual : %s" %(formatdef(method))) Print("actual definition: %s" %(formatdef(method)))
Print("required:", formatdef(hook)) Print("available hook arguments: %s" %
", ".join(hookargs))
fail = True fail = True
break break
#if not fail: #if not fail:
@ -166,20 +166,24 @@ class PluginManager(object):
return self.hook.pytest_internalerror(excrepr=excrepr) return self.hook.pytest_internalerror(excrepr=excrepr)
def do_addoption(self, parser): def do_addoption(self, parser):
methods = self.comregistry.listattr("pytest_addoption", reverse=True) mname = "pytest_addoption"
mc = py._com.MultiCall(methods, parser=parser) methods = self.comregistry.listattr(mname, reverse=True)
mc = py._com.MultiCall(methods, {'parser': parser})
mc.execute() mc.execute()
def pytest_plugin_registered(self, plugin): def pytest_plugin_registered(self, plugin):
if hasattr(self, '_config'): if hasattr(self, '_config'):
self.call_plugin(plugin, "pytest_addoption", parser=self._config._parser) self.call_plugin(plugin, "pytest_addoption",
self.call_plugin(plugin, "pytest_configure", config=self._config) {'parser': self._config._parser})
self.call_plugin(plugin, "pytest_configure",
{'config': self._config})
#dic = self.call_plugin(plugin, "pytest_namespace") #dic = self.call_plugin(plugin, "pytest_namespace")
#self._updateext(dic) #self._updateext(dic)
def call_plugin(self, plugin, methname, **kwargs): def call_plugin(self, plugin, methname, kwargs):
return self.MultiCall(self.listattr(methname, plugins=[plugin]), return py._com.MultiCall(
**kwargs).execute(firstresult=True) methods=self.listattr(methname, plugins=[plugin]),
kwargs=kwargs, firstresult=True).execute()
def _updateext(self, dic): def _updateext(self, dic):
if dic: if dic:
@ -200,9 +204,6 @@ class PluginManager(object):
config.hook.pytest_unconfigure(config=config) config.hook.pytest_unconfigure(config=config)
config.pluginmanager.unregister(self) config.pluginmanager.unregister(self)
class Ext:
""" namespace for extension objects. """
# #
# XXX old code to automatically load classes # XXX old code to automatically load classes
# #

View File

@ -155,8 +155,8 @@ class PyCollectorMixin(PyobjMixin, py.test.collect.Collector):
cls = clscol and clscol.obj or None cls = clscol and clscol.obj or None
metafunc = funcargs.Metafunc(funcobj, config=self.config, metafunc = funcargs.Metafunc(funcobj, config=self.config,
cls=cls, module=module) cls=cls, module=module)
gentesthook = self.config.hook.pytest_generate_tests.clone( gentesthook = self.config.hook._makecall(
extralookup=module) "pytest_generate_tests", extralookup=module)
gentesthook(metafunc=metafunc) gentesthook(metafunc=metafunc)
if not metafunc._calls: if not metafunc._calls:
return self.Function(name, parent=self) return self.Function(name, parent=self)

View File

@ -3,6 +3,14 @@ import py
EXPECTTIMEOUT=10.0 EXPECTTIMEOUT=10.0
class TestGeneralUsage: class TestGeneralUsage:
def test_version(self, testdir):
assert py.version == py.__version__
result = testdir.runpytest("--version")
assert result.ret == 0
p = py.path.local(py.__file__).dirpath()
assert result.stderr.fnmatch_lines([
'*py.test*%s*, imported from: %s*' % (py.version, p)
])
def test_config_error(self, testdir): def test_config_error(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
def pytest_configure(config): def pytest_configure(config):

View File

@ -145,7 +145,7 @@ class TestCollectFS:
names = [x.name for x in col.collect()] names = [x.name for x in col.collect()]
assert names == ["dir1", "dir2", "test_one.py", "test_two.py", "x"] assert names == ["dir1", "dir2", "test_one.py", "test_two.py", "x"]
class TestCollectPluginHooks: class TestCollectPluginHookRelay:
def test_pytest_collect_file(self, testdir): def test_pytest_collect_file(self, testdir):
tmpdir = testdir.tmpdir tmpdir = testdir.tmpdir
wascalled = [] wascalled = []

View File

@ -4,7 +4,7 @@ from py.__.test.pluginmanager import PluginManager, canonical_importname, collec
class TestBootstrapping: class TestBootstrapping:
def test_consider_env_fails_to_import(self, monkeypatch): def test_consider_env_fails_to_import(self, monkeypatch):
pluginmanager = PluginManager() pluginmanager = PluginManager()
monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'nonexistingmodule') monkeypatch.setenv('PYTEST_PLUGINS', 'nonexisting', prepend=",")
py.test.raises(ImportError, "pluginmanager.consider_env()") py.test.raises(ImportError, "pluginmanager.consider_env()")
def test_preparse_args(self): def test_preparse_args(self):
@ -50,7 +50,7 @@ class TestBootstrapping:
plugin = py.test.config.pluginmanager.getplugin('x500') plugin = py.test.config.pluginmanager.getplugin('x500')
assert plugin is not None assert plugin is not None
""") """)
monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'pytest_x500') monkeypatch.setenv('PYTEST_PLUGINS', 'pytest_x500', prepend=",")
result = testdir.runpytest(p) result = testdir.runpytest(p)
assert result.ret == 0 assert result.ret == 0
extra = result.stdout.fnmatch_lines(["*1 passed in*"]) extra = result.stdout.fnmatch_lines(["*1 passed in*"])
@ -185,7 +185,7 @@ class TestPytestPluginInteractions:
assert hello == "world" assert hello == "world"
""") """)
result = testdir.runpytest(p) result = testdir.runpytest(p)
assert result.stdout.fnmatch_lines([ result.stdout.fnmatch_lines([
"*1 passed*" "*1 passed*"
]) ])
@ -222,10 +222,6 @@ class TestPytestPluginInteractions:
config.pluginmanager.register(A()) config.pluginmanager.register(A())
assert len(l) == 2 assert len(l) == 2
def test_MultiCall(self):
pp = PluginManager()
assert hasattr(pp, 'MultiCall')
# lower level API # lower level API
def test_listattr(self): def test_listattr(self):
@ -247,3 +243,12 @@ def test_collectattr():
assert list(methods) == ['pytest_hello', 'pytest_world'] assert list(methods) == ['pytest_hello', 'pytest_world']
methods = py.builtin.sorted(collectattr(B())) methods = py.builtin.sorted(collectattr(B()))
assert list(methods) == ['pytest_hello', 'pytest_world'] assert list(methods) == ['pytest_hello', 'pytest_world']
@py.test.mark.xfail
def test_namespace_has_default_and_env_plugins(testdir):
p = testdir.makepyfile("""
import py
py.test.mark
""")
result = testdir.runpython(p)
assert result.ret == 0