Skip to content

Commit

Permalink
Fix collection of short paths on Windows (pytest-dev#11936)
Browse files Browse the repository at this point in the history
Passing a short path in the command line was causing the matchparts check to fail, because ``Path(short_path) != Path(long_path)``.

Using ``os.path.samefile`` as fallback ensures the comparsion works on Windows when comparing short/long paths.

Fix pytest-dev#11895
  • Loading branch information
nicoddemus authored and flying-sheep committed Apr 9, 2024
1 parent 3c5d013 commit dee5817
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 0 deletions.
1 change: 1 addition & 0 deletions changelog/11895.bugfix.rst
@@ -0,0 +1 @@
Fix collection on Windows where initial paths contain the short version of a path (for example ``c:\PROGRA~1\tests``).
4 changes: 4 additions & 0 deletions src/_pytest/main.py
Expand Up @@ -906,6 +906,10 @@ def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]:
# Path part e.g. `/a/b/` in `/a/b/test_file.py::TestIt::test_it`.
if isinstance(matchparts[0], Path):
is_match = node.path == matchparts[0]
if sys.platform == "win32" and not is_match:
# In case the file paths do not match, fallback to samefile() to
# account for short-paths on Windows (#11895).
is_match = os.path.samefile(node.path, matchparts[0])
# Name part e.g. `TestIt` in `/a/b/test_file.py::TestIt::test_it`.
else:
# TODO: Remove parametrized workaround once collection structure contains
Expand Down
28 changes: 28 additions & 0 deletions testing/test_collection.py
Expand Up @@ -4,9 +4,11 @@
import pprint
import shutil
import sys
import tempfile
import textwrap
from typing import List

from _pytest.assertion.util import running_on_ci
from _pytest.config import ExitCode
from _pytest.fixtures import FixtureRequest
from _pytest.main import _in_venv
Expand Down Expand Up @@ -1759,3 +1761,29 @@ def test_foo(): assert True

assert result.ret == ExitCode.OK
assert result.parseoutcomes() == {"passed": 1}


@pytest.mark.skipif(not sys.platform.startswith("win"), reason="Windows only")
def test_collect_short_file_windows(pytester: Pytester) -> None:
"""Reproducer for #11895: short paths not colleced on Windows."""
short_path = tempfile.mkdtemp()
if "~" not in short_path: # pragma: no cover
if running_on_ci():
# On CI, we are expecting that under the current GitHub actions configuration,
# tempfile.mkdtemp() is producing short paths, so we want to fail to prevent
# this from silently changing without us noticing.
pytest.fail(
f"tempfile.mkdtemp() failed to produce a short path on CI: {short_path}"
)
else:
# We want to skip failing this test locally in this situation because
# depending on the local configuration tempfile.mkdtemp() might not produce a short path:
# For example, user might have configured %TEMP% exactly to avoid generating short paths.
pytest.skip(
f"tempfile.mkdtemp() failed to produce a short path: {short_path}, skipping"
)

test_file = Path(short_path).joinpath("test_collect_short_file_windows.py")
test_file.write_text("def test(): pass", encoding="UTF-8")
result = pytester.runpytest(short_path)
assert result.parseoutcomes() == {"passed": 1}

0 comments on commit dee5817

Please sign in to comment.