From d35b802805b210c6d5281864d663f53c6a72f153 Mon Sep 17 00:00:00 2001 From: Nauman Ahmed <90570675+nauman897@users.noreply.github.com> Date: Sun, 8 Sep 2024 19:32:23 +0500 Subject: [PATCH] Fix issue with slashes being turned into backslashes on Windows (#12760) Fix #12745 --- AUTHORS | 1 + changelog/12745.bugfix.rst | 1 + src/_pytest/config/__init__.py | 8 ++++++-- testing/test_terminal.py | 35 ++++++++++++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 changelog/12745.bugfix.rst diff --git a/AUTHORS b/AUTHORS index 6da8cbfef80..374e6ad9bcc 100644 --- a/AUTHORS +++ b/AUTHORS @@ -301,6 +301,7 @@ mrbean-bremen Nathan Goldbaum Nathaniel Compton Nathaniel Waisbrot +Nauman Ahmed Ned Batchelder Neil Martin Neven Mundar diff --git a/changelog/12745.bugfix.rst b/changelog/12745.bugfix.rst new file mode 100644 index 00000000000..420be931ce9 --- /dev/null +++ b/changelog/12745.bugfix.rst @@ -0,0 +1 @@ +Fixed an issue with backslashes being incorrectly converted in nodeid paths on Windows, ensuring consistent path handling across environments. diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 29e5a58c4ac..c53661dbeb5 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -1178,8 +1178,12 @@ def notify_exception( def cwd_relative_nodeid(self, nodeid: str) -> str: # nodeid's are relative to the rootpath, compute relative to cwd. if self.invocation_params.dir != self.rootpath: - fullpath = self.rootpath / nodeid - nodeid = bestrelpath(self.invocation_params.dir, fullpath) + base_path_part, *nodeid_part = nodeid.split("::") + # Only process path part + fullpath = self.rootpath / base_path_part + relative_path = bestrelpath(self.invocation_params.dir, fullpath) + + nodeid = "::".join([relative_path, *nodeid_part]) return nodeid @classmethod diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 11ad623fb6b..14c152d6123 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -3067,3 +3067,38 @@ def test_pass(): "*= 1 xpassed in * =*", ] ) + + +class TestNodeIDHandling: + def test_nodeid_handling_windows_paths(self, pytester: Pytester, tmp_path) -> None: + """Test the correct handling of Windows-style paths with backslashes.""" + pytester.makeini("[pytest]") # Change `config.rootpath` + + test_path = pytester.path / "tests" / "test_foo.py" + test_path.parent.mkdir() + os.chdir(test_path.parent) # Change `config.invocation_params.dir` + + test_path.write_text( + textwrap.dedent( + """ + import pytest + + @pytest.mark.parametrize("a", ["x/y", "C:/path", "\\\\", "C:\\\\path", "a::b/"]) + def test_x(a): + assert False + """ + ), + encoding="utf-8", + ) + + result = pytester.runpytest("-v") + + result.stdout.re_match_lines( + [ + r".*test_foo.py::test_x\[x/y\] .*FAILED.*", + r".*test_foo.py::test_x\[C:/path\] .*FAILED.*", + r".*test_foo.py::test_x\[\\\\\] .*FAILED.*", + r".*test_foo.py::test_x\[C:\\\\path\] .*FAILED.*", + r".*test_foo.py::test_x\[a::b/\] .*FAILED.*", + ] + )