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

[8.0.x] Fix collection of short paths on Windows #12024

Merged
merged 1 commit into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/11895.bugfix.rst
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,10 @@
# 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])

Check warning on line 908 in src/_pytest/main.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/main.py#L908

Added line #L908 was not covered by tests
# 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
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,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 @@ -1758,3 +1760,29 @@

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()

Check warning on line 1768 in testing/test_collection.py

View check run for this annotation

Codecov / codecov/patch

testing/test_collection.py#L1768

Added line #L1768 was not covered by tests
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}

Check warning on line 1788 in testing/test_collection.py

View check run for this annotation

Codecov / codecov/patch

testing/test_collection.py#L1785-L1788

Added lines #L1785 - L1788 were not covered by tests
Loading