commit
58d7f236a7
25
CHANGELOG
25
CHANGELOG
|
@ -1,18 +1,31 @@
|
|||
Changes between 1.0.0 and 1.0.1
|
||||
=====================================
|
||||
|
||||
* various unicode fixes: capturing and prints of unicode strings now
|
||||
work within tests, they are encoded as "utf8" by default, terminalwriting
|
||||
* added a 'pytest_nose' plugin which handles nose.SkipTest,
|
||||
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
|
||||
|
||||
* 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
|
||||
=====================================
|
||||
|
|
|
@ -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
|
||||
reporting process. Upon loading of a plugin py.test performs
|
||||
strict checking on contained hook functions. Function and argument names
|
||||
need to match exactly one of `hook definition specification`_. It thus
|
||||
provides useful error reporting on mistyped hook or argument names
|
||||
and minimizes version incompatibilites. Below you find some introductory
|
||||
information on particular hooks. It's sensible to look at existing
|
||||
plugins so see example usages and start off with your own plugin.
|
||||
reporting process. When py.test loads a plugin it validates that all hook functions
|
||||
conform to the `hook definition specification`_. The hook function name and its
|
||||
argument names need to match exactly but it is allowed for an implementation
|
||||
to accept *less* parameters. You'll get useful errors on mistyped hook or
|
||||
argument names. Read on for some introductory information on particular
|
||||
hooks. It's sensible to look at existing plugins so see example usages
|
||||
and start off with your own plugin.
|
||||
|
||||
.. _`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',
|
||||
'xfail figleaf monkeypatch capture recwarn',),
|
||||
('Plugins for other testing styles and languages',
|
||||
'unittest doctest oejskit restdoc'),
|
||||
'oejskit unittest nose doctest restdoc'),
|
||||
('Plugins for generic reporting and failure logging',
|
||||
'pastebin resultlog terminal',),
|
||||
('internal plugins / core functionality',
|
||||
|
|
|
@ -52,7 +52,7 @@ initpkg(__name__,
|
|||
'_com.Registry' : ('./_com.py', 'Registry'),
|
||||
'_com.MultiCall' : ('./_com.py', 'MultiCall'),
|
||||
'_com.comregistry' : ('./_com.py', 'comregistry'),
|
||||
'_com.Hooks' : ('./_com.py', 'Hooks'),
|
||||
'_com.HookRelay' : ('./_com.py', 'HookRelay'),
|
||||
|
||||
# py lib cmdline tools
|
||||
'cmdline.pytest' : ('./cmdline/pytest.py', 'main',),
|
||||
|
|
143
py/_com.py
143
py/_com.py
|
@ -5,77 +5,53 @@ py lib plugins and plugin call management
|
|||
import py
|
||||
|
||||
class MultiCall:
|
||||
""" Manage a specific call into many python functions/methods.
|
||||
""" execute a call into multiple python functions/methods. """
|
||||
|
||||
Simple example:
|
||||
MultiCall([list1.append, list2.append], 42).execute()
|
||||
"""
|
||||
|
||||
def __init__(self, methods, *args, **kwargs):
|
||||
def __init__(self, methods, kwargs, firstresult=False):
|
||||
self.methods = methods[:]
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.kwargs = kwargs.copy()
|
||||
self.kwargs['__multicall__'] = self
|
||||
self.results = []
|
||||
self.firstresult = firstresult
|
||||
|
||||
def __repr__(self):
|
||||
args = []
|
||||
if self.args:
|
||||
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)
|
||||
status = "%d results, %d meths" % (len(self.results), len(self.methods))
|
||||
return "<MultiCall %s, kwargs=%r>" %(status, self.kwargs)
|
||||
|
||||
def execute(self, firstresult=False):
|
||||
def execute(self):
|
||||
while self.methods:
|
||||
currentmethod = self.methods.pop()
|
||||
res = self.execute_method(currentmethod)
|
||||
if hasattr(self, '_ex1'):
|
||||
self.results = [res]
|
||||
break
|
||||
method = self.methods.pop()
|
||||
kwargs = self.getkwargs(method)
|
||||
res = method(**kwargs)
|
||||
if res is not None:
|
||||
self.results.append(res)
|
||||
if firstresult:
|
||||
break
|
||||
if not firstresult:
|
||||
if self.firstresult:
|
||||
return res
|
||||
if not self.firstresult:
|
||||
return self.results
|
||||
if self.results:
|
||||
return self.results[-1]
|
||||
|
||||
def execute_method(self, currentmethod):
|
||||
self.currentmethod = currentmethod
|
||||
# provide call introspection if "__call__" is the first positional argument
|
||||
if hasattr(currentmethod, 'im_self'):
|
||||
varnames = currentmethod.im_func.func_code.co_varnames
|
||||
needscall = varnames[1:2] == ('__call__',)
|
||||
else:
|
||||
def getkwargs(self, method):
|
||||
kwargs = {}
|
||||
for argname in varnames(method):
|
||||
try:
|
||||
varnames = currentmethod.func_code.co_varnames
|
||||
except AttributeError:
|
||||
# builtin function
|
||||
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
|
||||
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:
|
||||
return ()
|
||||
|
||||
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):
|
||||
if plugins is None:
|
||||
plugins = []
|
||||
|
@ -83,6 +59,7 @@ class Registry:
|
|||
|
||||
def register(self, plugin):
|
||||
assert not isinstance(plugin, str)
|
||||
assert not plugin in self._plugins
|
||||
self._plugins.append(plugin)
|
||||
|
||||
def unregister(self, plugin):
|
||||
|
@ -107,45 +84,39 @@ class Registry:
|
|||
l.reverse()
|
||||
return l
|
||||
|
||||
class Hooks:
|
||||
def __init__(self, hookspecs, registry=None):
|
||||
class HookRelay:
|
||||
def __init__(self, hookspecs, registry):
|
||||
self._hookspecs = hookspecs
|
||||
if registry is None:
|
||||
registry = py._com.comregistry
|
||||
self.registry = registry
|
||||
self._registry = registry
|
||||
for name, method in vars(hookspecs).items():
|
||||
if name[:1] != "_":
|
||||
firstresult = getattr(method, 'firstresult', False)
|
||||
mm = HookCall(registry, name, firstresult=firstresult)
|
||||
setattr(self, name, mm)
|
||||
def __repr__(self):
|
||||
return "<Hooks %r %r>" %(self._hookspecs, self.registry)
|
||||
setattr(self, name, self._makecall(name))
|
||||
|
||||
class HookCall:
|
||||
def __init__(self, registry, name, firstresult, extralookup=None):
|
||||
self.registry = registry
|
||||
def _makecall(self, name, extralookup=None):
|
||||
hookspecmethod = getattr(self._hookspecs, name)
|
||||
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.firstresult = firstresult
|
||||
self.extralookup = extralookup and [extralookup] or ()
|
||||
|
||||
def clone(self, extralookup):
|
||||
return HookCall(self.registry, self.name, self.firstresult, extralookup)
|
||||
|
||||
def __repr__(self):
|
||||
mode = self.firstresult and "firstresult" or "each"
|
||||
return "<HookCall %r mode=%s %s>" %(self.name, mode, self.registry)
|
||||
return "<HookCaller %r>" %(self.name,)
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
if args:
|
||||
raise TypeError("only keyword arguments allowed "
|
||||
"for api call to %r" % self.name)
|
||||
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()
|
||||
def __call__(self, **kwargs):
|
||||
methods = self.hookrelay._getmethods(self.name, self.extralookup)
|
||||
mc = MultiCall(methods, kwargs, firstresult=self.firstresult)
|
||||
return self.hookrelay._performcall(self.name, mc)
|
||||
|
||||
comregistry = Registry([])
|
||||
|
|
|
@ -39,8 +39,7 @@ class ExceptionInfo(object):
|
|||
"""
|
||||
lines = py.std.traceback.format_exception_only(self.type, self.value)
|
||||
text = ''.join(lines)
|
||||
if text.endswith('\n'):
|
||||
text = text[:-1]
|
||||
text = text.rstrip()
|
||||
if tryshort:
|
||||
if text.startswith(self._striptext):
|
||||
text = text[len(self._striptext):]
|
||||
|
|
|
@ -200,6 +200,11 @@ def test_tbentry_reinterpret():
|
|||
def test_excinfo_exconly():
|
||||
excinfo = py.test.raises(ValueError, h)
|
||||
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():
|
||||
excinfo = py.test.raises(ValueError, h)
|
||||
|
@ -242,7 +247,6 @@ def test_entrysource_Queue_example():
|
|||
assert s.startswith("def get")
|
||||
|
||||
def test_codepath_Queue_example():
|
||||
py.test.skip("try harder to get at the paths of code objects.")
|
||||
import Queue
|
||||
try:
|
||||
Queue.Queue().get(timeout=0.001)
|
||||
|
|
|
@ -173,8 +173,8 @@ class TestSourceParsingAndCompiling:
|
|||
assert len(source) == 6
|
||||
assert source.getstatementrange(2) == (1, 4)
|
||||
|
||||
@py.test.mark.xfail
|
||||
def test_getstatementrange_bug2(self):
|
||||
py.test.skip("fix me (issue19)")
|
||||
source = Source("""\
|
||||
assert (
|
||||
33
|
||||
|
@ -300,8 +300,8 @@ def test_deindent():
|
|||
lines = deindent(source.splitlines())
|
||||
assert lines == ['', 'def f():', ' def g():', ' pass', ' ']
|
||||
|
||||
@py.test.mark.xfail
|
||||
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
|
||||
# does not return the "x = 1" last line.
|
||||
tmpdir = py.test.ensuretemp("source_write_read")
|
||||
|
|
|
@ -88,8 +88,8 @@ class Gateway(object):
|
|||
self._channelfactory = ChannelFactory(self, _startcount)
|
||||
self._cleanup.register(self)
|
||||
if _startcount == 1: # only import 'py' on the "client" side
|
||||
from py._com import Hooks
|
||||
self.hook = Hooks(ExecnetAPI)
|
||||
import py
|
||||
self.hook = py._com.HookRelay(ExecnetAPI, py._com.comregistry)
|
||||
else:
|
||||
self.hook = ExecnetAPI()
|
||||
|
||||
|
|
|
@ -21,7 +21,8 @@ class GatewayManager:
|
|||
if not spec.chdir and not spec.popen:
|
||||
spec.chdir = defaultchdir
|
||||
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):
|
||||
assert not self.gateways
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
|
||||
import py
|
||||
import os
|
||||
from py._com import Registry, MultiCall
|
||||
from py._com import Hooks
|
||||
|
||||
pytest_plugins = "xfail"
|
||||
from py.__._com import Registry, MultiCall, HookRelay, varnames
|
||||
|
||||
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:
|
||||
def test_uses_copy_of_methods(self):
|
||||
l = [lambda: 42]
|
||||
mc = MultiCall(l)
|
||||
mc = MultiCall(l, {})
|
||||
repr(mc)
|
||||
l[:] = []
|
||||
res = mc.execute()
|
||||
|
@ -17,23 +24,20 @@ class TestMultiCall:
|
|||
|
||||
def test_call_passing(self):
|
||||
class P1:
|
||||
def m(self, __call__, x):
|
||||
assert __call__.currentmethod == self.m
|
||||
assert len(__call__.results) == 1
|
||||
assert not __call__.methods
|
||||
def m(self, __multicall__, x):
|
||||
assert len(__multicall__.results) == 1
|
||||
assert not __multicall__.methods
|
||||
return 17
|
||||
|
||||
class P2:
|
||||
def m(self, __call__, x):
|
||||
assert __call__.currentmethod == self.m
|
||||
assert __call__.args
|
||||
assert __call__.results == []
|
||||
assert __call__.methods
|
||||
def m(self, __multicall__, x):
|
||||
assert __multicall__.results == []
|
||||
assert __multicall__.methods
|
||||
return 23
|
||||
|
||||
p1 = P1()
|
||||
p2 = P2()
|
||||
multicall = MultiCall([p1.m, p2.m], 23)
|
||||
multicall = MultiCall([p1.m, p2.m], {'x': 23})
|
||||
assert "23" in repr(multicall)
|
||||
reslist = multicall.execute()
|
||||
assert len(reslist) == 2
|
||||
|
@ -43,62 +47,43 @@ class TestMultiCall:
|
|||
def test_keyword_args(self):
|
||||
def f(x):
|
||||
return x + 1
|
||||
multicall = MultiCall([f], x=23)
|
||||
assert "x=23" in repr(multicall)
|
||||
class A:
|
||||
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()
|
||||
assert reslist == [24]
|
||||
assert "24" in repr(multicall)
|
||||
assert reslist == [24+23, 24]
|
||||
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 m(__call__):
|
||||
subresult = __call__.execute(firstresult=True)
|
||||
def m(__multicall__):
|
||||
subresult = __multicall__.execute()
|
||||
return subresult + 1
|
||||
|
||||
def n():
|
||||
return 1
|
||||
|
||||
call = MultiCall([n, m])
|
||||
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])
|
||||
call = MultiCall([n, m], {}, firstresult=True)
|
||||
res = call.execute()
|
||||
assert res == [10]
|
||||
# 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
|
||||
assert res == 2
|
||||
|
||||
def test_call_none_is_no_result(self):
|
||||
def m1():
|
||||
return 1
|
||||
def m2():
|
||||
return None
|
||||
mc = MultiCall([m1, m2])
|
||||
res = mc.execute(firstresult=True)
|
||||
res = MultiCall([m1, m2], {}, firstresult=True).execute()
|
||||
assert res == 1
|
||||
res = MultiCall([m1, m2], {}).execute()
|
||||
assert res == [1]
|
||||
|
||||
class TestRegistry:
|
||||
def test_MultiCall(self):
|
||||
plugins = Registry()
|
||||
assert hasattr(plugins, "MultiCall")
|
||||
|
||||
def test_register(self):
|
||||
registry = Registry()
|
||||
|
@ -142,14 +127,14 @@ class TestRegistry:
|
|||
def test_api_and_defaults():
|
||||
assert isinstance(py._com.comregistry, Registry)
|
||||
|
||||
class TestHooks:
|
||||
class TestHookRelay:
|
||||
def test_happypath(self):
|
||||
registry = Registry()
|
||||
class Api:
|
||||
def hello(self, arg):
|
||||
pass
|
||||
|
||||
mcm = Hooks(hookspecs=Api, registry=registry)
|
||||
mcm = HookRelay(hookspecs=Api, registry=registry)
|
||||
assert hasattr(mcm, 'hello')
|
||||
assert repr(mcm.hello).find("hello") != -1
|
||||
class Plugin:
|
||||
|
@ -160,23 +145,21 @@ class TestHooks:
|
|||
assert l == [4]
|
||||
assert not hasattr(mcm, 'world')
|
||||
|
||||
def test_needskeywordargs(self):
|
||||
def test_only_kwargs(self):
|
||||
registry = Registry()
|
||||
class Api:
|
||||
def hello(self, arg):
|
||||
pass
|
||||
mcm = Hooks(hookspecs=Api, registry=registry)
|
||||
excinfo = py.test.raises(TypeError, "mcm.hello(3)")
|
||||
assert str(excinfo.value).find("only keyword arguments") != -1
|
||||
assert str(excinfo.value).find("hello(self, arg)")
|
||||
mcm = HookRelay(hookspecs=Api, registry=registry)
|
||||
py.test.raises(TypeError, "mcm.hello(3)")
|
||||
|
||||
def test_firstresult(self):
|
||||
def test_firstresult_definition(self):
|
||||
registry = Registry()
|
||||
class Api:
|
||||
def hello(self, arg): pass
|
||||
hello.firstresult = True
|
||||
|
||||
mcm = Hooks(hookspecs=Api, registry=registry)
|
||||
mcm = HookRelay(hookspecs=Api, registry=registry)
|
||||
class Plugin:
|
||||
def hello(self, arg):
|
||||
return arg + 1
|
||||
|
@ -186,15 +169,16 @@ class TestHooks:
|
|||
|
||||
def test_default_plugins(self):
|
||||
class Api: pass
|
||||
mcm = Hooks(hookspecs=Api)
|
||||
assert mcm.registry == py._com.comregistry
|
||||
mcm = HookRelay(hookspecs=Api, registry=py._com.comregistry)
|
||||
assert mcm._registry == py._com.comregistry
|
||||
|
||||
def test_hooks_extra_plugins(self):
|
||||
registry = Registry()
|
||||
class Api:
|
||||
def hello(self, arg):
|
||||
pass
|
||||
hook_hello = Hooks(hookspecs=Api, registry=registry).hello
|
||||
hookrelay = HookRelay(hookspecs=Api, registry=registry)
|
||||
hook_hello = hookrelay.hello
|
||||
class Plugin:
|
||||
def hello(self, arg):
|
||||
return arg + 1
|
||||
|
@ -202,7 +186,7 @@ class TestHooks:
|
|||
class Plugin2:
|
||||
def hello(self, arg):
|
||||
return arg + 2
|
||||
newhook = hook_hello.clone(extralookup=Plugin2())
|
||||
newhook = hookrelay._makecall("hello", extralookup=Plugin2())
|
||||
l = newhook(arg=3)
|
||||
assert l == [5, 4]
|
||||
l2 = hook_hello(arg=3)
|
||||
|
|
|
@ -225,6 +225,12 @@ class TestWCSvnCommandPath(CommonSvnTests):
|
|||
'''
|
||||
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):
|
||||
p = self.root / 'anotherfile'
|
||||
out = p.diff(rev=2)
|
||||
|
|
|
@ -671,6 +671,10 @@ class XMLWCStatus(WCStatus):
|
|||
wcpath = rootwcpath.join(path, abs=1)
|
||||
rootstatus.ignored.append(wcpath)
|
||||
continue
|
||||
elif itemstatus == 'incomplete':
|
||||
wcpath = rootwcpath.join(path, abs=1)
|
||||
rootstatus.incomplete.append(wcpath)
|
||||
continue
|
||||
|
||||
rev = statusel.getAttribute('revision')
|
||||
if itemstatus == 'added' or itemstatus == 'none':
|
||||
|
|
|
@ -395,7 +395,12 @@ class Directory(FSCollector):
|
|||
|
||||
def _ignore(self, 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):
|
||||
if self._ignore(path):
|
||||
|
|
|
@ -77,8 +77,8 @@ class DSession(Session):
|
|||
self.item2nodes = {}
|
||||
super(DSession, self).__init__(config=config)
|
||||
|
||||
#def pytest_configure(self, __call__, config):
|
||||
# __call__.execute()
|
||||
#def pytest_configure(self, __multicall__, config):
|
||||
# __multicall__.execute()
|
||||
# try:
|
||||
# config.getxspecs()
|
||||
# except config.Error:
|
||||
|
|
|
@ -15,10 +15,15 @@ def pytest_funcarg__testdir(request):
|
|||
# testdir.plugins.append(obj.testplugin)
|
||||
# break
|
||||
#else:
|
||||
basename = request.module.__name__.split(".")[-1]
|
||||
if basename.startswith("pytest_"):
|
||||
modname = request.module.__name__.split(".")[-1]
|
||||
if modname.startswith("pytest_"):
|
||||
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:
|
||||
pass # raise ValueError("need better support code")
|
||||
return testdir
|
||||
|
|
|
@ -47,7 +47,7 @@ class HookRecorder:
|
|||
recorder = RecordCalls()
|
||||
self._recorders[hookspecs] = 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):
|
||||
for recorder in self._recorders.values():
|
||||
|
|
|
@ -181,11 +181,11 @@ class CaptureManager:
|
|||
capfuncarg._finalize()
|
||||
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)
|
||||
self.resumecapture(method)
|
||||
try:
|
||||
rep = __call__.execute(firstresult=True)
|
||||
rep = __multicall__.execute()
|
||||
finally:
|
||||
outerr = self.suspendcapture()
|
||||
addouterr(rep, outerr)
|
||||
|
@ -204,11 +204,11 @@ class CaptureManager:
|
|||
def pytest_runtest_teardown(self, 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)
|
||||
self.resumecapture(method)
|
||||
try:
|
||||
rep = __call__.execute(firstresult=True)
|
||||
rep = __multicall__.execute()
|
||||
finally:
|
||||
outerr = self.suspendcapture()
|
||||
if rep:
|
||||
|
@ -219,9 +219,9 @@ class CaptureManager:
|
|||
if hasattr(self, '_capturing'):
|
||||
self.suspendcapture()
|
||||
|
||||
def pytest_runtest_makereport(self, __call__, item, call):
|
||||
def pytest_runtest_makereport(self, __multicall__, item, call):
|
||||
self.deactivate_funcargs()
|
||||
rep = __call__.execute(firstresult=True)
|
||||
rep = __multicall__.execute()
|
||||
outerr = self.suspendcapture()
|
||||
outerr = (item.outerr[0] + outerr[0], item.outerr[1] + outerr[1])
|
||||
if not rep.passed:
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
""" default hooks and general py.test options. """
|
||||
|
||||
import sys
|
||||
import py
|
||||
|
||||
def pytest_pyfunc_call(__call__, pyfuncitem):
|
||||
if not __call__.execute(firstresult=True):
|
||||
def pytest_pyfunc_call(__multicall__, pyfuncitem):
|
||||
if not __multicall__.execute():
|
||||
testfunction = pyfuncitem.obj
|
||||
if pyfuncitem._isyieldedfunction():
|
||||
testfunction(*pyfuncitem._args)
|
||||
|
@ -90,10 +91,17 @@ def pytest_addoption(parser):
|
|||
help="shortcut for '--dist=load --tx=NUM*popen'")
|
||||
group.addoption('--rsyncdir', action="append", default=[], metavar="dir1",
|
||||
help="add directory for rsyncing to remote tx nodes.")
|
||||
group.addoption('--version', action="store_true",
|
||||
help="display version information")
|
||||
|
||||
def pytest_configure(config):
|
||||
fixoptions(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)
|
||||
|
||||
def fixoptions(config):
|
||||
|
|
|
@ -32,10 +32,10 @@ class Execnetcleanup:
|
|||
#for gw in l:
|
||||
# gw.join()
|
||||
|
||||
def pytest_pyfunc_call(self, __call__, pyfuncitem):
|
||||
def pytest_pyfunc_call(self, __multicall__, pyfuncitem):
|
||||
if self._gateways is not None:
|
||||
gateways = self._gateways[:]
|
||||
res = __call__.execute(firstresult=True)
|
||||
res = __multicall__.execute()
|
||||
while len(self._gateways) > len(gateways):
|
||||
self._gateways[-1].exit()
|
||||
return res
|
||||
|
|
|
@ -8,14 +8,27 @@ def pytest_addoption(parser):
|
|||
def pytest_configure(config):
|
||||
hooklog = config.getvalue("hooklog")
|
||||
if hooklog:
|
||||
assert not config.pluginmanager.comregistry.logfile
|
||||
config.pluginmanager.comregistry.logfile = open(hooklog, 'w')
|
||||
config._hooklogfile = open(hooklog, 'w', 0)
|
||||
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):
|
||||
f = config.pluginmanager.comregistry.logfile
|
||||
if f:
|
||||
f.close()
|
||||
config.pluginmanager.comregistry.logfile = None
|
||||
try:
|
||||
del config.hook.__dict__['_performcall']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# ===============================================================================
|
||||
# plugin tests
|
||||
|
|
|
@ -4,7 +4,7 @@ safely patch object attributes, dicts and environment variables.
|
|||
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
|
||||
to set the environment variable ``ENV1`` and patch the
|
||||
``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
|
||||
old state. After the test function finished execution all
|
||||
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/
|
||||
"""
|
||||
|
@ -57,8 +66,11 @@ class MonkeyPatch:
|
|||
self._setitem.insert(0, (dictionary, name, dictionary.get(name, notset)))
|
||||
dictionary[name] = value
|
||||
|
||||
def setenv(self, name, value):
|
||||
self.setitem(os.environ, name, str(value))
|
||||
def setenv(self, name, value, prepend=None):
|
||||
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):
|
||||
for obj, name, value in self._setattr:
|
||||
|
@ -111,6 +123,16 @@ def test_setenv():
|
|||
monkeypatch.finalize()
|
||||
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):
|
||||
reprec = testdir.inline_runsource("""
|
||||
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'],
|
||||
help="send failed|all info to Pocoo pastebin service.")
|
||||
|
||||
def pytest_configure(__call__, config):
|
||||
def pytest_configure(__multicall__, config):
|
||||
import tempfile
|
||||
__call__.execute()
|
||||
__multicall__.execute()
|
||||
if config.option.pastebin == "all":
|
||||
config._pastebinfile = tempfile.TemporaryFile()
|
||||
tr = config.pluginmanager.impname2plugin['terminalreporter']
|
||||
|
|
|
@ -241,22 +241,17 @@ class TerminalReporter:
|
|||
if self.config.option.traceconfig:
|
||||
plugins = []
|
||||
for plugin in self.config.pluginmanager.comregistry:
|
||||
name = plugin.__class__.__name__
|
||||
if name.endswith("Plugin"):
|
||||
name = name[:-6]
|
||||
#if name == "Conftest":
|
||||
# XXX get filename
|
||||
plugins.append(name)
|
||||
else:
|
||||
plugins.append(str(plugin))
|
||||
|
||||
name = getattr(plugin, '__name__', None)
|
||||
if name is None:
|
||||
name = plugin.__class__.__name__
|
||||
plugins.append(name)
|
||||
plugins = ", ".join(plugins)
|
||||
self.write_line("active plugins: %s" %(plugins,))
|
||||
for i, testarg in py.builtin.enumerate(self.config.args):
|
||||
self.write_line("test object %d: %s" %(i+1, testarg))
|
||||
|
||||
def pytest_sessionfinish(self, __call__, session, exitstatus):
|
||||
__call__.execute()
|
||||
def pytest_sessionfinish(self, exitstatus, __multicall__):
|
||||
__multicall__.execute()
|
||||
self._tw.line("")
|
||||
if exitstatus in (0, 1, 2):
|
||||
self.summary_errors()
|
||||
|
|
|
@ -55,6 +55,9 @@ class UnitTestFunction(py.test.collect.Function):
|
|||
if obj is not _dummy:
|
||||
self._obj = obj
|
||||
self._sort_value = sort_value
|
||||
if hasattr(self.parent, 'newinstance'):
|
||||
self.parent.newinstance()
|
||||
self.obj = self._getobj()
|
||||
|
||||
def runtest(self):
|
||||
target = self.obj
|
||||
|
@ -87,7 +90,6 @@ def test_simple_unittest(testdir):
|
|||
def test_setup(testdir):
|
||||
testpath = testdir.makepyfile(test_two="""
|
||||
import unittest
|
||||
pytest_plugins = "pytest_unittest" # XXX
|
||||
class MyTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.foo = 1
|
||||
|
@ -98,6 +100,18 @@ def test_setup(testdir):
|
|||
rep = reprec.matchreport("test_setUp")
|
||||
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):
|
||||
testpath = testdir.makepyfile(test_three="""
|
||||
import unittest
|
||||
|
|
|
@ -19,14 +19,12 @@ when it fails. Instead terminal reporting will list it in the
|
|||
|
||||
import py
|
||||
|
||||
pytest_plugins = ['keyword']
|
||||
|
||||
def pytest_runtest_makereport(__call__, item, call):
|
||||
def pytest_runtest_makereport(__multicall__, item, call):
|
||||
if call.when != "call":
|
||||
return
|
||||
if hasattr(item, 'obj') and hasattr(item.obj, 'func_dict'):
|
||||
if 'xfail' in item.obj.func_dict:
|
||||
res = __call__.execute(firstresult=True)
|
||||
res = __multicall__.execute()
|
||||
if call.excinfo:
|
||||
res.skipped = True
|
||||
res.failed = res.passed = False
|
||||
|
@ -53,6 +51,9 @@ def pytest_terminal_summary(terminalreporter):
|
|||
modpath = rep.item.getmodpath(includemodule=True)
|
||||
pos = "%s %s:%d: " %(modpath, entry.path, entry.lineno)
|
||||
reason = rep.longrepr.reprcrash.message
|
||||
i = reason.find("\n")
|
||||
if i != -1:
|
||||
reason = reason[:i]
|
||||
tr._tw.line("%s %s" %(pos, reason))
|
||||
|
||||
xpassed = terminalreporter.stats.get("xpassed")
|
||||
|
@ -89,3 +90,4 @@ def test_xfail(testdir):
|
|||
"*test_that*",
|
||||
])
|
||||
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:
|
||||
comregistry = py._com.Registry()
|
||||
self.comregistry = comregistry
|
||||
self.MultiCall = self.comregistry.MultiCall
|
||||
self.impname2plugin = {}
|
||||
|
||||
self.hook = py._com.Hooks(
|
||||
self.hook = py._com.HookRelay(
|
||||
hookspecs=hookspec,
|
||||
registry=self.comregistry)
|
||||
|
||||
|
@ -135,15 +134,16 @@ class PluginManager(object):
|
|||
fail = True
|
||||
else:
|
||||
method_args = getargs(method)
|
||||
if '__call__' in method_args:
|
||||
method_args.remove('__call__')
|
||||
if '__multicall__' in method_args:
|
||||
method_args.remove('__multicall__')
|
||||
hook = hooks[name]
|
||||
hookargs = getargs(hook)
|
||||
for arg, hookarg in zip(method_args, hookargs):
|
||||
if arg != hookarg:
|
||||
Print("argument mismatch: %r != %r" %(arg, hookarg))
|
||||
Print("actual : %s" %(formatdef(method)))
|
||||
Print("required:", formatdef(hook))
|
||||
for arg in method_args:
|
||||
if arg not in hookargs:
|
||||
Print("argument %r not available" %(arg, ))
|
||||
Print("actual definition: %s" %(formatdef(method)))
|
||||
Print("available hook arguments: %s" %
|
||||
", ".join(hookargs))
|
||||
fail = True
|
||||
break
|
||||
#if not fail:
|
||||
|
@ -166,20 +166,24 @@ class PluginManager(object):
|
|||
return self.hook.pytest_internalerror(excrepr=excrepr)
|
||||
|
||||
def do_addoption(self, parser):
|
||||
methods = self.comregistry.listattr("pytest_addoption", reverse=True)
|
||||
mc = py._com.MultiCall(methods, parser=parser)
|
||||
mname = "pytest_addoption"
|
||||
methods = self.comregistry.listattr(mname, reverse=True)
|
||||
mc = py._com.MultiCall(methods, {'parser': parser})
|
||||
mc.execute()
|
||||
|
||||
def pytest_plugin_registered(self, plugin):
|
||||
if hasattr(self, '_config'):
|
||||
self.call_plugin(plugin, "pytest_addoption", parser=self._config._parser)
|
||||
self.call_plugin(plugin, "pytest_configure", config=self._config)
|
||||
self.call_plugin(plugin, "pytest_addoption",
|
||||
{'parser': self._config._parser})
|
||||
self.call_plugin(plugin, "pytest_configure",
|
||||
{'config': self._config})
|
||||
#dic = self.call_plugin(plugin, "pytest_namespace")
|
||||
#self._updateext(dic)
|
||||
|
||||
def call_plugin(self, plugin, methname, **kwargs):
|
||||
return self.MultiCall(self.listattr(methname, plugins=[plugin]),
|
||||
**kwargs).execute(firstresult=True)
|
||||
def call_plugin(self, plugin, methname, kwargs):
|
||||
return py._com.MultiCall(
|
||||
methods=self.listattr(methname, plugins=[plugin]),
|
||||
kwargs=kwargs, firstresult=True).execute()
|
||||
|
||||
def _updateext(self, dic):
|
||||
if dic:
|
||||
|
@ -200,9 +204,6 @@ class PluginManager(object):
|
|||
config.hook.pytest_unconfigure(config=config)
|
||||
config.pluginmanager.unregister(self)
|
||||
|
||||
class Ext:
|
||||
""" namespace for extension objects. """
|
||||
|
||||
#
|
||||
# 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
|
||||
metafunc = funcargs.Metafunc(funcobj, config=self.config,
|
||||
cls=cls, module=module)
|
||||
gentesthook = self.config.hook.pytest_generate_tests.clone(
|
||||
extralookup=module)
|
||||
gentesthook = self.config.hook._makecall(
|
||||
"pytest_generate_tests", extralookup=module)
|
||||
gentesthook(metafunc=metafunc)
|
||||
if not metafunc._calls:
|
||||
return self.Function(name, parent=self)
|
||||
|
|
|
@ -3,6 +3,14 @@ import py
|
|||
EXPECTTIMEOUT=10.0
|
||||
|
||||
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):
|
||||
testdir.makeconftest("""
|
||||
def pytest_configure(config):
|
||||
|
|
|
@ -145,7 +145,7 @@ class TestCollectFS:
|
|||
names = [x.name for x in col.collect()]
|
||||
assert names == ["dir1", "dir2", "test_one.py", "test_two.py", "x"]
|
||||
|
||||
class TestCollectPluginHooks:
|
||||
class TestCollectPluginHookRelay:
|
||||
def test_pytest_collect_file(self, testdir):
|
||||
tmpdir = testdir.tmpdir
|
||||
wascalled = []
|
||||
|
|
|
@ -4,7 +4,7 @@ from py.__.test.pluginmanager import PluginManager, canonical_importname, collec
|
|||
class TestBootstrapping:
|
||||
def test_consider_env_fails_to_import(self, monkeypatch):
|
||||
pluginmanager = PluginManager()
|
||||
monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'nonexistingmodule')
|
||||
monkeypatch.setenv('PYTEST_PLUGINS', 'nonexisting', prepend=",")
|
||||
py.test.raises(ImportError, "pluginmanager.consider_env()")
|
||||
|
||||
def test_preparse_args(self):
|
||||
|
@ -50,7 +50,7 @@ class TestBootstrapping:
|
|||
plugin = py.test.config.pluginmanager.getplugin('x500')
|
||||
assert plugin is not None
|
||||
""")
|
||||
monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'pytest_x500')
|
||||
monkeypatch.setenv('PYTEST_PLUGINS', 'pytest_x500', prepend=",")
|
||||
result = testdir.runpytest(p)
|
||||
assert result.ret == 0
|
||||
extra = result.stdout.fnmatch_lines(["*1 passed in*"])
|
||||
|
@ -185,7 +185,7 @@ class TestPytestPluginInteractions:
|
|||
assert hello == "world"
|
||||
""")
|
||||
result = testdir.runpytest(p)
|
||||
assert result.stdout.fnmatch_lines([
|
||||
result.stdout.fnmatch_lines([
|
||||
"*1 passed*"
|
||||
])
|
||||
|
||||
|
@ -222,10 +222,6 @@ class TestPytestPluginInteractions:
|
|||
config.pluginmanager.register(A())
|
||||
assert len(l) == 2
|
||||
|
||||
def test_MultiCall(self):
|
||||
pp = PluginManager()
|
||||
assert hasattr(pp, 'MultiCall')
|
||||
|
||||
# lower level API
|
||||
|
||||
def test_listattr(self):
|
||||
|
@ -247,3 +243,12 @@ def test_collectattr():
|
|||
assert list(methods) == ['pytest_hello', 'pytest_world']
|
||||
methods = py.builtin.sorted(collectattr(B()))
|
||||
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