diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index 10326e1c9a6..bfc5429666b 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -1095,4 +1095,10 @@ def samefile_nofollow(p1: Path, p2: Path) -> bool: Unlike Path.samefile(), does not resolve symlinks. """ - return os.path.samestat(p1.lstat(), p2.lstat()) + s1, s2 = p1.lstat(), p2.lstat() + # Some network filesystems report a zero inode for every path. In that + # case samestat() cannot distinguish different files and would make every + # collected path match the requested one. + if not s1.st_ino or not s2.st_ino: + return False + return os.path.samestat(s1, s2) diff --git a/testing/test_pathlib.py b/testing/test_pathlib.py index bd85b7e8fb4..d8630db0b56 100644 --- a/testing/test_pathlib.py +++ b/testing/test_pathlib.py @@ -38,6 +38,7 @@ from _pytest.pathlib import resolve_package_path from _pytest.pathlib import resolve_pkg_root_and_module_name from _pytest.pathlib import safe_exists +from _pytest.pathlib import samefile_nofollow from _pytest.pathlib import scandir from _pytest.pathlib import spec_matches_module_path from _pytest.pathlib import symlink_or_skip @@ -570,6 +571,20 @@ def test_samefile_false_negatives(tmp_path: Path, monkeypatch: MonkeyPatch) -> N assert getattr(module, "foo")() == 42 +@pytest.mark.parametrize("inodes", [(0, 0), (0, 1), (1, 0)]) +def test_samefile_nofollow_rejects_zero_inodes( + inodes: tuple[int, int], monkeypatch: MonkeyPatch +) -> None: + stats = [unittest.mock.Mock(st_ino=inode) for inode in inodes] + lstat = unittest.mock.Mock(side_effect=stats) + samestat = unittest.mock.Mock(return_value=True) + monkeypatch.setattr(Path, "lstat", lstat) + monkeypatch.setattr(os.path, "samestat", samestat) + + assert not samefile_nofollow(Path("first"), Path("second")) + samestat.assert_not_called() + + def test_scandir_with_non_existent_directory() -> None: # Test with a directory that does not exist non_existent_dir = "path_to_non_existent_dir"