deprecate direct definition of Directory, Module, ... in conftest.py's,

add some pytest collect related tests + some refinements.

--HG--
branch : trunk
This commit is contained in:
holger krekel 2009-12-30 16:18:59 +01:00
parent d3b20e8d24
commit f5ea19858c
10 changed files with 177 additions and 101 deletions

View File

@ -14,11 +14,16 @@ Changes between 1.X and 1.1.1
- allow pytest_generate_tests to be defined in classes as well
- deprecate usage of 'disabled' attribute in favour of pytestmark
- deprecate definition of Directory, Module, Class and Function nodes
in conftest.py files. Use pytest collect hooks instead.
- collection/item node specific runtest/collect hooks are only called exactly
on matching conftest.py files, i.e. ones which are exactly below
the filesystem path of an item
- change: the first pytest_collect_directory hook to return something
will now prevent further hooks to be called.
- robustify capturing to survive if custom pytest_runtest_setup
code failed and prevented the capturing setup code from running.

View File

@ -36,10 +36,10 @@ class Node(object):
- configuration/options for setup/teardown
stdout/stderr capturing and execution of test items
"""
def __init__(self, name, parent=None):
def __init__(self, name, parent=None, config=None):
self.name = name
self.parent = parent
self.config = getattr(parent, 'config', None)
self.config = config or parent.config
self.fspath = getattr(parent, 'fspath', None)
self.ihook = HookProxy(self)
@ -353,9 +353,9 @@ class Collector(Node):
return traceback
class FSCollector(Collector):
def __init__(self, fspath, parent=None):
def __init__(self, fspath, parent=None, config=None):
fspath = py.path.local(fspath)
super(FSCollector, self).__init__(fspath.basename, parent)
super(FSCollector, self).__init__(fspath.basename, parent, config=config)
self.fspath = fspath
def __getstate__(self):

View File

@ -156,16 +156,20 @@ class Config(object):
pkgpath = path.pypkgpath()
if pkgpath is None:
pkgpath = path.check(file=1) and path.dirpath() or path
Dir = self._getcollectclass("Directory", pkgpath)
col = Dir(pkgpath)
col.config = self
tmpcol = py.test.collect.Directory(pkgpath, config=self)
col = tmpcol.ihook.pytest_collect_directory(path=pkgpath, parent=tmpcol)
col.parent = None
return col._getfsnode(path)
def _getcollectclass(self, name, path):
try:
return self.getvalue(name, path)
cls = self.getvalue(name, path)
except KeyError:
return getattr(py.test.collect, name)
else:
py.log._apiwarn(">1.1", "%r was found in a conftest.py file, "
"use pytest_collect hooks instead." % (cls,))
return cls
def getconftest_pathlist(self, name, path=None):
""" return a matching value, which needs to be sequence

View File

@ -315,9 +315,9 @@ class Function(FunctionMixin, py.test.collect.Item):
and executing a Python callable test object.
"""
_genid = None
def __init__(self, name, parent=None, args=None,
def __init__(self, name, parent=None, args=None, config=None,
callspec=None, callobj=_dummy):
super(Function, self).__init__(name, parent)
super(Function, self).__init__(name, parent, config=config)
self._args = args
if self._isyieldedfunction():
assert not callspec, "yielded functions (deprecated) cannot have funcargs"

View File

@ -26,6 +26,7 @@ def pytest_unconfigure(config):
def pytest_collect_directory(path, parent):
""" return Collection node or None for the given path. """
pytest_collect_directory.firstresult = True
def pytest_collect_file(path, parent):
""" return Collection node or None for the given path. """

View File

@ -3,8 +3,9 @@ import os
from py.plugin.pytest_resultlog import generic_path, ResultLog
from py.impl.test.collect import Node, Item, FSCollector
def test_generic_path():
p1 = Node('a')
def test_generic_path(testdir):
config = testdir.Config()
p1 = Node('a', config=config)
assert p1.fspath is None
p2 = Node('B', parent=p1)
p3 = Node('()', parent = p2)
@ -13,7 +14,7 @@ def test_generic_path():
res = generic_path(item)
assert res == 'a.B().c'
p0 = FSCollector('proj/test')
p0 = FSCollector('proj/test', config=config)
p1 = FSCollector('proj/test/a', parent=p0)
p2 = Node('B', parent=p1)
p3 = Node('()', parent = p2)

View File

@ -193,31 +193,6 @@ class TestPrunetraceback:
])
class TestCustomConftests:
def test_non_python_files(self, testdir):
testdir.makepyfile(conftest="""
import py
class CustomItem(py.test.collect.Item):
def run(self):
pass
class Directory(py.test.collect.Directory):
def consider_file(self, fspath):
if fspath.ext == ".xxx":
return CustomItem(fspath.basename, parent=self)
""")
checkfile = testdir.makefile(ext="xxx", hello="world")
testdir.makepyfile(x="")
testdir.maketxtfile(x="")
config = testdir.parseconfig()
dircol = config.getfsnode(checkfile.dirpath())
colitems = dircol.collect()
assert len(colitems) == 1
assert colitems[0].name == "hello.xxx"
assert colitems[0].__class__.__name__ == "CustomItem"
item = config.getfsnode(checkfile)
assert item.name == "hello.xxx"
assert item.__class__.__name__ == "CustomItem"
def test_collectignore_exclude_on_option(self, testdir):
testdir.makeconftest("""
collect_ignore = ['hello', 'test_world.py']
@ -237,3 +212,23 @@ class TestCustomConftests:
names = [rep.collector.name for rep in reprec.getreports("pytest_collectreport")]
assert 'hello' in names
assert 'test_world.py' in names
def test_pytest_fs_collect_hooks_are_seen(self, testdir):
testdir.makeconftest("""
import py
class MyDirectory(py.test.collect.Directory):
pass
class MyModule(py.test.collect.Module):
pass
def pytest_collect_directory(path, parent):
return MyDirectory(path, parent)
def pytest_collect_file(path, parent):
return MyModule(path, parent)
""")
testdir.makepyfile("def test_x(): pass")
result = testdir.runpytest("--collectonly")
result.stdout.fnmatch_lines([
"*MyDirectory*",
"*MyModule*",
"*test_x*"
])

View File

@ -48,12 +48,6 @@ class TestConftestValueAccessGlobal:
conftest.getconftestmodules(basedir.join('b'))
assert len(conftest._path2confmods) == snap1 + 2
def test_default_Module_setting_is_visible_always(self, basedir, testdir):
basedir.copy(testdir.tmpdir)
config = testdir.Config()
colclass = config._getcollectclass("Module", testdir.tmpdir)
assert colclass == py.test.collect.Module
def test_default_has_lower_prio(self, basedir):
conftest = ConftestWithSetinitial(basedir.join("adir"))
assert conftest.rget('Directory') == 3

View File

@ -37,15 +37,19 @@ class TestCollectDeprecated:
def join(self, name):
if name == "somefile.py":
return self.Module(self.fspath.join(name), parent=self)
Directory = MyDirectory
def pytest_collect_directory(path, parent):
return MyDirectory(path, parent)
""")
p = testdir.makepyfile(somefile="""
subconf = testdir.mkpydir("subconf")
somefile = subconf.join("somefile.py")
somefile.write(py.code.Source("""
def check(): pass
class Cls:
def check2(self): pass
""")
config = testdir.parseconfig()
dirnode = config.getfsnode(p.dirpath())
"""))
config = testdir.parseconfig(somefile)
dirnode = config.getfsnode(somefile.dirpath())
colitems = dirnode.collect()
w = recwarn.pop(DeprecationWarning)
assert w.filename.find("conftest.py") != -1
@ -120,6 +124,7 @@ class TestCollectDeprecated:
""")
modcol = testdir.getmodulecol("def test_func2(): pass")
funcitem = modcol.collect()[0]
w = recwarn.pop(DeprecationWarning) # for defining conftest.Function
assert funcitem.name == 'test_func2'
funcitem._deprecated_testexecution()
w = recwarn.pop(DeprecationWarning)
@ -136,6 +141,8 @@ class TestCollectDeprecated:
""")
modcol = testdir.getmodulecol("def test_some2(): pass")
funcitem = modcol.collect()[0]
w = recwarn.pop(DeprecationWarning)
assert "conftest.py" in str(w.message)
recwarn.clear()
funcitem._deprecated_testexecution()
@ -169,6 +176,7 @@ class TestCollectDeprecated:
col = config.getfsnode(testme)
assert col.collect() == []
class TestDisabled:
def test_disabled_module(self, recwarn, testdir):
@ -211,6 +219,31 @@ class TestDisabled:
""")
reprec.assertoutcome(skipped=2)
@py.test.mark.multi(name="Directory Module Class Function".split())
def test_function_deprecated_run_execute(self, name, testdir, recwarn):
testdir.makeconftest("""
import py
class %s(py.test.collect.%s):
pass
""" % (name, name))
p = testdir.makepyfile("""
class TestClass:
def test_method(self):
pass
def test_function():
pass
""")
config = testdir.parseconfig()
if name == "Directory":
config.getfsnode(testdir.tmpdir)
elif name in ("Module", "File"):
config.getfsnode(p)
else:
fnode = config.getfsnode(p)
recwarn.clear()
fnode.collect()
w = recwarn.pop(DeprecationWarning)
assert "conftest.py" in str(w.message)
def test_config_cmdline_options(recwarn, testdir):
testdir.makepyfile(conftest="""
@ -267,3 +300,80 @@ def test_dist_conftest_options(testdir):
"*1 passed*",
])
def test_conftest_non_python_items(recwarn, testdir):
testdir.makepyfile(conftest="""
import py
class CustomItem(py.test.collect.Item):
def run(self):
pass
class Directory(py.test.collect.Directory):
def consider_file(self, fspath):
if fspath.ext == ".xxx":
return CustomItem(fspath.basename, parent=self)
""")
checkfile = testdir.makefile(ext="xxx", hello="world")
testdir.makepyfile(x="")
testdir.maketxtfile(x="")
config = testdir.parseconfig()
recwarn.clear()
dircol = config.getfsnode(checkfile.dirpath())
w = recwarn.pop(DeprecationWarning)
assert str(w.message).find("conftest.py") != -1
colitems = dircol.collect()
assert len(colitems) == 1
assert colitems[0].name == "hello.xxx"
assert colitems[0].__class__.__name__ == "CustomItem"
item = config.getfsnode(checkfile)
assert item.name == "hello.xxx"
assert item.__class__.__name__ == "CustomItem"
def test_extra_python_files_and_functions(testdir):
testdir.makepyfile(conftest="""
import py
class MyFunction(py.test.collect.Function):
pass
class Directory(py.test.collect.Directory):
def consider_file(self, path):
if path.check(fnmatch="check_*.py"):
return self.Module(path, parent=self)
return super(Directory, self).consider_file(path)
class myfuncmixin:
Function = MyFunction
def funcnamefilter(self, name):
return name.startswith('check_')
class Module(myfuncmixin, py.test.collect.Module):
def classnamefilter(self, name):
return name.startswith('CustomTestClass')
class Instance(myfuncmixin, py.test.collect.Instance):
pass
""")
checkfile = testdir.makepyfile(check_file="""
def check_func():
assert 42 == 42
class CustomTestClass:
def check_method(self):
assert 23 == 23
""")
# check that directory collects "check_" files
config = testdir.parseconfig()
col = config.getfsnode(checkfile.dirpath())
colitems = col.collect()
assert len(colitems) == 1
assert isinstance(colitems[0], py.test.collect.Module)
# check that module collects "check_" functions and methods
config = testdir.parseconfig(checkfile)
col = config.getfsnode(checkfile)
assert isinstance(col, py.test.collect.Module)
colitems = col.collect()
assert len(colitems) == 2
funccol = colitems[0]
assert isinstance(funccol, py.test.collect.Function)
assert funccol.name == "check_func"
clscol = colitems[1]
assert isinstance(clscol, py.test.collect.Class)
colitems = clscol.collect()[0].collect()
assert len(colitems) == 1
assert colitems[0].name == "check_method"

View File

@ -4,7 +4,7 @@ class TestModule:
def test_module_file_not_found(self, testdir):
tmpdir = testdir.tmpdir
fn = tmpdir.join('nada','no')
col = py.test.collect.Module(fn)
col = py.test.collect.Module(fn, config=testdir.Config())
col.config = testdir.parseconfig(tmpdir)
py.test.raises(py.error.ENOENT, col.collect)
@ -213,13 +213,13 @@ class TestFunction:
def test_function_equality(self, testdir, tmpdir):
config = testdir.reparseconfig()
f1 = py.test.collect.Function(name="name",
f1 = py.test.collect.Function(name="name", config=config,
args=(1,), callobj=isinstance)
f2 = py.test.collect.Function(name="name",
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",
f3 = py.test.collect.Function(name="name", config=config,
args=(1,2), callobj=py.builtin.callable)
assert not f3 == f2
assert f3 != f2
@ -227,7 +227,7 @@ class TestFunction:
assert not f3 == f1
assert f3 != f1
f1_b = py.test.collect.Function(name="name",
f1_b = py.test.collect.Function(name="name", config=config,
args=(1,), callobj=isinstance)
assert f1 == f1_b
assert not f1 != f1_b
@ -242,9 +242,9 @@ class TestFunction:
param = 1
funcargs = {}
id = "world"
f5 = py.test.collect.Function(name="name",
f5 = py.test.collect.Function(name="name", config=config,
callspec=callspec1, callobj=isinstance)
f5b = py.test.collect.Function(name="name",
f5b = py.test.collect.Function(name="name", config=config,
callspec=callspec2, callobj=isinstance)
assert f5 != f5b
assert not (f5 == f5b)
@ -313,55 +313,21 @@ class TestSorting:
class TestConftestCustomization:
def test_extra_python_files_and_functions(self, testdir):
testdir.makepyfile(conftest="""
def test_pytest_pycollect_makeitem(self, testdir):
testdir.makeconftest("""
import py
class MyFunction(py.test.collect.Function):
pass
class Directory(py.test.collect.Directory):
def consider_file(self, path):
if path.check(fnmatch="check_*.py"):
return self.Module(path, parent=self)
return super(Directory, self).consider_file(path)
class myfuncmixin:
Function = MyFunction
def funcnamefilter(self, name):
return name.startswith('check_')
class Module(myfuncmixin, py.test.collect.Module):
def classnamefilter(self, name):
return name.startswith('CustomTestClass')
class Instance(myfuncmixin, py.test.collect.Instance):
pass
def pytest_pycollect_makeitem(collector, name, obj):
if name == "some":
return MyFunction(name, collector)
""")
checkfile = testdir.makepyfile(check_file="""
def check_func():
assert 42 == 42
class CustomTestClass:
def check_method(self):
assert 23 == 23
""")
# check that directory collects "check_" files
config = testdir.parseconfig()
col = config.getfsnode(checkfile.dirpath())
colitems = col.collect()
assert len(colitems) == 1
assert isinstance(colitems[0], py.test.collect.Module)
# check that module collects "check_" functions and methods
config = testdir.parseconfig(checkfile)
col = config.getfsnode(checkfile)
assert isinstance(col, py.test.collect.Module)
colitems = col.collect()
assert len(colitems) == 2
funccol = colitems[0]
assert isinstance(funccol, py.test.collect.Function)
assert funccol.name == "check_func"
clscol = colitems[1]
assert isinstance(clscol, py.test.collect.Class)
colitems = clscol.collect()[0].collect()
assert len(colitems) == 1
assert colitems[0].name == "check_method"
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 = []