refine parsefactories interface, fix two_classes test originally reported by Alex Okrushko, also add a few more tests to make sure autouse-fixtures are properly distinguished

This commit is contained in:
holger krekel 2012-10-16 16:13:12 +02:00
parent ab4183d400
commit cc2337af3a
5 changed files with 83 additions and 39 deletions

View File

@ -1,2 +1,2 @@
# #
__version__ = '2.3.0.dev23' __version__ = '2.3.0.dev24'

View File

@ -83,7 +83,7 @@ def pytest_addoption(parser):
group.addoption('--fixtures', '--fixtures', group.addoption('--fixtures', '--fixtures',
action="store_true", dest="showfixtures", default=False, action="store_true", dest="showfixtures", default=False,
help="show available fixtures, sorted by plugin appearance") help="show available fixtures, sorted by plugin appearance")
parser.addini("usefixtures", type="args", default=(), parser.addini("usefixtures", type="args", default=[],
help="list of default fixtures to be used with this project") help="list of default fixtures to be used with this project")
parser.addini("python_files", type="args", parser.addini("python_files", type="args",
default=('test_*.py', '*_test.py'), default=('test_*.py', '*_test.py'),
@ -379,7 +379,7 @@ class Module(pytest.File, PyCollector):
return self._memoizedcall('_obj', self._importtestmodule) return self._memoizedcall('_obj', self._importtestmodule)
def collect(self): def collect(self):
self.session._fixturemanager._parsefactories(self.obj, self.nodeid) self.session._fixturemanager.parsefactories(self)
return super(Module, self).collect() return super(Module, self).collect()
def _importtestmodule(self): def _importtestmodule(self):
@ -452,7 +452,7 @@ class Instance(PyCollector):
return obj return obj
def collect(self): def collect(self):
self.session._fixturemanager._parsefactories(self.obj, self.nodeid) self.session._fixturemanager.parsefactories(self)
return super(Instance, self).collect() return super(Instance, self).collect()
def newinstance(self): def newinstance(self):
@ -781,7 +781,7 @@ def _showfixtures_main(config, session):
fm = session._fixturemanager fm = session._fixturemanager
available = [] available = []
for argname in fm.arg2fixturedefs: for argname in fm._arg2fixturedefs:
fixturedefs = fm.getfixturedefs(argname, nodeid) fixturedefs = fm.getfixturedefs(argname, nodeid)
assert fixturedefs is not None assert fixturedefs is not None
if not fixturedefs: if not fixturedefs:
@ -1335,7 +1335,7 @@ class FixtureLookupError(LookupError):
fm = self.request._fixturemanager fm = self.request._fixturemanager
nodeid = self.request._parentid nodeid = self.request._parentid
available = [] available = []
for name, fixturedef in fm.arg2fixturedefs.items(): for name, fixturedef in fm._arg2fixturedefs.items():
faclist = list(fm._matchfactories(fixturedef, self.request._parentid)) faclist = list(fm._matchfactories(fixturedef, self.request._parentid))
if faclist: if faclist:
available.append(name) available.append(name)
@ -1370,11 +1370,11 @@ class FixtureManager:
def __init__(self, session): def __init__(self, session):
self.session = session self.session = session
self.config = session.config self.config = session.config
self.arg2fixturedefs = {} self._arg2fixturedefs = {}
self._seenplugins = set() self._seenplugins = set()
self._holderobjseen = set() self._holderobjseen = set()
self._arg2finish = {} self._arg2finish = {}
self._autofixtures = [] self._nodeid_and_autousenames = [("", self.config.getini("usefixtures"))]
session.config.pluginmanager.register(self, "funcmanage") session.config.pluginmanager.register(self, "funcmanage")
### XXX this hook should be called for historic events like pytest_configure ### XXX this hook should be called for historic events like pytest_configure
@ -1391,7 +1391,7 @@ class FixtureManager:
else: else:
if p.basename.startswith("conftest.py"): if p.basename.startswith("conftest.py"):
nodeid = p.dirpath().relto(self.session.fspath) nodeid = p.dirpath().relto(self.session.fspath)
self._parsefactories(plugin, nodeid) self.parsefactories(plugin, nodeid)
self._seenplugins.add(plugin) self._seenplugins.add(plugin)
@pytest.mark.tryfirst @pytest.mark.tryfirst
@ -1400,19 +1400,21 @@ class FixtureManager:
for plugin in plugins: for plugin in plugins:
self.pytest_plugin_registered(plugin) self.pytest_plugin_registered(plugin)
def getdefaultfixtures(self): def _getautousenames(self, nodeid):
""" return a tuple of default fixture names (XXX for the given file path). """ """ return a tuple of fixture names to be used. """
try: autousenames = []
return self._defaultfixtures for baseid, basenames in self._nodeid_and_autousenames:
except AttributeError: if nodeid.startswith(baseid):
defaultfixtures = tuple(self.config.getini("usefixtures")) if baseid:
# make sure the self._autofixtures list is sorted i = len(baseid) + 1
# by scope, scopenum 0 is session nextchar = nodeid[i:i+1]
self._autofixtures.sort( if nextchar and nextchar not in ":/":
key=lambda x: self.arg2fixturedefs[x][-1].scopenum) continue
defaultfixtures = defaultfixtures + tuple(self._autofixtures) autousenames.extend(basenames)
self._defaultfixtures = defaultfixtures # make sure autousenames are sorted by scope, scopenum 0 is session
return defaultfixtures autousenames.sort(
key=lambda x: self._arg2fixturedefs[x][-1].scopenum)
return autousenames
def getfixtureclosure(self, fixturenames, parentnode): def getfixtureclosure(self, fixturenames, parentnode):
# collect the closure of all fixtures , starting with the given # collect the closure of all fixtures , starting with the given
@ -1423,7 +1425,7 @@ class FixtureManager:
# (discovering matching fixtures for a given name/node is expensive) # (discovering matching fixtures for a given name/node is expensive)
parentid = parentnode.nodeid parentid = parentnode.nodeid
fixturenames_closure = list(self.getdefaultfixtures()) fixturenames_closure = self._getautousenames(parentid)
def merge(otherlist): def merge(otherlist):
for arg in otherlist: for arg in otherlist:
if arg not in fixturenames_closure: if arg not in fixturenames_closure:
@ -1484,10 +1486,16 @@ class FixtureManager:
for fin in l: for fin in l:
fin() fin()
def _parsefactories(self, holderobj, nodeid, unittest=False): def parsefactories(self, node_or_obj, nodeid=None, unittest=False):
if nodeid is not None:
holderobj = node_or_obj
else:
holderobj = node_or_obj.obj
nodeid = node_or_obj.nodeid
if holderobj in self._holderobjseen: if holderobj in self._holderobjseen:
return return
self._holderobjseen.add(holderobj) self._holderobjseen.add(holderobj)
autousenames = []
for name in dir(holderobj): for name in dir(holderobj):
obj = getattr(holderobj, name) obj = getattr(holderobj, name)
if not callable(obj): if not callable(obj):
@ -1509,18 +1517,16 @@ class FixtureManager:
fixturedef = FixtureDef(self, nodeid, name, obj, fixturedef = FixtureDef(self, nodeid, name, obj,
marker.scope, marker.params, marker.scope, marker.params,
unittest=unittest) unittest=unittest)
faclist = self.arg2fixturedefs.setdefault(name, []) faclist = self._arg2fixturedefs.setdefault(name, [])
faclist.append(fixturedef) faclist.append(fixturedef)
if marker.autouse: if marker.autouse:
self._autofixtures.append(name) autousenames.append(name)
try: if autousenames:
del self._defaultfixtures self._nodeid_and_autousenames.append((nodeid, autousenames))
except AttributeError:
pass
def getfixturedefs(self, argname, nodeid): def getfixturedefs(self, argname, nodeid):
try: try:
fixturedefs = self.arg2fixturedefs[argname] fixturedefs = self._arg2fixturedefs[argname]
except KeyError: except KeyError:
return None return None
else: else:

View File

@ -22,8 +22,7 @@ def pytest_pycollect_makeitem(collector, name, obj):
class UnitTestCase(pytest.Class): class UnitTestCase(pytest.Class):
def collect(self): def collect(self):
self.session._fixturemanager._parsefactories(self.obj, self.nodeid, self.session._fixturemanager.parsefactories(self, unittest=True)
unittest=True)
loader = py.std.unittest.TestLoader() loader = py.std.unittest.TestLoader()
module = self.getparent(pytest.Module).obj module = self.getparent(pytest.Module).obj
cls = self.obj cls = self.obj

View File

@ -24,7 +24,7 @@ def main():
name='pytest', name='pytest',
description='py.test: simple powerful testing with Python', description='py.test: simple powerful testing with Python',
long_description = long_description, long_description = long_description,
version='2.3.0.dev23', version='2.3.0.dev24',
url='http://pytest.org', url='http://pytest.org',
license='MIT license', license='MIT license',
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],

View File

@ -2026,14 +2026,14 @@ class TestSetupDiscovery:
def test_parsefactories_conftest(self, testdir): def test_parsefactories_conftest(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
def test_check_setup(item, fm): def test_check_setup(item, fm):
assert len(fm._autofixtures) == 2 autousenames = fm._getautousenames(item.nodeid)
assert "perfunction2" in fm._autofixtures assert len(autousenames) == 2
assert "perfunction" in fm._autofixtures assert "perfunction2" in autousenames
assert "perfunction" in autousenames
""") """)
reprec = testdir.inline_run("-s") reprec = testdir.inline_run("-s")
reprec.assertoutcome(passed=1) reprec.assertoutcome(passed=1)
@pytest.mark.xfail
def test_two_classes_separated_autouse(self, testdir): def test_two_classes_separated_autouse(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
@ -2113,6 +2113,45 @@ class TestSetupDiscovery:
reprec = testdir.inline_run("-s") reprec = testdir.inline_run("-s")
reprec.assertoutcome(failed=0, passed=0) reprec.assertoutcome(failed=0, passed=0)
def test_autouse_in_conftests(self, testdir):
a = testdir.mkdir("a")
b = testdir.mkdir("a1")
conftest = testdir.makeconftest("""
import pytest
@pytest.fixture(autouse=True)
def hello():
xxx
""")
conftest.move(a.join(conftest.basename))
a.join("test_something.py").write("def test_func(): pass")
b.join("test_otherthing.py").write("def test_func(): pass")
result = testdir.runpytest()
result.stdout.fnmatch_lines("""
*1 passed*1 error*
""")
def test_autouse_in_module_and_two_classes(self, testdir):
testdir.makepyfile("""
import pytest
l = []
@pytest.fixture(autouse=True)
def append1():
l.append("module")
def test_x():
assert l == ["module"]
class TestA:
@pytest.fixture(autouse=True)
def append2(self):
l.append("A")
def test_hello(self):
assert l == ["module", "module", "A"], l
class TestA2:
def test_world(self):
assert l == ["module", "module", "A", "module"], l
""")
reprec = testdir.inline_run()
reprec.assertoutcome(passed=3)
class TestSetupManagement: class TestSetupManagement:
def test_funcarg_and_setup(self, testdir): def test_funcarg_and_setup(self, testdir):
@ -2220,7 +2259,7 @@ class TestSetupManagement:
def test_2(self): def test_2(self):
pass pass
""") """)
reprec = testdir.inline_run("-v",) reprec = testdir.inline_run("-v","-s")
reprec.assertoutcome(passed=8) reprec.assertoutcome(passed=8)
config = reprec.getcalls("pytest_unconfigure")[0].config config = reprec.getcalls("pytest_unconfigure")[0].config
l = config._conftest.getconftestmodules(p)[0].l l = config._conftest.getconftestmodules(p)[0].l