shift all python related testing functioanlity to a dedicated
pytest_python plugin which incorporates pytest's logic of python function testing (including funcargs). --HG-- branch : trunk
This commit is contained in:
parent
e2683f4538
commit
2cf22e3124
|
@ -3,6 +3,7 @@ Changes between 1.3.4 and 1.4.0a1
|
|||
==================================================
|
||||
|
||||
- major refactoring of internal collection handling
|
||||
- majorly reduce py.test core code, shift function/python testing to own plugin
|
||||
- fix issue88 (finding custom test nodes from command line arg)
|
||||
|
||||
Changes between 1.3.3 and 1.3.4
|
||||
|
|
|
@ -45,12 +45,6 @@ py.apipkg.initpkg(__name__, dict(
|
|||
'Directory' : '._test.collect:Directory',
|
||||
'File' : '._test.collect:File',
|
||||
'Item' : '._test.collect:Item',
|
||||
'Module' : '._test.pycollect:Module',
|
||||
'Class' : '._test.pycollect:Class',
|
||||
'Instance' : '._test.pycollect:Instance',
|
||||
'Generator' : '._test.pycollect:Generator',
|
||||
'Function' : '._test.pycollect:Function',
|
||||
'_fillfuncargs' : '._test.funcargs:fillfuncargs',
|
||||
},
|
||||
'cmdline': {
|
||||
'main' : '._test.session:main', # backward compat
|
||||
|
|
|
@ -5,11 +5,6 @@ import py
|
|||
|
||||
def pytest_cmdline_main(config):
|
||||
from py._test.session import Session, Collection
|
||||
exitstatus = 0
|
||||
if config.option.showfuncargs:
|
||||
from py._test.funcargs import showfuncargs
|
||||
session = showfuncargs(config)
|
||||
else:
|
||||
collection = Collection(config)
|
||||
# instantiate session already because it
|
||||
# records failures and implements maxfail handling
|
||||
|
@ -19,31 +14,6 @@ def pytest_cmdline_main(config):
|
|||
exitstatus = session.main()
|
||||
return exitstatus
|
||||
|
||||
def pytest_pyfunc_call(__multicall__, pyfuncitem):
|
||||
if not __multicall__.execute():
|
||||
testfunction = pyfuncitem.obj
|
||||
if pyfuncitem._isyieldedfunction():
|
||||
testfunction(*pyfuncitem._args)
|
||||
else:
|
||||
funcargs = pyfuncitem.funcargs
|
||||
testfunction(**funcargs)
|
||||
|
||||
def pytest_collect_file(path, parent):
|
||||
ext = path.ext
|
||||
pb = path.purebasename
|
||||
if pb.startswith("test_") or pb.endswith("_test") or \
|
||||
path in parent.collection._argfspaths:
|
||||
if ext == ".py":
|
||||
return parent.ihook.pytest_pycollect_makemodule(
|
||||
path=path, parent=parent)
|
||||
|
||||
def pytest_pycollect_makemodule(path, parent):
|
||||
return parent.Module(path, parent)
|
||||
|
||||
def pytest_funcarg__pytestconfig(request):
|
||||
""" the pytest config object with access to command line opts."""
|
||||
return request.config
|
||||
|
||||
def pytest_ignore_collect(path, config):
|
||||
ignore_paths = config.getconftest_pathlist("collect_ignore", path=path)
|
||||
ignore_paths = ignore_paths or []
|
||||
|
@ -57,7 +27,6 @@ def pytest_ignore_collect(path, config):
|
|||
if path == p or path.relto(p):
|
||||
return True
|
||||
|
||||
|
||||
def pytest_collect_directory(path, parent):
|
||||
# XXX reconsider the following comment
|
||||
# not use parent.Directory here as we generally
|
||||
|
@ -105,30 +74,3 @@ def pytest_configure(config):
|
|||
if config.getvalue("exitfirst"):
|
||||
config.option.maxfail = 1
|
||||
|
||||
# pycollect related hooks and code, should move to pytest_pycollect.py
|
||||
|
||||
def pytest_pycollect_makeitem(__multicall__, collector, name, obj):
|
||||
res = __multicall__.execute()
|
||||
if res is not None:
|
||||
return res
|
||||
if collector._istestclasscandidate(name, obj):
|
||||
res = collector._deprecated_join(name)
|
||||
if res is not None:
|
||||
return res
|
||||
return collector.Class(name, parent=collector)
|
||||
elif collector.funcnamefilter(name) and hasattr(obj, '__call__'):
|
||||
res = collector._deprecated_join(name)
|
||||
if res is not None:
|
||||
return res
|
||||
if is_generator(obj):
|
||||
# XXX deprecation warning
|
||||
return collector.Generator(name, parent=collector)
|
||||
else:
|
||||
return collector._genfunctions(name, obj)
|
||||
|
||||
def is_generator(func):
|
||||
try:
|
||||
return py.code.getrawcode(func).co_flags & 32 # generator function
|
||||
except AttributeError: # builtin functions have no bytecode
|
||||
# assume them to not be generators
|
||||
return False
|
||||
|
|
|
@ -5,9 +5,80 @@ import py
|
|||
import inspect
|
||||
import sys
|
||||
from py._test.collect import configproperty, warnoldcollect
|
||||
from py._test import funcargs
|
||||
from py._code.code import TerminalRepr
|
||||
|
||||
def pytest_addoption(parser):
|
||||
group = parser.getgroup("terminal reporting")
|
||||
group._addoption('--funcargs',
|
||||
action="store_true", dest="showfuncargs", default=False,
|
||||
help="show available function arguments, sorted by plugin")
|
||||
|
||||
def pytest_cmdline_main(config):
|
||||
if config.option.showfuncargs:
|
||||
showfuncargs(config)
|
||||
return 0
|
||||
|
||||
def pytest_namespace():
|
||||
# XXX rather return than set directly
|
||||
py.test.collect.Module = Module
|
||||
py.test.collect.Class = Class
|
||||
py.test.collect.Instance = Instance
|
||||
py.test.collect.Function = Function
|
||||
py.test.collect.Generator = Generator
|
||||
py.test.collect._fillfuncargs = fillfuncargs
|
||||
|
||||
def pytest_funcarg__pytestconfig(request):
|
||||
""" the pytest config object with access to command line opts."""
|
||||
return request.config
|
||||
|
||||
def pytest_pyfunc_call(__multicall__, pyfuncitem):
|
||||
if not __multicall__.execute():
|
||||
testfunction = pyfuncitem.obj
|
||||
if pyfuncitem._isyieldedfunction():
|
||||
testfunction(*pyfuncitem._args)
|
||||
else:
|
||||
funcargs = pyfuncitem.funcargs
|
||||
testfunction(**funcargs)
|
||||
|
||||
def pytest_collect_file(path, parent):
|
||||
ext = path.ext
|
||||
pb = path.purebasename
|
||||
if pb.startswith("test_") or pb.endswith("_test") or \
|
||||
path in parent.collection._argfspaths:
|
||||
if ext == ".py":
|
||||
return parent.ihook.pytest_pycollect_makemodule(
|
||||
path=path, parent=parent)
|
||||
|
||||
def pytest_pycollect_makemodule(path, parent):
|
||||
return parent.Module(path, parent)
|
||||
|
||||
|
||||
def pytest_pycollect_makeitem(__multicall__, collector, name, obj):
|
||||
res = __multicall__.execute()
|
||||
if res is not None:
|
||||
return res
|
||||
if collector._istestclasscandidate(name, obj):
|
||||
res = collector._deprecated_join(name)
|
||||
if res is not None:
|
||||
return res
|
||||
return collector.Class(name, parent=collector)
|
||||
elif collector.funcnamefilter(name) and hasattr(obj, '__call__'):
|
||||
res = collector._deprecated_join(name)
|
||||
if res is not None:
|
||||
return res
|
||||
if is_generator(obj):
|
||||
# XXX deprecation warning
|
||||
return collector.Generator(name, parent=collector)
|
||||
else:
|
||||
return collector._genfunctions(name, obj)
|
||||
|
||||
def is_generator(func):
|
||||
try:
|
||||
return py.code.getrawcode(func).co_flags & 32 # generator function
|
||||
except AttributeError: # builtin functions have no bytecode
|
||||
# assume them to not be generators
|
||||
return False
|
||||
|
||||
class PyobjMixin(object):
|
||||
def obj():
|
||||
def fget(self):
|
||||
|
@ -120,10 +191,10 @@ class PyCollectorMixin(PyobjMixin, py.test.collect.Collector):
|
|||
module = self.getparent(Module).obj
|
||||
clscol = self.getparent(Class)
|
||||
cls = clscol and clscol.obj or None
|
||||
metafunc = funcargs.Metafunc(funcobj, config=self.config,
|
||||
metafunc = Metafunc(funcobj, config=self.config,
|
||||
cls=cls, module=module)
|
||||
gentesthook = self.config.hook.pytest_generate_tests
|
||||
plugins = funcargs.getplugins(self, withpy=True)
|
||||
plugins = getplugins(self, withpy=True)
|
||||
gentesthook.pcall(plugins, metafunc=metafunc)
|
||||
if not metafunc._calls:
|
||||
return self.Function(name, parent=self)
|
||||
|
@ -212,16 +283,9 @@ class Class(PyCollectorMixin, py.test.collect.Collector):
|
|||
class Instance(PyCollectorMixin, py.test.collect.Collector):
|
||||
def _getobj(self):
|
||||
return self.parent.obj()
|
||||
def Function(self):
|
||||
return getattr(self.obj, 'Function',
|
||||
PyCollectorMixin.Function.__get__(self)) # XXX for python 2.2
|
||||
|
||||
def _keywords(self):
|
||||
return []
|
||||
Function = property(Function)
|
||||
|
||||
#def __repr__(self):
|
||||
# return "<%s of '%s'>" %(self.__class__.__name__,
|
||||
# self.parent.obj.__name__)
|
||||
|
||||
def newinstance(self):
|
||||
self.obj = self._getobj()
|
||||
|
@ -270,7 +334,7 @@ class FunctionMixin(PyobjMixin):
|
|||
return traceback
|
||||
|
||||
def _repr_failure_py(self, excinfo, style="long"):
|
||||
if excinfo.errisinstance(funcargs.FuncargRequest.LookupError):
|
||||
if excinfo.errisinstance(FuncargRequest.LookupError):
|
||||
fspath, lineno, msg = self.reportinfo()
|
||||
lines, _ = inspect.getsourcelines(self.obj)
|
||||
for i, line in enumerate(lines):
|
||||
|
@ -384,7 +448,7 @@ class Function(FunctionMixin, py.test.collect.Item):
|
|||
def setup(self):
|
||||
super(Function, self).setup()
|
||||
if hasattr(self, 'funcargs'):
|
||||
funcargs.fillfuncargs(self)
|
||||
fillfuncargs(self)
|
||||
|
||||
def __eq__(self, other):
|
||||
try:
|
||||
|
@ -410,3 +474,229 @@ def hasinit(obj):
|
|||
if init:
|
||||
if init != object.__init__:
|
||||
return True
|
||||
|
||||
|
||||
def getfuncargnames(function):
|
||||
argnames = py.std.inspect.getargs(py.code.getrawcode(function))[0]
|
||||
startindex = py.std.inspect.ismethod(function) and 1 or 0
|
||||
defaults = getattr(function, 'func_defaults',
|
||||
getattr(function, '__defaults__', None)) or ()
|
||||
numdefaults = len(defaults)
|
||||
if numdefaults:
|
||||
return argnames[startindex:-numdefaults]
|
||||
return argnames[startindex:]
|
||||
|
||||
def fillfuncargs(function):
|
||||
""" fill missing funcargs. """
|
||||
request = FuncargRequest(pyfuncitem=function)
|
||||
request._fillfuncargs()
|
||||
|
||||
def getplugins(node, withpy=False): # might by any node
|
||||
plugins = node.config._getmatchingplugins(node.fspath)
|
||||
if withpy:
|
||||
mod = node.getparent(py.test.collect.Module)
|
||||
if mod is not None:
|
||||
plugins.append(mod.obj)
|
||||
inst = node.getparent(py.test.collect.Instance)
|
||||
if inst is not None:
|
||||
plugins.append(inst.obj)
|
||||
return plugins
|
||||
|
||||
_notexists = object()
|
||||
class CallSpec:
|
||||
def __init__(self, funcargs, id, param):
|
||||
self.funcargs = funcargs
|
||||
self.id = id
|
||||
if param is not _notexists:
|
||||
self.param = param
|
||||
def __repr__(self):
|
||||
return "<CallSpec id=%r param=%r funcargs=%r>" %(
|
||||
self.id, getattr(self, 'param', '?'), self.funcargs)
|
||||
|
||||
class Metafunc:
|
||||
def __init__(self, function, config=None, cls=None, module=None):
|
||||
self.config = config
|
||||
self.module = module
|
||||
self.function = function
|
||||
self.funcargnames = getfuncargnames(function)
|
||||
self.cls = cls
|
||||
self.module = module
|
||||
self._calls = []
|
||||
self._ids = py.builtin.set()
|
||||
|
||||
def addcall(self, funcargs=None, id=_notexists, param=_notexists):
|
||||
assert funcargs is None or isinstance(funcargs, dict)
|
||||
if id is None:
|
||||
raise ValueError("id=None not allowed")
|
||||
if id is _notexists:
|
||||
id = len(self._calls)
|
||||
id = str(id)
|
||||
if id in self._ids:
|
||||
raise ValueError("duplicate id %r" % id)
|
||||
self._ids.add(id)
|
||||
self._calls.append(CallSpec(funcargs, id, param))
|
||||
|
||||
class FuncargRequest:
|
||||
_argprefix = "pytest_funcarg__"
|
||||
_argname = None
|
||||
|
||||
class LookupError(LookupError):
|
||||
""" error on performing funcarg request. """
|
||||
|
||||
def __init__(self, pyfuncitem):
|
||||
self._pyfuncitem = pyfuncitem
|
||||
self.function = pyfuncitem.obj
|
||||
self.module = pyfuncitem.getparent(py.test.collect.Module).obj
|
||||
clscol = pyfuncitem.getparent(py.test.collect.Class)
|
||||
self.cls = clscol and clscol.obj or None
|
||||
self.instance = py.builtin._getimself(self.function)
|
||||
self.config = pyfuncitem.config
|
||||
self.fspath = pyfuncitem.fspath
|
||||
if hasattr(pyfuncitem, '_requestparam'):
|
||||
self.param = pyfuncitem._requestparam
|
||||
self._plugins = getplugins(pyfuncitem, withpy=True)
|
||||
self._funcargs = self._pyfuncitem.funcargs.copy()
|
||||
self._name2factory = {}
|
||||
self._currentarg = None
|
||||
|
||||
def _fillfuncargs(self):
|
||||
argnames = getfuncargnames(self.function)
|
||||
if argnames:
|
||||
assert not getattr(self._pyfuncitem, '_args', None), (
|
||||
"yielded functions cannot have funcargs")
|
||||
for argname in argnames:
|
||||
if argname not in self._pyfuncitem.funcargs:
|
||||
self._pyfuncitem.funcargs[argname] = self.getfuncargvalue(argname)
|
||||
|
||||
|
||||
def applymarker(self, marker):
|
||||
""" apply a marker to a test function invocation.
|
||||
|
||||
The 'marker' must be created with py.test.mark.* XYZ.
|
||||
"""
|
||||
if not isinstance(marker, py.test.mark.XYZ.__class__):
|
||||
raise ValueError("%r is not a py.test.mark.* object")
|
||||
self._pyfuncitem.keywords[marker.markname] = marker
|
||||
|
||||
def cached_setup(self, setup, teardown=None, scope="module", extrakey=None):
|
||||
""" cache and return result of calling setup().
|
||||
|
||||
The requested argument name, the scope and the ``extrakey``
|
||||
determine the cache key. The scope also determines when
|
||||
teardown(result) will be called. valid scopes are:
|
||||
scope == 'function': when the single test function run finishes.
|
||||
scope == 'module': when tests in a different module are run
|
||||
scope == 'session': when tests of the session have run.
|
||||
"""
|
||||
if not hasattr(self.config, '_setupcache'):
|
||||
self.config._setupcache = {} # XXX weakref?
|
||||
cachekey = (self._currentarg, self._getscopeitem(scope), extrakey)
|
||||
cache = self.config._setupcache
|
||||
try:
|
||||
val = cache[cachekey]
|
||||
except KeyError:
|
||||
val = setup()
|
||||
cache[cachekey] = val
|
||||
if teardown is not None:
|
||||
def finalizer():
|
||||
del cache[cachekey]
|
||||
teardown(val)
|
||||
self._addfinalizer(finalizer, scope=scope)
|
||||
return val
|
||||
|
||||
def getfuncargvalue(self, argname):
|
||||
try:
|
||||
return self._funcargs[argname]
|
||||
except KeyError:
|
||||
pass
|
||||
if argname not in self._name2factory:
|
||||
self._name2factory[argname] = self.config.pluginmanager.listattr(
|
||||
plugins=self._plugins,
|
||||
attrname=self._argprefix + str(argname)
|
||||
)
|
||||
#else: we are called recursively
|
||||
if not self._name2factory[argname]:
|
||||
self._raiselookupfailed(argname)
|
||||
funcargfactory = self._name2factory[argname].pop()
|
||||
oldarg = self._currentarg
|
||||
self._currentarg = argname
|
||||
try:
|
||||
self._funcargs[argname] = res = funcargfactory(request=self)
|
||||
finally:
|
||||
self._currentarg = oldarg
|
||||
return res
|
||||
|
||||
def _getscopeitem(self, scope):
|
||||
if scope == "function":
|
||||
return self._pyfuncitem
|
||||
elif scope == "module":
|
||||
return self._pyfuncitem.getparent(py.test.collect.Module)
|
||||
elif scope == "session":
|
||||
return None
|
||||
raise ValueError("unknown finalization scope %r" %(scope,))
|
||||
|
||||
def _addfinalizer(self, finalizer, scope):
|
||||
colitem = self._getscopeitem(scope)
|
||||
self.config._setupstate.addfinalizer(
|
||||
finalizer=finalizer, colitem=colitem)
|
||||
|
||||
def addfinalizer(self, finalizer):
|
||||
""" call the given finalizer after test function finished execution. """
|
||||
self._addfinalizer(finalizer, scope="function")
|
||||
|
||||
def __repr__(self):
|
||||
return "<FuncargRequest for %r>" %(self._pyfuncitem)
|
||||
|
||||
def _raiselookupfailed(self, argname):
|
||||
available = []
|
||||
for plugin in self._plugins:
|
||||
for name in vars(plugin):
|
||||
if name.startswith(self._argprefix):
|
||||
name = name[len(self._argprefix):]
|
||||
if name not in available:
|
||||
available.append(name)
|
||||
fspath, lineno, msg = self._pyfuncitem.reportinfo()
|
||||
msg = "LookupError: no factory found for function argument %r" % (argname,)
|
||||
msg += "\n available funcargs: %s" %(", ".join(available),)
|
||||
msg += "\n use 'py.test --funcargs [testpath]' for help on them."
|
||||
raise self.LookupError(msg)
|
||||
|
||||
def showfuncargs(config):
|
||||
from py._test.session import Collection
|
||||
collection = Collection(config)
|
||||
colitem = collection.getinitialnodes()[0]
|
||||
curdir = py.path.local()
|
||||
tw = py.io.TerminalWriter()
|
||||
plugins = getplugins(colitem, withpy=True)
|
||||
verbose = config.getvalue("verbose")
|
||||
for plugin in plugins:
|
||||
available = []
|
||||
for name, factory in vars(plugin).items():
|
||||
if name.startswith(FuncargRequest._argprefix):
|
||||
name = name[len(FuncargRequest._argprefix):]
|
||||
if name not in available:
|
||||
available.append([name, factory])
|
||||
if available:
|
||||
pluginname = plugin.__name__
|
||||
for name, factory in available:
|
||||
loc = getlocation(factory, curdir)
|
||||
if verbose:
|
||||
funcargspec = "%s -- %s" %(name, loc,)
|
||||
else:
|
||||
funcargspec = name
|
||||
tw.line(funcargspec, green=True)
|
||||
doc = factory.__doc__ or ""
|
||||
if doc:
|
||||
for line in doc.split("\n"):
|
||||
tw.line(" " + line.strip())
|
||||
else:
|
||||
tw.line(" %s: no docstring available" %(loc,),
|
||||
red=True)
|
||||
|
||||
def getlocation(function, curdir):
|
||||
import inspect
|
||||
fn = py.path.local(inspect.getfile(function))
|
||||
lineno = py.builtin._getcode(function).co_firstlineno
|
||||
if fn.relto(curdir):
|
||||
fn = fn.relto(curdir)
|
||||
return "%s:%d" %(fn, lineno+1)
|
|
@ -27,9 +27,6 @@ def pytest_addoption(parser):
|
|||
group._addoption('--fulltrace',
|
||||
action="store_true", dest="fulltrace", default=False,
|
||||
help="don't cut any tracebacks (default is to cut).")
|
||||
group._addoption('--funcargs',
|
||||
action="store_true", dest="showfuncargs", default=False,
|
||||
help="show available function arguments, sorted by plugin")
|
||||
|
||||
def pytest_configure(config):
|
||||
if config.option.collectonly:
|
||||
|
|
|
@ -1,228 +0,0 @@
|
|||
import py
|
||||
|
||||
def getfuncargnames(function):
|
||||
argnames = py.std.inspect.getargs(py.code.getrawcode(function))[0]
|
||||
startindex = py.std.inspect.ismethod(function) and 1 or 0
|
||||
defaults = getattr(function, 'func_defaults',
|
||||
getattr(function, '__defaults__', None)) or ()
|
||||
numdefaults = len(defaults)
|
||||
if numdefaults:
|
||||
return argnames[startindex:-numdefaults]
|
||||
return argnames[startindex:]
|
||||
|
||||
def fillfuncargs(function):
|
||||
""" fill missing funcargs. """
|
||||
request = FuncargRequest(pyfuncitem=function)
|
||||
request._fillfuncargs()
|
||||
|
||||
def getplugins(node, withpy=False): # might by any node
|
||||
plugins = node.config._getmatchingplugins(node.fspath)
|
||||
if withpy:
|
||||
mod = node.getparent(py.test.collect.Module)
|
||||
if mod is not None:
|
||||
plugins.append(mod.obj)
|
||||
inst = node.getparent(py.test.collect.Instance)
|
||||
if inst is not None:
|
||||
plugins.append(inst.obj)
|
||||
return plugins
|
||||
|
||||
_notexists = object()
|
||||
class CallSpec:
|
||||
def __init__(self, funcargs, id, param):
|
||||
self.funcargs = funcargs
|
||||
self.id = id
|
||||
if param is not _notexists:
|
||||
self.param = param
|
||||
def __repr__(self):
|
||||
return "<CallSpec id=%r param=%r funcargs=%r>" %(
|
||||
self.id, getattr(self, 'param', '?'), self.funcargs)
|
||||
|
||||
class Metafunc:
|
||||
def __init__(self, function, config=None, cls=None, module=None):
|
||||
self.config = config
|
||||
self.module = module
|
||||
self.function = function
|
||||
self.funcargnames = getfuncargnames(function)
|
||||
self.cls = cls
|
||||
self.module = module
|
||||
self._calls = []
|
||||
self._ids = py.builtin.set()
|
||||
|
||||
def addcall(self, funcargs=None, id=_notexists, param=_notexists):
|
||||
assert funcargs is None or isinstance(funcargs, dict)
|
||||
if id is None:
|
||||
raise ValueError("id=None not allowed")
|
||||
if id is _notexists:
|
||||
id = len(self._calls)
|
||||
id = str(id)
|
||||
if id in self._ids:
|
||||
raise ValueError("duplicate id %r" % id)
|
||||
self._ids.add(id)
|
||||
self._calls.append(CallSpec(funcargs, id, param))
|
||||
|
||||
class FuncargRequest:
|
||||
_argprefix = "pytest_funcarg__"
|
||||
_argname = None
|
||||
|
||||
class LookupError(LookupError):
|
||||
""" error on performing funcarg request. """
|
||||
|
||||
def __init__(self, pyfuncitem):
|
||||
self._pyfuncitem = pyfuncitem
|
||||
self.function = pyfuncitem.obj
|
||||
self.module = pyfuncitem.getparent(py.test.collect.Module).obj
|
||||
clscol = pyfuncitem.getparent(py.test.collect.Class)
|
||||
self.cls = clscol and clscol.obj or None
|
||||
self.instance = py.builtin._getimself(self.function)
|
||||
self.config = pyfuncitem.config
|
||||
self.fspath = pyfuncitem.fspath
|
||||
if hasattr(pyfuncitem, '_requestparam'):
|
||||
self.param = pyfuncitem._requestparam
|
||||
self._plugins = getplugins(pyfuncitem, withpy=True)
|
||||
self._funcargs = self._pyfuncitem.funcargs.copy()
|
||||
self._name2factory = {}
|
||||
self._currentarg = None
|
||||
|
||||
def _fillfuncargs(self):
|
||||
argnames = getfuncargnames(self.function)
|
||||
if argnames:
|
||||
assert not getattr(self._pyfuncitem, '_args', None), (
|
||||
"yielded functions cannot have funcargs")
|
||||
for argname in argnames:
|
||||
if argname not in self._pyfuncitem.funcargs:
|
||||
self._pyfuncitem.funcargs[argname] = self.getfuncargvalue(argname)
|
||||
|
||||
|
||||
def applymarker(self, marker):
|
||||
""" apply a marker to a test function invocation.
|
||||
|
||||
The 'marker' must be created with py.test.mark.* XYZ.
|
||||
"""
|
||||
if not isinstance(marker, py.test.mark.XYZ.__class__):
|
||||
raise ValueError("%r is not a py.test.mark.* object")
|
||||
self._pyfuncitem.keywords[marker.markname] = marker
|
||||
|
||||
def cached_setup(self, setup, teardown=None, scope="module", extrakey=None):
|
||||
""" cache and return result of calling setup().
|
||||
|
||||
The requested argument name, the scope and the ``extrakey``
|
||||
determine the cache key. The scope also determines when
|
||||
teardown(result) will be called. valid scopes are:
|
||||
scope == 'function': when the single test function run finishes.
|
||||
scope == 'module': when tests in a different module are run
|
||||
scope == 'session': when tests of the session have run.
|
||||
"""
|
||||
if not hasattr(self.config, '_setupcache'):
|
||||
self.config._setupcache = {} # XXX weakref?
|
||||
cachekey = (self._currentarg, self._getscopeitem(scope), extrakey)
|
||||
cache = self.config._setupcache
|
||||
try:
|
||||
val = cache[cachekey]
|
||||
except KeyError:
|
||||
val = setup()
|
||||
cache[cachekey] = val
|
||||
if teardown is not None:
|
||||
def finalizer():
|
||||
del cache[cachekey]
|
||||
teardown(val)
|
||||
self._addfinalizer(finalizer, scope=scope)
|
||||
return val
|
||||
|
||||
def getfuncargvalue(self, argname):
|
||||
try:
|
||||
return self._funcargs[argname]
|
||||
except KeyError:
|
||||
pass
|
||||
if argname not in self._name2factory:
|
||||
self._name2factory[argname] = self.config.pluginmanager.listattr(
|
||||
plugins=self._plugins,
|
||||
attrname=self._argprefix + str(argname)
|
||||
)
|
||||
#else: we are called recursively
|
||||
if not self._name2factory[argname]:
|
||||
self._raiselookupfailed(argname)
|
||||
funcargfactory = self._name2factory[argname].pop()
|
||||
oldarg = self._currentarg
|
||||
self._currentarg = argname
|
||||
try:
|
||||
self._funcargs[argname] = res = funcargfactory(request=self)
|
||||
finally:
|
||||
self._currentarg = oldarg
|
||||
return res
|
||||
|
||||
def _getscopeitem(self, scope):
|
||||
if scope == "function":
|
||||
return self._pyfuncitem
|
||||
elif scope == "module":
|
||||
return self._pyfuncitem.getparent(py.test.collect.Module)
|
||||
elif scope == "session":
|
||||
return None
|
||||
raise ValueError("unknown finalization scope %r" %(scope,))
|
||||
|
||||
def _addfinalizer(self, finalizer, scope):
|
||||
colitem = self._getscopeitem(scope)
|
||||
self.config._setupstate.addfinalizer(
|
||||
finalizer=finalizer, colitem=colitem)
|
||||
|
||||
def addfinalizer(self, finalizer):
|
||||
""" call the given finalizer after test function finished execution. """
|
||||
self._addfinalizer(finalizer, scope="function")
|
||||
|
||||
def __repr__(self):
|
||||
return "<FuncargRequest for %r>" %(self._pyfuncitem)
|
||||
|
||||
def _raiselookupfailed(self, argname):
|
||||
available = []
|
||||
for plugin in self._plugins:
|
||||
for name in vars(plugin):
|
||||
if name.startswith(self._argprefix):
|
||||
name = name[len(self._argprefix):]
|
||||
if name not in available:
|
||||
available.append(name)
|
||||
fspath, lineno, msg = self._pyfuncitem.reportinfo()
|
||||
msg = "LookupError: no factory found for function argument %r" % (argname,)
|
||||
msg += "\n available funcargs: %s" %(", ".join(available),)
|
||||
msg += "\n use 'py.test --funcargs [testpath]' for help on them."
|
||||
raise self.LookupError(msg)
|
||||
|
||||
def showfuncargs(config):
|
||||
from py._test.session import Collection
|
||||
collection = Collection(config)
|
||||
colitem = collection.getinitialnodes()[0]
|
||||
curdir = py.path.local()
|
||||
tw = py.io.TerminalWriter()
|
||||
#from py._test.funcargs import getplugins
|
||||
#from py._test.funcargs import FuncargRequest
|
||||
plugins = getplugins(colitem, withpy=True)
|
||||
verbose = config.getvalue("verbose")
|
||||
for plugin in plugins:
|
||||
available = []
|
||||
for name, factory in vars(plugin).items():
|
||||
if name.startswith(FuncargRequest._argprefix):
|
||||
name = name[len(FuncargRequest._argprefix):]
|
||||
if name not in available:
|
||||
available.append([name, factory])
|
||||
if available:
|
||||
pluginname = plugin.__name__
|
||||
for name, factory in available:
|
||||
loc = getlocation(factory, curdir)
|
||||
if verbose:
|
||||
funcargspec = "%s -- %s" %(name, loc,)
|
||||
else:
|
||||
funcargspec = name
|
||||
tw.line(funcargspec, green=True)
|
||||
doc = factory.__doc__ or ""
|
||||
if doc:
|
||||
for line in doc.split("\n"):
|
||||
tw.line(" " + line.strip())
|
||||
else:
|
||||
tw.line(" %s: no docstring available" %(loc,),
|
||||
red=True)
|
||||
|
||||
def getlocation(function, curdir):
|
||||
import inspect
|
||||
fn = py.path.local(inspect.getfile(function))
|
||||
lineno = py.builtin._getcode(function).co_firstlineno
|
||||
if fn.relto(curdir):
|
||||
fn = fn.relto(curdir)
|
||||
return "%s:%d" %(fn, lineno+1)
|
|
@ -6,7 +6,7 @@ import inspect
|
|||
from py._plugin import hookspec
|
||||
|
||||
default_plugins = (
|
||||
"default runner pdb capture mark terminal skipping tmpdir monkeypatch "
|
||||
"default python runner pdb capture mark terminal skipping tmpdir monkeypatch "
|
||||
"recwarn pastebin unittest helpconfig nose assertion genscript "
|
||||
"junitxml doctest keyword").split()
|
||||
|
||||
|
|
|
@ -1,5 +1,521 @@
|
|||
import py, sys
|
||||
from py._test import funcargs
|
||||
from py._plugin import pytest_python as funcargs
|
||||
|
||||
class TestModule:
|
||||
def test_module_file_not_found(self, testdir):
|
||||
tmpdir = testdir.tmpdir
|
||||
fn = tmpdir.join('nada','no')
|
||||
col = py.test.collect.Module(fn, config=testdir.Config())
|
||||
col.config = testdir.parseconfig(tmpdir)
|
||||
py.test.raises(py.error.ENOENT, col.collect)
|
||||
|
||||
def test_failing_import(self, testdir):
|
||||
modcol = testdir.getmodulecol("import alksdjalskdjalkjals")
|
||||
py.test.raises(ImportError, modcol.collect)
|
||||
py.test.raises(ImportError, modcol.collect)
|
||||
py.test.raises(ImportError, modcol.run)
|
||||
|
||||
def test_import_duplicate(self, testdir):
|
||||
a = testdir.mkdir("a")
|
||||
b = testdir.mkdir("b")
|
||||
p = a.ensure("test_whatever.py")
|
||||
p.pyimport()
|
||||
del py.std.sys.modules['test_whatever']
|
||||
b.ensure("test_whatever.py")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines([
|
||||
"*import*mismatch*",
|
||||
"*imported*test_whatever*",
|
||||
"*%s*" % a.join("test_whatever.py"),
|
||||
"*not the same*",
|
||||
"*%s*" % b.join("test_whatever.py"),
|
||||
"*HINT*",
|
||||
])
|
||||
|
||||
def test_syntax_error_in_module(self, testdir):
|
||||
modcol = testdir.getmodulecol("this is a syntax error")
|
||||
py.test.raises(modcol.CollectError, modcol.collect)
|
||||
py.test.raises(modcol.CollectError, modcol.collect)
|
||||
py.test.raises(modcol.CollectError, modcol.run)
|
||||
|
||||
def test_module_considers_pluginmanager_at_import(self, testdir):
|
||||
modcol = testdir.getmodulecol("pytest_plugins='xasdlkj',")
|
||||
py.test.raises(ImportError, "modcol.obj")
|
||||
|
||||
class TestClass:
|
||||
def test_class_with_init_not_collected(self, testdir):
|
||||
modcol = testdir.getmodulecol("""
|
||||
class TestClass1:
|
||||
def __init__(self):
|
||||
pass
|
||||
class TestClass2(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
""")
|
||||
l = modcol.collect()
|
||||
assert len(l) == 0
|
||||
|
||||
if py.std.sys.version_info > (3, 0):
|
||||
_func_name_attr = "__name__"
|
||||
else:
|
||||
_func_name_attr = "func_name"
|
||||
|
||||
class TestGenerator:
|
||||
def test_generative_functions(self, testdir):
|
||||
modcol = testdir.getmodulecol("""
|
||||
def func1(arg, arg2):
|
||||
assert arg == arg2
|
||||
|
||||
def test_gen():
|
||||
yield func1, 17, 3*5
|
||||
yield func1, 42, 6*7
|
||||
""")
|
||||
colitems = modcol.collect()
|
||||
assert len(colitems) == 1
|
||||
gencol = colitems[0]
|
||||
assert isinstance(gencol, py.test.collect.Generator)
|
||||
gencolitems = gencol.collect()
|
||||
assert len(gencolitems) == 2
|
||||
assert isinstance(gencolitems[0], py.test.collect.Function)
|
||||
assert isinstance(gencolitems[1], py.test.collect.Function)
|
||||
assert gencolitems[0].name == '[0]'
|
||||
assert getattr(gencolitems[0].obj, _func_name_attr) == 'func1'
|
||||
|
||||
def test_generative_methods(self, testdir):
|
||||
modcol = testdir.getmodulecol("""
|
||||
def func1(arg, arg2):
|
||||
assert arg == arg2
|
||||
class TestGenMethods:
|
||||
def test_gen(self):
|
||||
yield func1, 17, 3*5
|
||||
yield func1, 42, 6*7
|
||||
""")
|
||||
gencol = modcol.collect()[0].collect()[0].collect()[0]
|
||||
assert isinstance(gencol, py.test.collect.Generator)
|
||||
gencolitems = gencol.collect()
|
||||
assert len(gencolitems) == 2
|
||||
assert isinstance(gencolitems[0], py.test.collect.Function)
|
||||
assert isinstance(gencolitems[1], py.test.collect.Function)
|
||||
assert gencolitems[0].name == '[0]'
|
||||
assert getattr(gencolitems[0].obj, _func_name_attr) == 'func1'
|
||||
|
||||
def test_generative_functions_with_explicit_names(self, testdir):
|
||||
modcol = testdir.getmodulecol("""
|
||||
def func1(arg, arg2):
|
||||
assert arg == arg2
|
||||
|
||||
def test_gen():
|
||||
yield "seventeen", func1, 17, 3*5
|
||||
yield "fortytwo", func1, 42, 6*7
|
||||
""")
|
||||
colitems = modcol.collect()
|
||||
assert len(colitems) == 1
|
||||
gencol = colitems[0]
|
||||
assert isinstance(gencol, py.test.collect.Generator)
|
||||
gencolitems = gencol.collect()
|
||||
assert len(gencolitems) == 2
|
||||
assert isinstance(gencolitems[0], py.test.collect.Function)
|
||||
assert isinstance(gencolitems[1], py.test.collect.Function)
|
||||
assert gencolitems[0].name == "['seventeen']"
|
||||
assert getattr(gencolitems[0].obj, _func_name_attr) == 'func1'
|
||||
assert gencolitems[1].name == "['fortytwo']"
|
||||
assert getattr(gencolitems[1].obj, _func_name_attr) == 'func1'
|
||||
|
||||
def test_generative_functions_unique_explicit_names(self, testdir):
|
||||
# generative
|
||||
modcol = testdir.getmodulecol("""
|
||||
def func(): pass
|
||||
def test_gen():
|
||||
yield "name", func
|
||||
yield "name", func
|
||||
""")
|
||||
colitems = modcol.collect()
|
||||
assert len(colitems) == 1
|
||||
gencol = colitems[0]
|
||||
assert isinstance(gencol, py.test.collect.Generator)
|
||||
py.test.raises(ValueError, "gencol.collect()")
|
||||
|
||||
def test_generative_methods_with_explicit_names(self, testdir):
|
||||
modcol = testdir.getmodulecol("""
|
||||
def func1(arg, arg2):
|
||||
assert arg == arg2
|
||||
class TestGenMethods:
|
||||
def test_gen(self):
|
||||
yield "m1", func1, 17, 3*5
|
||||
yield "m2", func1, 42, 6*7
|
||||
""")
|
||||
gencol = modcol.collect()[0].collect()[0].collect()[0]
|
||||
assert isinstance(gencol, py.test.collect.Generator)
|
||||
gencolitems = gencol.collect()
|
||||
assert len(gencolitems) == 2
|
||||
assert isinstance(gencolitems[0], py.test.collect.Function)
|
||||
assert isinstance(gencolitems[1], py.test.collect.Function)
|
||||
assert gencolitems[0].name == "['m1']"
|
||||
assert getattr(gencolitems[0].obj, _func_name_attr) == 'func1'
|
||||
assert gencolitems[1].name == "['m2']"
|
||||
assert getattr(gencolitems[1].obj, _func_name_attr) == 'func1'
|
||||
|
||||
def test_order_of_execution_generator_same_codeline(self, testdir, tmpdir):
|
||||
o = testdir.makepyfile("""
|
||||
def test_generative_order_of_execution():
|
||||
import py
|
||||
test_list = []
|
||||
expected_list = list(range(6))
|
||||
|
||||
def list_append(item):
|
||||
test_list.append(item)
|
||||
|
||||
def assert_order_of_execution():
|
||||
py.builtin.print_('expected order', expected_list)
|
||||
py.builtin.print_('but got ', test_list)
|
||||
assert test_list == expected_list
|
||||
|
||||
for i in expected_list:
|
||||
yield list_append, i
|
||||
yield assert_order_of_execution
|
||||
""")
|
||||
reprec = testdir.inline_run(o)
|
||||
passed, skipped, failed = reprec.countoutcomes()
|
||||
assert passed == 7
|
||||
assert not skipped and not failed
|
||||
|
||||
def test_order_of_execution_generator_different_codeline(self, testdir):
|
||||
o = testdir.makepyfile("""
|
||||
def test_generative_tests_different_codeline():
|
||||
import py
|
||||
test_list = []
|
||||
expected_list = list(range(3))
|
||||
|
||||
def list_append_2():
|
||||
test_list.append(2)
|
||||
|
||||
def list_append_1():
|
||||
test_list.append(1)
|
||||
|
||||
def list_append_0():
|
||||
test_list.append(0)
|
||||
|
||||
def assert_order_of_execution():
|
||||
py.builtin.print_('expected order', expected_list)
|
||||
py.builtin.print_('but got ', test_list)
|
||||
assert test_list == expected_list
|
||||
|
||||
yield list_append_0
|
||||
yield list_append_1
|
||||
yield list_append_2
|
||||
yield assert_order_of_execution
|
||||
""")
|
||||
reprec = testdir.inline_run(o)
|
||||
passed, skipped, failed = reprec.countoutcomes()
|
||||
assert passed == 4
|
||||
assert not skipped and not failed
|
||||
|
||||
class TestFunction:
|
||||
def test_getmodulecollector(self, testdir):
|
||||
item = testdir.getitem("def test_func(): pass")
|
||||
modcol = item.getparent(py.test.collect.Module)
|
||||
assert isinstance(modcol, py.test.collect.Module)
|
||||
assert hasattr(modcol.obj, 'test_func')
|
||||
|
||||
def test_function_equality(self, testdir, tmpdir):
|
||||
config = testdir.reparseconfig()
|
||||
f1 = py.test.collect.Function(name="name", config=config,
|
||||
args=(1,), callobj=isinstance)
|
||||
f2 = py.test.collect.Function(name="name",config=config,
|
||||
args=(1,), callobj=py.builtin.callable)
|
||||
assert not f1 == f2
|
||||
assert f1 != f2
|
||||
f3 = py.test.collect.Function(name="name", config=config,
|
||||
args=(1,2), callobj=py.builtin.callable)
|
||||
assert not f3 == f2
|
||||
assert f3 != f2
|
||||
|
||||
assert not f3 == f1
|
||||
assert f3 != f1
|
||||
|
||||
f1_b = py.test.collect.Function(name="name", config=config,
|
||||
args=(1,), callobj=isinstance)
|
||||
assert f1 == f1_b
|
||||
assert not f1 != f1_b
|
||||
|
||||
def test_function_equality_with_callspec(self, testdir, tmpdir):
|
||||
config = testdir.reparseconfig()
|
||||
class callspec1:
|
||||
param = 1
|
||||
funcargs = {}
|
||||
id = "hello"
|
||||
class callspec2:
|
||||
param = 1
|
||||
funcargs = {}
|
||||
id = "world"
|
||||
collection = object()
|
||||
f5 = py.test.collect.Function(name="name", config=config,
|
||||
callspec=callspec1, callobj=isinstance, collection=collection)
|
||||
f5b = py.test.collect.Function(name="name", config=config,
|
||||
callspec=callspec2, callobj=isinstance, collection=collection)
|
||||
assert f5 != f5b
|
||||
assert not (f5 == f5b)
|
||||
|
||||
def test_pyfunc_call(self, testdir):
|
||||
item = testdir.getitem("def test_func(): raise ValueError")
|
||||
config = item.config
|
||||
class MyPlugin1:
|
||||
def pytest_pyfunc_call(self, pyfuncitem):
|
||||
raise ValueError
|
||||
class MyPlugin2:
|
||||
def pytest_pyfunc_call(self, pyfuncitem):
|
||||
return True
|
||||
config.pluginmanager.register(MyPlugin1())
|
||||
config.pluginmanager.register(MyPlugin2())
|
||||
config.hook.pytest_pyfunc_call(pyfuncitem=item)
|
||||
|
||||
class TestSorting:
|
||||
def test_check_equality(self, testdir):
|
||||
modcol = testdir.getmodulecol("""
|
||||
def test_pass(): pass
|
||||
def test_fail(): assert 0
|
||||
""")
|
||||
fn1 = modcol.collect_by_name("test_pass")
|
||||
assert isinstance(fn1, py.test.collect.Function)
|
||||
fn2 = modcol.collect_by_name("test_pass")
|
||||
assert isinstance(fn2, py.test.collect.Function)
|
||||
|
||||
assert fn1 == fn2
|
||||
assert fn1 != modcol
|
||||
if py.std.sys.version_info < (3, 0):
|
||||
assert cmp(fn1, fn2) == 0
|
||||
assert hash(fn1) == hash(fn2)
|
||||
|
||||
fn3 = modcol.collect_by_name("test_fail")
|
||||
assert isinstance(fn3, py.test.collect.Function)
|
||||
assert not (fn1 == fn3)
|
||||
assert fn1 != fn3
|
||||
|
||||
for fn in fn1,fn2,fn3:
|
||||
assert fn != 3
|
||||
assert fn != modcol
|
||||
assert fn != [1,2,3]
|
||||
assert [1,2,3] != fn
|
||||
assert modcol != fn
|
||||
|
||||
def test_allow_sane_sorting_for_decorators(self, testdir):
|
||||
modcol = testdir.getmodulecol("""
|
||||
def dec(f):
|
||||
g = lambda: f(2)
|
||||
g.place_as = f
|
||||
return g
|
||||
|
||||
|
||||
def test_b(y):
|
||||
pass
|
||||
test_b = dec(test_b)
|
||||
|
||||
def test_a(y):
|
||||
pass
|
||||
test_a = dec(test_a)
|
||||
""")
|
||||
colitems = modcol.collect()
|
||||
assert len(colitems) == 2
|
||||
assert [item.name for item in colitems] == ['test_b', 'test_a']
|
||||
|
||||
|
||||
class TestConftestCustomization:
|
||||
def test_pytest_pycollect_module(self, testdir):
|
||||
testdir.makeconftest("""
|
||||
import py
|
||||
class MyModule(py.test.collect.Module):
|
||||
pass
|
||||
def pytest_pycollect_makemodule(path, parent):
|
||||
if path.basename == "test_xyz.py":
|
||||
return MyModule(path, parent)
|
||||
""")
|
||||
testdir.makepyfile("def some(): pass")
|
||||
testdir.makepyfile(test_xyz="")
|
||||
result = testdir.runpytest("--collectonly")
|
||||
result.stdout.fnmatch_lines([
|
||||
"*<Module*test_pytest*",
|
||||
"*<MyModule*xyz*",
|
||||
])
|
||||
|
||||
def test_pytest_pycollect_makeitem(self, testdir):
|
||||
testdir.makeconftest("""
|
||||
import py
|
||||
class MyFunction(py.test.collect.Function):
|
||||
pass
|
||||
def pytest_pycollect_makeitem(collector, name, obj):
|
||||
if name == "some":
|
||||
return MyFunction(name, collector)
|
||||
""")
|
||||
testdir.makepyfile("def some(): pass")
|
||||
result = testdir.runpytest("--collectonly")
|
||||
result.stdout.fnmatch_lines([
|
||||
"*MyFunction*some*",
|
||||
])
|
||||
|
||||
def test_makeitem_non_underscore(self, testdir, monkeypatch):
|
||||
modcol = testdir.getmodulecol("def _hello(): pass")
|
||||
l = []
|
||||
monkeypatch.setattr(py.test.collect.Module, 'makeitem',
|
||||
lambda self, name, obj: l.append(name))
|
||||
l = modcol.collect()
|
||||
assert '_hello' not in l
|
||||
|
||||
|
||||
class TestReportinfo:
|
||||
|
||||
def test_func_reportinfo(self, testdir):
|
||||
item = testdir.getitem("def test_func(): pass")
|
||||
fspath, lineno, modpath = item.reportinfo()
|
||||
assert fspath == item.fspath
|
||||
assert lineno == 0
|
||||
assert modpath == "test_func"
|
||||
|
||||
def test_class_reportinfo(self, testdir):
|
||||
modcol = testdir.getmodulecol("""
|
||||
# lineno 0
|
||||
class TestClass:
|
||||
def test_hello(self): pass
|
||||
""")
|
||||
classcol = modcol.collect_by_name("TestClass")
|
||||
fspath, lineno, msg = classcol.reportinfo()
|
||||
assert fspath == modcol.fspath
|
||||
assert lineno == 1
|
||||
assert msg == "TestClass"
|
||||
|
||||
def test_generator_reportinfo(self, testdir):
|
||||
modcol = testdir.getmodulecol("""
|
||||
# lineno 0
|
||||
def test_gen():
|
||||
def check(x):
|
||||
assert x
|
||||
yield check, 3
|
||||
""")
|
||||
gencol = modcol.collect_by_name("test_gen")
|
||||
fspath, lineno, modpath = gencol.reportinfo()
|
||||
assert fspath == modcol.fspath
|
||||
assert lineno == 1
|
||||
assert modpath == "test_gen"
|
||||
|
||||
genitem = gencol.collect()[0]
|
||||
fspath, lineno, modpath = genitem.reportinfo()
|
||||
assert fspath == modcol.fspath
|
||||
assert lineno == 2
|
||||
assert modpath == "test_gen[0]"
|
||||
"""
|
||||
def test_func():
|
||||
pass
|
||||
def test_genfunc():
|
||||
def check(x):
|
||||
pass
|
||||
yield check, 3
|
||||
class TestClass:
|
||||
def test_method(self):
|
||||
pass
|
||||
"""
|
||||
|
||||
def test_setup_only_available_in_subdir(testdir):
|
||||
sub1 = testdir.mkpydir("sub1")
|
||||
sub2 = testdir.mkpydir("sub2")
|
||||
sub1.join("conftest.py").write(py.code.Source("""
|
||||
import py
|
||||
def pytest_runtest_setup(item):
|
||||
assert item.fspath.purebasename == "test_in_sub1"
|
||||
def pytest_runtest_call(item):
|
||||
assert item.fspath.purebasename == "test_in_sub1"
|
||||
def pytest_runtest_teardown(item):
|
||||
assert item.fspath.purebasename == "test_in_sub1"
|
||||
"""))
|
||||
sub2.join("conftest.py").write(py.code.Source("""
|
||||
import py
|
||||
def pytest_runtest_setup(item):
|
||||
assert item.fspath.purebasename == "test_in_sub2"
|
||||
def pytest_runtest_call(item):
|
||||
assert item.fspath.purebasename == "test_in_sub2"
|
||||
def pytest_runtest_teardown(item):
|
||||
assert item.fspath.purebasename == "test_in_sub2"
|
||||
"""))
|
||||
sub1.join("test_in_sub1.py").write("def test_1(): pass")
|
||||
sub2.join("test_in_sub2.py").write("def test_2(): pass")
|
||||
result = testdir.runpytest("-v", "-s")
|
||||
result.stdout.fnmatch_lines([
|
||||
"*2 passed*"
|
||||
])
|
||||
|
||||
def test_generate_tests_only_done_in_subdir(testdir):
|
||||
sub1 = testdir.mkpydir("sub1")
|
||||
sub2 = testdir.mkpydir("sub2")
|
||||
sub1.join("conftest.py").write(py.code.Source("""
|
||||
def pytest_generate_tests(metafunc):
|
||||
assert metafunc.function.__name__ == "test_1"
|
||||
"""))
|
||||
sub2.join("conftest.py").write(py.code.Source("""
|
||||
def pytest_generate_tests(metafunc):
|
||||
assert metafunc.function.__name__ == "test_2"
|
||||
"""))
|
||||
sub1.join("test_in_sub1.py").write("def test_1(): pass")
|
||||
sub2.join("test_in_sub2.py").write("def test_2(): pass")
|
||||
result = testdir.runpytest("-v", "-s", sub1, sub2, sub1)
|
||||
result.stdout.fnmatch_lines([
|
||||
"*3 passed*"
|
||||
])
|
||||
|
||||
def test_modulecol_roundtrip(testdir):
|
||||
modcol = testdir.getmodulecol("pass", withinit=True)
|
||||
trail = modcol.collection.getid(modcol)
|
||||
newcol = modcol.collection.getbyid(trail)[0]
|
||||
assert modcol.name == newcol.name
|
||||
|
||||
|
||||
class TestTracebackCutting:
|
||||
def test_skip_simple(self):
|
||||
excinfo = py.test.raises(py.test.skip.Exception, 'py.test.skip("xxx")')
|
||||
assert excinfo.traceback[-1].frame.code.name == "skip"
|
||||
assert excinfo.traceback[-1].ishidden()
|
||||
|
||||
def test_traceback_argsetup(self, testdir):
|
||||
testdir.makeconftest("""
|
||||
def pytest_funcarg__hello(request):
|
||||
raise ValueError("xyz")
|
||||
""")
|
||||
p = testdir.makepyfile("def test(hello): pass")
|
||||
result = testdir.runpytest(p)
|
||||
assert result.ret != 0
|
||||
out = result.stdout.str()
|
||||
assert out.find("xyz") != -1
|
||||
assert out.find("conftest.py:2: ValueError") != -1
|
||||
numentries = out.count("_ _ _") # separator for traceback entries
|
||||
assert numentries == 0
|
||||
|
||||
result = testdir.runpytest("--fulltrace", p)
|
||||
out = result.stdout.str()
|
||||
assert out.find("conftest.py:2: ValueError") != -1
|
||||
numentries = out.count("_ _ _ _") # separator for traceback entries
|
||||
assert numentries >3
|
||||
|
||||
def test_traceback_error_during_import(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
x = 1
|
||||
x = 2
|
||||
x = 17
|
||||
asd
|
||||
""")
|
||||
result = testdir.runpytest()
|
||||
assert result.ret != 0
|
||||
out = result.stdout.str()
|
||||
assert "x = 1" not in out
|
||||
assert "x = 2" not in out
|
||||
result.stdout.fnmatch_lines([
|
||||
">*asd*",
|
||||
"E*NameError*",
|
||||
])
|
||||
result = testdir.runpytest("--fulltrace")
|
||||
out = result.stdout.str()
|
||||
assert "x = 1" in out
|
||||
assert "x = 2" in out
|
||||
result.stdout.fnmatch_lines([
|
||||
">*asd*",
|
||||
"E*NameError*",
|
||||
])
|
||||
|
||||
def test_getfuncargnames():
|
||||
def f(): pass
|
|
@ -1,7 +1,7 @@
|
|||
from py._plugin.pytest_tmpdir import pytest_funcarg__tmpdir
|
||||
from py._plugin.pytest_python import FuncargRequest
|
||||
|
||||
def test_funcarg(testdir):
|
||||
from py._test.funcargs import FuncargRequest
|
||||
item = testdir.getitem("def test_func(tmpdir): pass")
|
||||
p = pytest_funcarg__tmpdir(FuncargRequest(item))
|
||||
assert p.check()
|
||||
|
|
|
@ -1,517 +0,0 @@
|
|||
import py
|
||||
|
||||
class TestModule:
|
||||
def test_module_file_not_found(self, testdir):
|
||||
tmpdir = testdir.tmpdir
|
||||
fn = tmpdir.join('nada','no')
|
||||
col = py.test.collect.Module(fn, config=testdir.Config())
|
||||
col.config = testdir.parseconfig(tmpdir)
|
||||
py.test.raises(py.error.ENOENT, col.collect)
|
||||
|
||||
def test_failing_import(self, testdir):
|
||||
modcol = testdir.getmodulecol("import alksdjalskdjalkjals")
|
||||
py.test.raises(ImportError, modcol.collect)
|
||||
py.test.raises(ImportError, modcol.collect)
|
||||
py.test.raises(ImportError, modcol.run)
|
||||
|
||||
def test_import_duplicate(self, testdir):
|
||||
a = testdir.mkdir("a")
|
||||
b = testdir.mkdir("b")
|
||||
p = a.ensure("test_whatever.py")
|
||||
p.pyimport()
|
||||
del py.std.sys.modules['test_whatever']
|
||||
b.ensure("test_whatever.py")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines([
|
||||
"*import*mismatch*",
|
||||
"*imported*test_whatever*",
|
||||
"*%s*" % a.join("test_whatever.py"),
|
||||
"*not the same*",
|
||||
"*%s*" % b.join("test_whatever.py"),
|
||||
"*HINT*",
|
||||
])
|
||||
|
||||
def test_syntax_error_in_module(self, testdir):
|
||||
modcol = testdir.getmodulecol("this is a syntax error")
|
||||
py.test.raises(modcol.CollectError, modcol.collect)
|
||||
py.test.raises(modcol.CollectError, modcol.collect)
|
||||
py.test.raises(modcol.CollectError, modcol.run)
|
||||
|
||||
def test_module_considers_pluginmanager_at_import(self, testdir):
|
||||
modcol = testdir.getmodulecol("pytest_plugins='xasdlkj',")
|
||||
py.test.raises(ImportError, "modcol.obj")
|
||||
|
||||
class TestClass:
|
||||
def test_class_with_init_not_collected(self, testdir):
|
||||
modcol = testdir.getmodulecol("""
|
||||
class TestClass1:
|
||||
def __init__(self):
|
||||
pass
|
||||
class TestClass2(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
""")
|
||||
l = modcol.collect()
|
||||
assert len(l) == 0
|
||||
|
||||
if py.std.sys.version_info > (3, 0):
|
||||
_func_name_attr = "__name__"
|
||||
else:
|
||||
_func_name_attr = "func_name"
|
||||
|
||||
class TestGenerator:
|
||||
def test_generative_functions(self, testdir):
|
||||
modcol = testdir.getmodulecol("""
|
||||
def func1(arg, arg2):
|
||||
assert arg == arg2
|
||||
|
||||
def test_gen():
|
||||
yield func1, 17, 3*5
|
||||
yield func1, 42, 6*7
|
||||
""")
|
||||
colitems = modcol.collect()
|
||||
assert len(colitems) == 1
|
||||
gencol = colitems[0]
|
||||
assert isinstance(gencol, py.test.collect.Generator)
|
||||
gencolitems = gencol.collect()
|
||||
assert len(gencolitems) == 2
|
||||
assert isinstance(gencolitems[0], py.test.collect.Function)
|
||||
assert isinstance(gencolitems[1], py.test.collect.Function)
|
||||
assert gencolitems[0].name == '[0]'
|
||||
assert getattr(gencolitems[0].obj, _func_name_attr) == 'func1'
|
||||
|
||||
def test_generative_methods(self, testdir):
|
||||
modcol = testdir.getmodulecol("""
|
||||
def func1(arg, arg2):
|
||||
assert arg == arg2
|
||||
class TestGenMethods:
|
||||
def test_gen(self):
|
||||
yield func1, 17, 3*5
|
||||
yield func1, 42, 6*7
|
||||
""")
|
||||
gencol = modcol.collect()[0].collect()[0].collect()[0]
|
||||
assert isinstance(gencol, py.test.collect.Generator)
|
||||
gencolitems = gencol.collect()
|
||||
assert len(gencolitems) == 2
|
||||
assert isinstance(gencolitems[0], py.test.collect.Function)
|
||||
assert isinstance(gencolitems[1], py.test.collect.Function)
|
||||
assert gencolitems[0].name == '[0]'
|
||||
assert getattr(gencolitems[0].obj, _func_name_attr) == 'func1'
|
||||
|
||||
def test_generative_functions_with_explicit_names(self, testdir):
|
||||
modcol = testdir.getmodulecol("""
|
||||
def func1(arg, arg2):
|
||||
assert arg == arg2
|
||||
|
||||
def test_gen():
|
||||
yield "seventeen", func1, 17, 3*5
|
||||
yield "fortytwo", func1, 42, 6*7
|
||||
""")
|
||||
colitems = modcol.collect()
|
||||
assert len(colitems) == 1
|
||||
gencol = colitems[0]
|
||||
assert isinstance(gencol, py.test.collect.Generator)
|
||||
gencolitems = gencol.collect()
|
||||
assert len(gencolitems) == 2
|
||||
assert isinstance(gencolitems[0], py.test.collect.Function)
|
||||
assert isinstance(gencolitems[1], py.test.collect.Function)
|
||||
assert gencolitems[0].name == "['seventeen']"
|
||||
assert getattr(gencolitems[0].obj, _func_name_attr) == 'func1'
|
||||
assert gencolitems[1].name == "['fortytwo']"
|
||||
assert getattr(gencolitems[1].obj, _func_name_attr) == 'func1'
|
||||
|
||||
def test_generative_functions_unique_explicit_names(self, testdir):
|
||||
# generative
|
||||
modcol = testdir.getmodulecol("""
|
||||
def func(): pass
|
||||
def test_gen():
|
||||
yield "name", func
|
||||
yield "name", func
|
||||
""")
|
||||
colitems = modcol.collect()
|
||||
assert len(colitems) == 1
|
||||
gencol = colitems[0]
|
||||
assert isinstance(gencol, py.test.collect.Generator)
|
||||
py.test.raises(ValueError, "gencol.collect()")
|
||||
|
||||
def test_generative_methods_with_explicit_names(self, testdir):
|
||||
modcol = testdir.getmodulecol("""
|
||||
def func1(arg, arg2):
|
||||
assert arg == arg2
|
||||
class TestGenMethods:
|
||||
def test_gen(self):
|
||||
yield "m1", func1, 17, 3*5
|
||||
yield "m2", func1, 42, 6*7
|
||||
""")
|
||||
gencol = modcol.collect()[0].collect()[0].collect()[0]
|
||||
assert isinstance(gencol, py.test.collect.Generator)
|
||||
gencolitems = gencol.collect()
|
||||
assert len(gencolitems) == 2
|
||||
assert isinstance(gencolitems[0], py.test.collect.Function)
|
||||
assert isinstance(gencolitems[1], py.test.collect.Function)
|
||||
assert gencolitems[0].name == "['m1']"
|
||||
assert getattr(gencolitems[0].obj, _func_name_attr) == 'func1'
|
||||
assert gencolitems[1].name == "['m2']"
|
||||
assert getattr(gencolitems[1].obj, _func_name_attr) == 'func1'
|
||||
|
||||
def test_order_of_execution_generator_same_codeline(self, testdir, tmpdir):
|
||||
o = testdir.makepyfile("""
|
||||
def test_generative_order_of_execution():
|
||||
import py
|
||||
test_list = []
|
||||
expected_list = list(range(6))
|
||||
|
||||
def list_append(item):
|
||||
test_list.append(item)
|
||||
|
||||
def assert_order_of_execution():
|
||||
py.builtin.print_('expected order', expected_list)
|
||||
py.builtin.print_('but got ', test_list)
|
||||
assert test_list == expected_list
|
||||
|
||||
for i in expected_list:
|
||||
yield list_append, i
|
||||
yield assert_order_of_execution
|
||||
""")
|
||||
reprec = testdir.inline_run(o)
|
||||
passed, skipped, failed = reprec.countoutcomes()
|
||||
assert passed == 7
|
||||
assert not skipped and not failed
|
||||
|
||||
def test_order_of_execution_generator_different_codeline(self, testdir):
|
||||
o = testdir.makepyfile("""
|
||||
def test_generative_tests_different_codeline():
|
||||
import py
|
||||
test_list = []
|
||||
expected_list = list(range(3))
|
||||
|
||||
def list_append_2():
|
||||
test_list.append(2)
|
||||
|
||||
def list_append_1():
|
||||
test_list.append(1)
|
||||
|
||||
def list_append_0():
|
||||
test_list.append(0)
|
||||
|
||||
def assert_order_of_execution():
|
||||
py.builtin.print_('expected order', expected_list)
|
||||
py.builtin.print_('but got ', test_list)
|
||||
assert test_list == expected_list
|
||||
|
||||
yield list_append_0
|
||||
yield list_append_1
|
||||
yield list_append_2
|
||||
yield assert_order_of_execution
|
||||
""")
|
||||
reprec = testdir.inline_run(o)
|
||||
passed, skipped, failed = reprec.countoutcomes()
|
||||
assert passed == 4
|
||||
assert not skipped and not failed
|
||||
|
||||
class TestFunction:
|
||||
def test_getmodulecollector(self, testdir):
|
||||
item = testdir.getitem("def test_func(): pass")
|
||||
modcol = item.getparent(py.test.collect.Module)
|
||||
assert isinstance(modcol, py.test.collect.Module)
|
||||
assert hasattr(modcol.obj, 'test_func')
|
||||
|
||||
def test_function_equality(self, testdir, tmpdir):
|
||||
config = testdir.reparseconfig()
|
||||
f1 = py.test.collect.Function(name="name", config=config,
|
||||
args=(1,), callobj=isinstance)
|
||||
f2 = py.test.collect.Function(name="name",config=config,
|
||||
args=(1,), callobj=py.builtin.callable)
|
||||
assert not f1 == f2
|
||||
assert f1 != f2
|
||||
f3 = py.test.collect.Function(name="name", config=config,
|
||||
args=(1,2), callobj=py.builtin.callable)
|
||||
assert not f3 == f2
|
||||
assert f3 != f2
|
||||
|
||||
assert not f3 == f1
|
||||
assert f3 != f1
|
||||
|
||||
f1_b = py.test.collect.Function(name="name", config=config,
|
||||
args=(1,), callobj=isinstance)
|
||||
assert f1 == f1_b
|
||||
assert not f1 != f1_b
|
||||
|
||||
def test_function_equality_with_callspec(self, testdir, tmpdir):
|
||||
config = testdir.reparseconfig()
|
||||
class callspec1:
|
||||
param = 1
|
||||
funcargs = {}
|
||||
id = "hello"
|
||||
class callspec2:
|
||||
param = 1
|
||||
funcargs = {}
|
||||
id = "world"
|
||||
collection = object()
|
||||
f5 = py.test.collect.Function(name="name", config=config,
|
||||
callspec=callspec1, callobj=isinstance, collection=collection)
|
||||
f5b = py.test.collect.Function(name="name", config=config,
|
||||
callspec=callspec2, callobj=isinstance, collection=collection)
|
||||
assert f5 != f5b
|
||||
assert not (f5 == f5b)
|
||||
|
||||
def test_pyfunc_call(self, testdir):
|
||||
item = testdir.getitem("def test_func(): raise ValueError")
|
||||
config = item.config
|
||||
class MyPlugin1:
|
||||
def pytest_pyfunc_call(self, pyfuncitem):
|
||||
raise ValueError
|
||||
class MyPlugin2:
|
||||
def pytest_pyfunc_call(self, pyfuncitem):
|
||||
return True
|
||||
config.pluginmanager.register(MyPlugin1())
|
||||
config.pluginmanager.register(MyPlugin2())
|
||||
config.hook.pytest_pyfunc_call(pyfuncitem=item)
|
||||
|
||||
class TestSorting:
|
||||
def test_check_equality(self, testdir):
|
||||
modcol = testdir.getmodulecol("""
|
||||
def test_pass(): pass
|
||||
def test_fail(): assert 0
|
||||
""")
|
||||
fn1 = modcol.collect_by_name("test_pass")
|
||||
assert isinstance(fn1, py.test.collect.Function)
|
||||
fn2 = modcol.collect_by_name("test_pass")
|
||||
assert isinstance(fn2, py.test.collect.Function)
|
||||
|
||||
assert fn1 == fn2
|
||||
assert fn1 != modcol
|
||||
if py.std.sys.version_info < (3, 0):
|
||||
assert cmp(fn1, fn2) == 0
|
||||
assert hash(fn1) == hash(fn2)
|
||||
|
||||
fn3 = modcol.collect_by_name("test_fail")
|
||||
assert isinstance(fn3, py.test.collect.Function)
|
||||
assert not (fn1 == fn3)
|
||||
assert fn1 != fn3
|
||||
|
||||
for fn in fn1,fn2,fn3:
|
||||
assert fn != 3
|
||||
assert fn != modcol
|
||||
assert fn != [1,2,3]
|
||||
assert [1,2,3] != fn
|
||||
assert modcol != fn
|
||||
|
||||
def test_allow_sane_sorting_for_decorators(self, testdir):
|
||||
modcol = testdir.getmodulecol("""
|
||||
def dec(f):
|
||||
g = lambda: f(2)
|
||||
g.place_as = f
|
||||
return g
|
||||
|
||||
|
||||
def test_b(y):
|
||||
pass
|
||||
test_b = dec(test_b)
|
||||
|
||||
def test_a(y):
|
||||
pass
|
||||
test_a = dec(test_a)
|
||||
""")
|
||||
colitems = modcol.collect()
|
||||
assert len(colitems) == 2
|
||||
assert [item.name for item in colitems] == ['test_b', 'test_a']
|
||||
|
||||
|
||||
class TestConftestCustomization:
|
||||
def test_pytest_pycollect_module(self, testdir):
|
||||
testdir.makeconftest("""
|
||||
import py
|
||||
class MyModule(py.test.collect.Module):
|
||||
pass
|
||||
def pytest_pycollect_makemodule(path, parent):
|
||||
if path.basename == "test_xyz.py":
|
||||
return MyModule(path, parent)
|
||||
""")
|
||||
testdir.makepyfile("def some(): pass")
|
||||
testdir.makepyfile(test_xyz="")
|
||||
result = testdir.runpytest("--collectonly")
|
||||
result.stdout.fnmatch_lines([
|
||||
"*<Module*test_pytest*",
|
||||
"*<MyModule*xyz*",
|
||||
])
|
||||
|
||||
def test_pytest_pycollect_makeitem(self, testdir):
|
||||
testdir.makeconftest("""
|
||||
import py
|
||||
class MyFunction(py.test.collect.Function):
|
||||
pass
|
||||
def pytest_pycollect_makeitem(collector, name, obj):
|
||||
if name == "some":
|
||||
return MyFunction(name, collector)
|
||||
""")
|
||||
testdir.makepyfile("def some(): pass")
|
||||
result = testdir.runpytest("--collectonly")
|
||||
result.stdout.fnmatch_lines([
|
||||
"*MyFunction*some*",
|
||||
])
|
||||
|
||||
def test_makeitem_non_underscore(self, testdir, monkeypatch):
|
||||
modcol = testdir.getmodulecol("def _hello(): pass")
|
||||
l = []
|
||||
monkeypatch.setattr(py.test.collect.Module, 'makeitem',
|
||||
lambda self, name, obj: l.append(name))
|
||||
l = modcol.collect()
|
||||
assert '_hello' not in l
|
||||
|
||||
|
||||
class TestReportinfo:
|
||||
|
||||
def test_func_reportinfo(self, testdir):
|
||||
item = testdir.getitem("def test_func(): pass")
|
||||
fspath, lineno, modpath = item.reportinfo()
|
||||
assert fspath == item.fspath
|
||||
assert lineno == 0
|
||||
assert modpath == "test_func"
|
||||
|
||||
def test_class_reportinfo(self, testdir):
|
||||
modcol = testdir.getmodulecol("""
|
||||
# lineno 0
|
||||
class TestClass:
|
||||
def test_hello(self): pass
|
||||
""")
|
||||
classcol = modcol.collect_by_name("TestClass")
|
||||
fspath, lineno, msg = classcol.reportinfo()
|
||||
assert fspath == modcol.fspath
|
||||
assert lineno == 1
|
||||
assert msg == "TestClass"
|
||||
|
||||
def test_generator_reportinfo(self, testdir):
|
||||
modcol = testdir.getmodulecol("""
|
||||
# lineno 0
|
||||
def test_gen():
|
||||
def check(x):
|
||||
assert x
|
||||
yield check, 3
|
||||
""")
|
||||
gencol = modcol.collect_by_name("test_gen")
|
||||
fspath, lineno, modpath = gencol.reportinfo()
|
||||
assert fspath == modcol.fspath
|
||||
assert lineno == 1
|
||||
assert modpath == "test_gen"
|
||||
|
||||
genitem = gencol.collect()[0]
|
||||
fspath, lineno, modpath = genitem.reportinfo()
|
||||
assert fspath == modcol.fspath
|
||||
assert lineno == 2
|
||||
assert modpath == "test_gen[0]"
|
||||
"""
|
||||
def test_func():
|
||||
pass
|
||||
def test_genfunc():
|
||||
def check(x):
|
||||
pass
|
||||
yield check, 3
|
||||
class TestClass:
|
||||
def test_method(self):
|
||||
pass
|
||||
"""
|
||||
|
||||
def test_setup_only_available_in_subdir(testdir):
|
||||
sub1 = testdir.mkpydir("sub1")
|
||||
sub2 = testdir.mkpydir("sub2")
|
||||
sub1.join("conftest.py").write(py.code.Source("""
|
||||
import py
|
||||
def pytest_runtest_setup(item):
|
||||
assert item.fspath.purebasename == "test_in_sub1"
|
||||
def pytest_runtest_call(item):
|
||||
assert item.fspath.purebasename == "test_in_sub1"
|
||||
def pytest_runtest_teardown(item):
|
||||
assert item.fspath.purebasename == "test_in_sub1"
|
||||
"""))
|
||||
sub2.join("conftest.py").write(py.code.Source("""
|
||||
import py
|
||||
def pytest_runtest_setup(item):
|
||||
assert item.fspath.purebasename == "test_in_sub2"
|
||||
def pytest_runtest_call(item):
|
||||
assert item.fspath.purebasename == "test_in_sub2"
|
||||
def pytest_runtest_teardown(item):
|
||||
assert item.fspath.purebasename == "test_in_sub2"
|
||||
"""))
|
||||
sub1.join("test_in_sub1.py").write("def test_1(): pass")
|
||||
sub2.join("test_in_sub2.py").write("def test_2(): pass")
|
||||
result = testdir.runpytest("-v", "-s")
|
||||
result.stdout.fnmatch_lines([
|
||||
"*2 passed*"
|
||||
])
|
||||
|
||||
def test_generate_tests_only_done_in_subdir(testdir):
|
||||
sub1 = testdir.mkpydir("sub1")
|
||||
sub2 = testdir.mkpydir("sub2")
|
||||
sub1.join("conftest.py").write(py.code.Source("""
|
||||
def pytest_generate_tests(metafunc):
|
||||
assert metafunc.function.__name__ == "test_1"
|
||||
"""))
|
||||
sub2.join("conftest.py").write(py.code.Source("""
|
||||
def pytest_generate_tests(metafunc):
|
||||
assert metafunc.function.__name__ == "test_2"
|
||||
"""))
|
||||
sub1.join("test_in_sub1.py").write("def test_1(): pass")
|
||||
sub2.join("test_in_sub2.py").write("def test_2(): pass")
|
||||
result = testdir.runpytest("-v", "-s", sub1, sub2, sub1)
|
||||
result.stdout.fnmatch_lines([
|
||||
"*3 passed*"
|
||||
])
|
||||
|
||||
def test_modulecol_roundtrip(testdir):
|
||||
modcol = testdir.getmodulecol("pass", withinit=True)
|
||||
trail = modcol.collection.getid(modcol)
|
||||
newcol = modcol.collection.getbyid(trail)[0]
|
||||
assert modcol.name == newcol.name
|
||||
|
||||
|
||||
class TestTracebackCutting:
|
||||
def test_skip_simple(self):
|
||||
excinfo = py.test.raises(py.test.skip.Exception, 'py.test.skip("xxx")')
|
||||
assert excinfo.traceback[-1].frame.code.name == "skip"
|
||||
assert excinfo.traceback[-1].ishidden()
|
||||
|
||||
def test_traceback_argsetup(self, testdir):
|
||||
testdir.makeconftest("""
|
||||
def pytest_funcarg__hello(request):
|
||||
raise ValueError("xyz")
|
||||
""")
|
||||
p = testdir.makepyfile("def test(hello): pass")
|
||||
result = testdir.runpytest(p)
|
||||
assert result.ret != 0
|
||||
out = result.stdout.str()
|
||||
assert out.find("xyz") != -1
|
||||
assert out.find("conftest.py:2: ValueError") != -1
|
||||
numentries = out.count("_ _ _") # separator for traceback entries
|
||||
assert numentries == 0
|
||||
|
||||
result = testdir.runpytest("--fulltrace", p)
|
||||
out = result.stdout.str()
|
||||
assert out.find("conftest.py:2: ValueError") != -1
|
||||
numentries = out.count("_ _ _ _") # separator for traceback entries
|
||||
assert numentries >3
|
||||
|
||||
def test_traceback_error_during_import(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
x = 1
|
||||
x = 2
|
||||
x = 17
|
||||
asd
|
||||
""")
|
||||
result = testdir.runpytest()
|
||||
assert result.ret != 0
|
||||
out = result.stdout.str()
|
||||
assert "x = 1" not in out
|
||||
assert "x = 2" not in out
|
||||
result.stdout.fnmatch_lines([
|
||||
">*asd*",
|
||||
"E*NameError*",
|
||||
])
|
||||
result = testdir.runpytest("--fulltrace")
|
||||
out = result.stdout.str()
|
||||
assert "x = 1" in out
|
||||
assert "x = 2" in out
|
||||
result.stdout.fnmatch_lines([
|
||||
">*asd*",
|
||||
"E*NameError*",
|
||||
])
|
Loading…
Reference in New Issue