Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix PytestPluginManager._is_in_confcutdir on Windows #12006

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 9 additions & 7 deletions src/_pytest/config/__init__.py
Expand Up @@ -580,11 +580,9 @@ def _set_initial_conftests(
def _is_in_confcutdir(self, path: Path) -> bool:
"""Whether a path is within the confcutdir.

When false, should not load conftest.
When false, should not load conftest or recurse into path for collection.
"""
if self._confcutdir is None:
return True
return path not in self._confcutdir.parents
return path_within_confcutdir(path=path, confcutdir=self._confcutdir)

def _try_load_conftest(
self, anchor: Path, importmode: Union[str, ImportMode], rootpath: Path
Expand All @@ -609,9 +607,6 @@ def _loadconftestmodules(
if directory in self._dirpath2confmods:
return

# XXX these days we may rather want to use config.rootpath
# and allow users to opt into looking into the rootdir parent
# directories instead of requiring to specify confcutdir.
clist = []
for parent in reversed((directory, *directory.parents)):
if self._is_in_confcutdir(parent):
Expand Down Expand Up @@ -1908,3 +1903,10 @@ def apply_warning_filters(

for arg in cmdline_filters:
warnings.filterwarnings(*parse_warning_filter(arg, escape=True))


def path_within_confcutdir(*, path: Path, confcutdir: Optional[Path]) -> bool:
# Extracted into a function for unit-testing.
if confcutdir is None:
return True
return path.is_relative_to(confcutdir)
55 changes: 54 additions & 1 deletion testing/test_pluginmanager.py
@@ -1,12 +1,15 @@
# mypy: allow-untyped-defs
import os
from pathlib import Path
import shutil
import sys
import types
from typing import List
from typing import Optional

from _pytest.config import Config
from _pytest.config import ExitCode
from _pytest.config import path_within_confcutdir
from _pytest.config import PytestPluginManager
from _pytest.config.exceptions import UsageError
from _pytest.main import Session
Expand Down Expand Up @@ -404,7 +407,7 @@ def test_consider_conftest_deps(
pytestpm.consider_conftest(mod, registration_name="unused")


class TestPytestPluginManagerBootstrapming:
class TestPytestPluginManagerBootstrapping:
def test_preparse_args(self, pytestpm: PytestPluginManager) -> None:
pytest.raises(
ImportError, lambda: pytestpm.consider_preparse(["xyz", "-p", "hello123"])
Expand Down Expand Up @@ -464,3 +467,53 @@ def test_blocked_plugin_can_be_used(self, pytestpm: PytestPluginManager) -> None
assert pytestpm.has_plugin("abc")
assert not pytestpm.is_blocked("abc")
assert not pytestpm.is_blocked("pytest_abc")


skip_if_win = pytest.mark.skipif(
not sys.platform.startswith("win"), reason="Windows only"
)


@pytest.mark.parametrize(
"path, confcutdir, expected",
[
(Path("/projects/app/tests"), Path("/projects/app"), True),
(Path("/projects/app"), Path("/projects/app"), True),
(Path("/projects"), Path("/projects/app"), False),
(Path("/"), Path("/projects/app"), False),
pytest.param(
Path("e:/projects/app/tests"),
Path("e:/projects/app"),
True,
marks=skip_if_win,
),
pytest.param(
Path("e:/projects/app"),
Path("e:/projects/app"),
True,
marks=skip_if_win,
),
pytest.param(
Path("e:/"),
Path("e:/projects/app"),
False,
marks=skip_if_win,
),
pytest.param(
Path("c:/testing"),
Path("e:/projects/app"),
False,
marks=skip_if_win,
),
pytest.param(
Path("c:/projects/app"),
Path("e:/projects/app"),
False,
marks=skip_if_win,
),
],
)
def test_path_within_confcutdir(
path: Path, confcutdir: Optional[Path], expected: bool
) -> None:
assert path_within_confcutdir(path=path, confcutdir=confcutdir) == expected