Fix regression due to different cases on Windows (#5840)
Fix regression due to different cases on Windows
This commit is contained in:
		
						commit
						9422e10322
					
				| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					Windows: Fix regression with conftest whose qualified name contains uppercase
 | 
				
			||||||
 | 
					characters (introduced by #5792).
 | 
				
			||||||
| 
						 | 
					@ -30,7 +30,6 @@ from _pytest._code import filter_traceback
 | 
				
			||||||
from _pytest.compat import importlib_metadata
 | 
					from _pytest.compat import importlib_metadata
 | 
				
			||||||
from _pytest.outcomes import fail
 | 
					from _pytest.outcomes import fail
 | 
				
			||||||
from _pytest.outcomes import Skipped
 | 
					from _pytest.outcomes import Skipped
 | 
				
			||||||
from _pytest.pathlib import unique_path
 | 
					 | 
				
			||||||
from _pytest.warning_types import PytestConfigWarning
 | 
					from _pytest.warning_types import PytestConfigWarning
 | 
				
			||||||
 | 
					
 | 
				
			||||||
hookimpl = HookimplMarker("pytest")
 | 
					hookimpl = HookimplMarker("pytest")
 | 
				
			||||||
| 
						 | 
					@ -367,7 +366,7 @@ class PytestPluginManager(PluginManager):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        current = py.path.local()
 | 
					        current = py.path.local()
 | 
				
			||||||
        self._confcutdir = (
 | 
					        self._confcutdir = (
 | 
				
			||||||
            unique_path(current.join(namespace.confcutdir, abs=True))
 | 
					            current.join(namespace.confcutdir, abs=True)
 | 
				
			||||||
            if namespace.confcutdir
 | 
					            if namespace.confcutdir
 | 
				
			||||||
            else None
 | 
					            else None
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
| 
						 | 
					@ -406,13 +405,11 @@ class PytestPluginManager(PluginManager):
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            directory = path
 | 
					            directory = path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        directory = unique_path(directory)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # XXX these days we may rather want to use config.rootdir
 | 
					        # XXX these days we may rather want to use config.rootdir
 | 
				
			||||||
        # and allow users to opt into looking into the rootdir parent
 | 
					        # and allow users to opt into looking into the rootdir parent
 | 
				
			||||||
        # directories instead of requiring to specify confcutdir
 | 
					        # directories instead of requiring to specify confcutdir
 | 
				
			||||||
        clist = []
 | 
					        clist = []
 | 
				
			||||||
        for parent in directory.parts():
 | 
					        for parent in directory.realpath().parts():
 | 
				
			||||||
            if self._confcutdir and self._confcutdir.relto(parent):
 | 
					            if self._confcutdir and self._confcutdir.relto(parent):
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
            conftestpath = parent.join("conftest.py")
 | 
					            conftestpath = parent.join("conftest.py")
 | 
				
			||||||
| 
						 | 
					@ -432,12 +429,14 @@ class PytestPluginManager(PluginManager):
 | 
				
			||||||
        raise KeyError(name)
 | 
					        raise KeyError(name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _importconftest(self, conftestpath):
 | 
					    def _importconftest(self, conftestpath):
 | 
				
			||||||
        # Use realpath to avoid loading the same conftest twice
 | 
					        # Use a resolved Path object as key to avoid loading the same conftest twice
 | 
				
			||||||
        # with build systems that create build directories containing
 | 
					        # with build systems that create build directories containing
 | 
				
			||||||
        # symlinks to actual files.
 | 
					        # symlinks to actual files.
 | 
				
			||||||
        conftestpath = unique_path(conftestpath)
 | 
					        # 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:
 | 
					        try:
 | 
				
			||||||
            return self._conftestpath2mod[conftestpath]
 | 
					            return self._conftestpath2mod[key]
 | 
				
			||||||
        except KeyError:
 | 
					        except KeyError:
 | 
				
			||||||
            pkgpath = conftestpath.pypkgpath()
 | 
					            pkgpath = conftestpath.pypkgpath()
 | 
				
			||||||
            if pkgpath is None:
 | 
					            if pkgpath is None:
 | 
				
			||||||
| 
						 | 
					@ -454,7 +453,7 @@ class PytestPluginManager(PluginManager):
 | 
				
			||||||
                raise ConftestImportFailure(conftestpath, sys.exc_info())
 | 
					                raise ConftestImportFailure(conftestpath, sys.exc_info())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self._conftest_plugins.add(mod)
 | 
					            self._conftest_plugins.add(mod)
 | 
				
			||||||
            self._conftestpath2mod[conftestpath] = mod
 | 
					            self._conftestpath2mod[key] = mod
 | 
				
			||||||
            dirpath = conftestpath.dirpath()
 | 
					            dirpath = conftestpath.dirpath()
 | 
				
			||||||
            if dirpath in self._dirpath2confmods:
 | 
					            if dirpath in self._dirpath2confmods:
 | 
				
			||||||
                for path, mods in self._dirpath2confmods.items():
 | 
					                for path, mods in self._dirpath2confmods.items():
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,6 @@ from functools import partial
 | 
				
			||||||
from os.path import expanduser
 | 
					from os.path import expanduser
 | 
				
			||||||
from os.path import expandvars
 | 
					from os.path import expandvars
 | 
				
			||||||
from os.path import isabs
 | 
					from os.path import isabs
 | 
				
			||||||
from os.path import normcase
 | 
					 | 
				
			||||||
from os.path import sep
 | 
					from os.path import sep
 | 
				
			||||||
from posixpath import sep as posix_sep
 | 
					from posixpath import sep as posix_sep
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -335,12 +334,3 @@ def fnmatch_ex(pattern, path):
 | 
				
			||||||
def parts(s):
 | 
					def parts(s):
 | 
				
			||||||
    parts = s.split(sep)
 | 
					    parts = s.split(sep)
 | 
				
			||||||
    return {sep.join(parts[: i + 1]) or sep for i in range(len(parts))}
 | 
					    return {sep.join(parts[: i + 1]) or sep for i in range(len(parts))}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def unique_path(path):
 | 
					 | 
				
			||||||
    """Returns a unique path in case-insensitive (but case-preserving) file
 | 
					 | 
				
			||||||
    systems such as Windows.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This is needed only for ``py.path.local``; ``pathlib.Path`` handles this
 | 
					 | 
				
			||||||
    natively with ``resolve()``."""
 | 
					 | 
				
			||||||
    return type(path)(normcase(str(path.realpath())))
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,12 @@
 | 
				
			||||||
import os.path
 | 
					import os
 | 
				
			||||||
import textwrap
 | 
					import textwrap
 | 
				
			||||||
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import py
 | 
					import py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pytest
 | 
					import pytest
 | 
				
			||||||
from _pytest.config import PytestPluginManager
 | 
					from _pytest.config import PytestPluginManager
 | 
				
			||||||
from _pytest.main import ExitCode
 | 
					from _pytest.main import ExitCode
 | 
				
			||||||
from _pytest.pathlib import unique_path
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def ConftestWithSetinitial(path):
 | 
					def ConftestWithSetinitial(path):
 | 
				
			||||||
| 
						 | 
					@ -143,11 +143,11 @@ def test_conftestcutdir(testdir):
 | 
				
			||||||
    # but we can still import a conftest directly
 | 
					    # but we can still import a conftest directly
 | 
				
			||||||
    conftest._importconftest(conf)
 | 
					    conftest._importconftest(conf)
 | 
				
			||||||
    values = conftest._getconftestmodules(conf.dirpath())
 | 
					    values = conftest._getconftestmodules(conf.dirpath())
 | 
				
			||||||
    assert values[0].__file__.startswith(str(unique_path(conf)))
 | 
					    assert values[0].__file__.startswith(str(conf))
 | 
				
			||||||
    # and all sub paths get updated properly
 | 
					    # and all sub paths get updated properly
 | 
				
			||||||
    values = conftest._getconftestmodules(p)
 | 
					    values = conftest._getconftestmodules(p)
 | 
				
			||||||
    assert len(values) == 1
 | 
					    assert len(values) == 1
 | 
				
			||||||
    assert values[0].__file__.startswith(str(unique_path(conf)))
 | 
					    assert values[0].__file__.startswith(str(conf))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_conftestcutdir_inplace_considered(testdir):
 | 
					def test_conftestcutdir_inplace_considered(testdir):
 | 
				
			||||||
| 
						 | 
					@ -156,7 +156,7 @@ def test_conftestcutdir_inplace_considered(testdir):
 | 
				
			||||||
    conftest_setinitial(conftest, [conf.dirpath()], confcutdir=conf.dirpath())
 | 
					    conftest_setinitial(conftest, [conf.dirpath()], confcutdir=conf.dirpath())
 | 
				
			||||||
    values = conftest._getconftestmodules(conf.dirpath())
 | 
					    values = conftest._getconftestmodules(conf.dirpath())
 | 
				
			||||||
    assert len(values) == 1
 | 
					    assert len(values) == 1
 | 
				
			||||||
    assert values[0].__file__.startswith(str(unique_path(conf)))
 | 
					    assert values[0].__file__.startswith(str(conf))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("name", "test tests whatever .dotdir".split())
 | 
					@pytest.mark.parametrize("name", "test tests whatever .dotdir".split())
 | 
				
			||||||
| 
						 | 
					@ -165,11 +165,12 @@ def test_setinitial_conftest_subdirs(testdir, name):
 | 
				
			||||||
    subconftest = sub.ensure("conftest.py")
 | 
					    subconftest = sub.ensure("conftest.py")
 | 
				
			||||||
    conftest = PytestPluginManager()
 | 
					    conftest = PytestPluginManager()
 | 
				
			||||||
    conftest_setinitial(conftest, [sub.dirpath()], confcutdir=testdir.tmpdir)
 | 
					    conftest_setinitial(conftest, [sub.dirpath()], confcutdir=testdir.tmpdir)
 | 
				
			||||||
 | 
					    key = Path(str(subconftest)).resolve()
 | 
				
			||||||
    if name not in ("whatever", ".dotdir"):
 | 
					    if name not in ("whatever", ".dotdir"):
 | 
				
			||||||
        assert unique_path(subconftest) in conftest._conftestpath2mod
 | 
					        assert key in conftest._conftestpath2mod
 | 
				
			||||||
        assert len(conftest._conftestpath2mod) == 1
 | 
					        assert len(conftest._conftestpath2mod) == 1
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        assert subconftest not in conftest._conftestpath2mod
 | 
					        assert key not in conftest._conftestpath2mod
 | 
				
			||||||
        assert len(conftest._conftestpath2mod) == 0
 | 
					        assert len(conftest._conftestpath2mod) == 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -282,7 +283,7 @@ def test_conftest_symlink_files(testdir):
 | 
				
			||||||
    reason="only relevant for case insensitive file systems",
 | 
					    reason="only relevant for case insensitive file systems",
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
def test_conftest_badcase(testdir):
 | 
					def test_conftest_badcase(testdir):
 | 
				
			||||||
    """Check conftest.py loading when directory casing is wrong."""
 | 
					    """Check conftest.py loading when directory casing is wrong (#5792)."""
 | 
				
			||||||
    testdir.tmpdir.mkdir("JenkinsRoot").mkdir("test")
 | 
					    testdir.tmpdir.mkdir("JenkinsRoot").mkdir("test")
 | 
				
			||||||
    source = {"setup.py": "", "test/__init__.py": "", "test/conftest.py": ""}
 | 
					    source = {"setup.py": "", "test/__init__.py": "", "test/conftest.py": ""}
 | 
				
			||||||
    testdir.makepyfile(**{"JenkinsRoot/%s" % k: v for k, v in source.items()})
 | 
					    testdir.makepyfile(**{"JenkinsRoot/%s" % k: v for k, v in source.items()})
 | 
				
			||||||
| 
						 | 
					@ -292,6 +293,16 @@ def test_conftest_badcase(testdir):
 | 
				
			||||||
    assert result.ret == ExitCode.NO_TESTS_COLLECTED
 | 
					    assert result.ret == ExitCode.NO_TESTS_COLLECTED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_conftest_uppercase(testdir):
 | 
				
			||||||
 | 
					    """Check conftest.py whose qualified name contains uppercase characters (#5819)"""
 | 
				
			||||||
 | 
					    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):
 | 
					def test_no_conftest(testdir):
 | 
				
			||||||
    testdir.makeconftest("assert 0")
 | 
					    testdir.makeconftest("assert 0")
 | 
				
			||||||
    result = testdir.runpytest("--noconftest")
 | 
					    result = testdir.runpytest("--noconftest")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue