diff --git a/AUTHORS b/AUTHORS index f78c4b3f9..4c4d68df1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -283,6 +283,7 @@ Mike Hoyle (hoylemd) Mike Lundy Milan Lesnek Miro HronĨok +mrbean-bremen Nathaniel Compton Nathaniel Waisbrot Ned Batchelder diff --git a/changelog/12039.bugfix.rst b/changelog/12039.bugfix.rst new file mode 100644 index 000000000..038d9b755 --- /dev/null +++ b/changelog/12039.bugfix.rst @@ -0,0 +1 @@ +Fixed a regression in 8.0.2 where tests have been collected multiple times in the CI under Windows \ No newline at end of file diff --git a/src/_pytest/main.py b/src/_pytest/main.py index b7ed72ddc..67d6e142a 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -924,7 +924,14 @@ class Session(nodes.Collector): 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]) + same_file = os.path.samefile(node.path, matchparts[0]) + # we don't want to find links, so we at least + # exclude symlinks to regular directories + is_match = ( + same_file and + os.path.islink(node.path) == os.path.islink(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 diff --git a/testing/test_collection.py b/testing/test_collection.py index fbc8543e9..910dba3e6 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -1765,7 +1765,7 @@ def test_does_not_crash_on_recursive_symlink(pytester: Pytester) -> None: @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.""" + """Reproducer for #11895: short paths not collected on Windows.""" short_path = tempfile.mkdtemp() if "~" not in short_path: # pragma: no cover if running_on_ci(): @@ -1832,3 +1832,12 @@ def test_pyargs_collection_tree(pytester: Pytester, monkeypatch: MonkeyPatch) -> ], consecutive=True, ) + + +def test_collect_symlinks(pytester: Pytester, tmpdir) -> None: + """Regression test for #12039: Tests collected multiple times under Windows.""" + test_file = Path(tmpdir) / "symlink_collection_test.py" + test_file.write_text("def test(): pass", encoding="UTF-8") + result = pytester.runpytest(tmpdir) + # this failed in CI only (GitHub actions) + assert result.parseoutcomes() == {"passed": 1}