|
20 | 20 | from _pytest.config import ExitCode |
21 | 21 | from _pytest.monkeypatch import MonkeyPatch |
22 | 22 | from _pytest.pathlib import _import_module_using_spec |
| 23 | +from _pytest.pathlib import _top_level_shadows_external |
23 | 24 | from _pytest.pathlib import bestrelpath |
24 | 25 | from _pytest.pathlib import commonpath |
25 | 26 | from _pytest.pathlib import compute_module_name |
@@ -1828,6 +1829,55 @@ def test_compute_module_name(tmp_path: Path) -> None: |
1828 | 1829 | ) |
1829 | 1830 |
|
1830 | 1831 |
|
| 1832 | +class TestTopLevelShadowsExternal: |
| 1833 | + """Unit tests for ``_top_level_shadows_external`` to cover all branches.""" |
| 1834 | + |
| 1835 | + def test_no_external_module(self, tmp_path: Path) -> None: |
| 1836 | + """When find_spec returns None (no such external module), return False.""" |
| 1837 | + (tmp_path / "zzz_nonexistent_pkg").mkdir() |
| 1838 | + assert not _top_level_shadows_external("zzz_nonexistent_pkg.foo", tmp_path) |
| 1839 | + |
| 1840 | + def test_builtin_or_frozen(self, tmp_path: Path) -> None: |
| 1841 | + """Built-in/frozen modules (e.g. 'sys', 'os') are always external.""" |
| 1842 | + (tmp_path / "sys").mkdir() |
| 1843 | + assert _top_level_shadows_external("sys.something", tmp_path) |
| 1844 | + |
| 1845 | + def test_find_spec_raises(self, tmp_path: Path, monkeypatch: MonkeyPatch) -> None: |
| 1846 | + """When find_spec raises, treat as no external module (return False).""" |
| 1847 | + import importlib.util |
| 1848 | + |
| 1849 | + def _raise(*a: Any, **kw: Any) -> None: |
| 1850 | + raise ValueError("broken") |
| 1851 | + |
| 1852 | + monkeypatch.setattr(importlib.util, "find_spec", _raise) |
| 1853 | + assert not _top_level_shadows_external("whatever.foo", tmp_path) |
| 1854 | + |
| 1855 | + def test_normalized_dir_name(self, tmp_path: Path) -> None: |
| 1856 | + """A dir named '.tests' normalizes to '_tests' and should be |
| 1857 | + recognized as local, not external.""" |
| 1858 | + dot_tests = tmp_path / ".tests" |
| 1859 | + dot_tests.mkdir() |
| 1860 | + (dot_tests / "foo.py").write_text("", encoding="utf-8") |
| 1861 | + # '_tests' is not a real external module, so find_spec returns None. |
| 1862 | + # This just exercises the iterdir + normalization branch. |
| 1863 | + assert not _top_level_shadows_external("_tests.foo", tmp_path) |
| 1864 | + |
| 1865 | + def test_external_package_detected(self, tmp_path: Path) -> None: |
| 1866 | + """An installed package at a different location is external.""" |
| 1867 | + (tmp_path / "test").mkdir() |
| 1868 | + assert _top_level_shadows_external("test.support", tmp_path) |
| 1869 | + |
| 1870 | + def test_local_package_not_external( |
| 1871 | + self, tmp_path: Path, monkeypatch: MonkeyPatch |
| 1872 | + ) -> None: |
| 1873 | + """A package whose spec resolves inside local_root is not external.""" |
| 1874 | + pkg = tmp_path / "mypkg" |
| 1875 | + pkg.mkdir() |
| 1876 | + (pkg / "__init__.py").touch() |
| 1877 | + monkeypatch.syspath_prepend(tmp_path) |
| 1878 | + assert not _top_level_shadows_external("mypkg.sub", tmp_path) |
| 1879 | + |
| 1880 | + |
1831 | 1881 | def validate_namespace_package( |
1832 | 1882 | pytester: Pytester, paths: Sequence[Path], modules: Sequence[str] |
1833 | 1883 | ) -> RunResult: |
|
0 commit comments