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:
parent
ab4183d400
commit
cc2337af3a
|
@ -1,2 +1,2 @@
|
||||||
#
|
#
|
||||||
__version__ = '2.3.0.dev23'
|
__version__ = '2.3.0.dev24'
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -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'],
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue