commit
58d7f236a7
23
CHANGELOG
23
CHANGELOG
|
@ -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
|
||||||
=====================================
|
=====================================
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import py
|
||||||
|
|
||||||
|
def pytest_runtest_call(item, __multicall__):
|
||||||
|
cap = py.io.StdCapture()
|
||||||
|
try:
|
||||||
|
return __multicall__.execute()
|
||||||
|
finally:
|
||||||
|
outerr = cap.reset()
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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',
|
||||||
|
|
|
@ -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',),
|
||||||
|
|
141
py/_com.py
141
py/_com.py
|
@ -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 KeyError:
|
||||||
|
pass # might be optional param
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
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:
|
except AttributeError:
|
||||||
# builtin function
|
return ()
|
||||||
varnames = ()
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
# 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()
|
comregistry = Registry([])
|
||||||
|
|
|
@ -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):]
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_optionalcallarg(self):
|
def test_keywords_call_error(self):
|
||||||
class P1:
|
multicall = MultiCall([lambda x: x], {})
|
||||||
def m(self, x):
|
py.test.raises(TypeError, "multicall.execute()")
|
||||||
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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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':
|
||||||
|
|
|
@ -396,6 +396,11 @@ 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):
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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():
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
@ -22,6 +22,15 @@ 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',
|
||||||
|
|
|
@ -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()
|
|
@ -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']
|
||||||
|
|
|
@ -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 = getattr(plugin, '__name__', None)
|
||||||
|
if name is None:
|
||||||
name = plugin.__class__.__name__
|
name = plugin.__class__.__name__
|
||||||
if name.endswith("Plugin"):
|
|
||||||
name = name[:-6]
|
|
||||||
#if name == "Conftest":
|
|
||||||
# XXX get filename
|
|
||||||
plugins.append(name)
|
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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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*"
|
||||||
|
])
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
#
|
#
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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 = []
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue