Commits

holger krekel  committed 5f7bfa9

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

  • Participants
  • Parent commits eb495bd

Comments (0)

Files changed (5)

File _pytest/__init__.py

 #
-__version__ = '2.3.0.dev23'
+__version__ = '2.3.0.dev24'

File _pytest/python.py

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

File _pytest/unittest.py

 class UnitTestCase(pytest.Class):
 
     def collect(self):
-        self.session._fixturemanager._parsefactories(self.obj, self.nodeid,
-                unittest=True)
+        self.session._fixturemanager.parsefactories(self, unittest=True)
         loader = py.std.unittest.TestLoader()
         module = self.getparent(pytest.Module).obj
         cls = self.obj
         name='pytest',
         description='py.test: simple powerful testing with Python',
         long_description = long_description,
-        version='2.3.0.dev23',
+        version='2.3.0.dev24',
         url='http://pytest.org',
         license='MIT license',
         platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],

File testing/test_python.py

     def test_parsefactories_conftest(self, testdir):
         testdir.makepyfile("""
             def test_check_setup(item, fm):
-                assert len(fm._autofixtures) == 2
-                assert "perfunction2" in fm._autofixtures
-                assert "perfunction" in fm._autofixtures
+                autousenames = fm._getautousenames(item.nodeid)
+                assert len(autousenames) == 2
+                assert "perfunction2" in autousenames
+                assert "perfunction" in autousenames
         """)
         reprec = testdir.inline_run("-s")
         reprec.assertoutcome(passed=1)
 
-    @pytest.mark.xfail
     def test_two_classes_separated_autouse(self, testdir):
         testdir.makepyfile("""
             import pytest
         reprec = testdir.inline_run("-s")
         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:
     def test_funcarg_and_setup(self, testdir):
                 def test_2(self):
                     pass
         """)
-        reprec = testdir.inline_run("-v",)
+        reprec = testdir.inline_run("-v","-s")
         reprec.assertoutcome(passed=8)
         config = reprec.getcalls("pytest_unconfigure")[0].config
         l = config._conftest.getconftestmodules(p)[0].l