From f44cd3f1e7850c0ff9940c40d723b8b6f8985999 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 11 Sep 2019 20:09:08 -0300 Subject: [PATCH] Use Path() objects to store conftest files Using Path().resolve() is better than py.path.realpath because it resolves to the correct path/drive in case-insensitive file systems (#5792): >>> from py.path import local >>> from pathlib import Path >>> >>> local('d:\\projects').realpath() local('d:\\projects') >>> Path('d:\\projects').resolve() WindowsPath('D:/projects') Fix #5819 --- src/_pytest/config/__init__.py | 15 +++++++++------ testing/test_conftest.py | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index b861563e99b..e39c63c4b52 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -414,10 +414,7 @@ def _getconftestmodules(self, path): continue conftestpath = parent.join("conftest.py") if conftestpath.isfile(): - # Use realpath to avoid loading the same conftest twice - # with build systems that create build directories containing - # symlinks to actual files. - mod = self._importconftest(conftestpath.realpath()) + mod = self._importconftest(conftestpath) clist.append(mod) self._dirpath2confmods[directory] = clist return clist @@ -432,8 +429,14 @@ def _rget_with_confmod(self, name, path): raise KeyError(name) def _importconftest(self, conftestpath): + # Use a resolved Path object as key to avoid loading the same conftest twice + # with build systems that create build directories containing + # symlinks to actual files. + # Using Path().resolve() is better than py.path.realpath because + # it resolves to the correct path/drive in case-insensitive file systems (#5792) + key = Path(str(conftestpath)).resolve() try: - return self._conftestpath2mod[conftestpath] + return self._conftestpath2mod[key] except KeyError: pkgpath = conftestpath.pypkgpath() if pkgpath is None: @@ -450,7 +453,7 @@ def _importconftest(self, conftestpath): raise ConftestImportFailure(conftestpath, sys.exc_info()) self._conftest_plugins.add(mod) - self._conftestpath2mod[conftestpath] = mod + self._conftestpath2mod[key] = mod dirpath = conftestpath.dirpath() if dirpath in self._dirpath2confmods: for path, mods in self._dirpath2confmods.items(): diff --git a/testing/test_conftest.py b/testing/test_conftest.py index 447416f1076..4bb635913c8 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -1,4 +1,6 @@ +import os import textwrap +from pathlib import Path import py @@ -163,11 +165,12 @@ def test_setinitial_conftest_subdirs(testdir, name): subconftest = sub.ensure("conftest.py") conftest = PytestPluginManager() conftest_setinitial(conftest, [sub.dirpath()], confcutdir=testdir.tmpdir) + key = Path(str(subconftest)).resolve() if name not in ("whatever", ".dotdir"): - assert subconftest in conftest._conftestpath2mod + assert key in conftest._conftestpath2mod assert len(conftest._conftestpath2mod) == 1 else: - assert subconftest not in conftest._conftestpath2mod + assert key not in conftest._conftestpath2mod assert len(conftest._conftestpath2mod) == 0 @@ -275,6 +278,31 @@ def fixture(): assert result.ret == ExitCode.OK +@pytest.mark.skipif( + os.path.normcase("x") != os.path.normcase("X"), + reason="only relevant for case insensitive file systems", +) +def test_conftest_badcase(testdir): + """Check conftest.py loading when directory casing is wrong.""" + testdir.tmpdir.mkdir("JenkinsRoot").mkdir("test") + source = {"setup.py": "", "test/__init__.py": "", "test/conftest.py": ""} + testdir.makepyfile(**{"JenkinsRoot/%s" % k: v for k, v in source.items()}) + + testdir.tmpdir.join("jenkinsroot/test").chdir() + result = testdir.runpytest() + assert result.ret == ExitCode.NO_TESTS_COLLECTED + + +def test_conftest_uppercase(testdir): + """Check conftest.py loading when directory casing is wrong.""" + source = {"__init__.py": "", "Foo/conftest.py": "", "Foo/__init__.py": ""} + testdir.makepyfile(**source) + + testdir.tmpdir.chdir() + result = testdir.runpytest() + assert result.ret == ExitCode.NO_TESTS_COLLECTED + + def test_no_conftest(testdir): testdir.makeconftest("assert 0") result = testdir.runpytest("--noconftest")