Files
oraios--serena/test/solidlsp/test_is_ignored_path_missing.py
wehub-resource-sync c6af9e284a
Tests / catch-all (windows-latest) (push) Has been cancelled
Tests / jvm (macos-latest) (push) Has been cancelled
Tests / jvm (ubuntu-latest) (push) Has been cancelled
Tests / jvm (windows-latest) (push) Has been cancelled
Tests / native (macos-latest) (push) Has been cancelled
Tests / native (ubuntu-latest) (push) Has been cancelled
Tests / native (windows-latest) (push) Has been cancelled
Tests / niche (ubuntu-latest) (push) Has been cancelled
Tests / other-langs (macos-latest) (push) Has been cancelled
Tests / other-langs (ubuntu-latest) (push) Has been cancelled
Tests / other-langs (windows-latest) (push) Has been cancelled
Tests / catch-all (macos-latest) (push) Has been cancelled
Tests / catch-all (ubuntu-latest) (push) Has been cancelled
Docs Build / build (push) Has been cancelled
Docs Build / deploy (push) Has been cancelled
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Codespell / Check for spelling errors (push) Has been cancelled
Build and Push Docker Images / build-and-push (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:39:38 +08:00

197 lines
8.2 KiB
Python

"""Unit tests: missing-path handling in ``SolidLanguageServer``.
``is_ignored_path`` must not raise on missing paths, and symbol locations resolving to
non-existent files must be skipped. Language servers may report locations of generated
files that are not present on disk, e.g. JDTLS reporting a Lombok-generated
``LombokModel$LombokModelBuilder.class`` under ``target/classes``; the ignore check must
classify such paths instead of raising ``FileNotFoundError``.
No language markers: these use a local test double and run in catch-all.
"""
from __future__ import annotations
from pathlib import Path
import pathspec
import pytest
from solidlsp import SolidLanguageServer, ls_types
from solidlsp.ls_config import Language
from solidlsp.ls_utils import PathUtils
class _IgnoredPathServer(SolidLanguageServer):
"""Minimal test double exposing only :meth:`is_ignored_path` dependencies."""
def __init__(
self,
root: Path,
language: Language,
*,
ignored_dirnames: tuple[str, ...] = (),
ignore_lines: tuple[str, ...] = (),
) -> None:
self.repository_root_path = str(root)
self.language = language
self._ignored_dirnames = frozenset(ignored_dirnames)
self._ignore_spec = pathspec.PathSpec.from_lines(pathspec.patterns.GitWildMatchPattern, ignore_lines)
def is_ignored_dirname(self, dirname: str) -> bool:
return super().is_ignored_dirname(dirname) or dirname in self._ignored_dirnames
def _start_server(self) -> None:
raise AssertionError("The test double must not start a language server")
def _create_base_initialize_params(self) -> dict[str, object]:
raise AssertionError("The test double must not build initialize params")
def test_missing_lombok_class_under_target_is_ignored_not_raised(tmp_path: Path) -> None:
"""JDTLS-style missing build artifact under target/classes -- must be ignored, never raise."""
ls = _IgnoredPathServer(tmp_path, Language.JAVA)
missing = "target/classes/test_repo/LombokModel$LombokModelBuilder.class"
assert not (tmp_path / missing).exists()
assert ls.is_ignored_path(missing) is True
def test_missing_unsupported_extension_is_ignored(tmp_path: Path) -> None:
ls = _IgnoredPathServer(tmp_path, Language.PYTHON)
missing = "src/foo.pyc"
assert not (tmp_path / missing).exists()
assert ls.is_ignored_path(missing) is True
def test_missing_source_file_not_in_ignored_dir_is_not_ignored(tmp_path: Path) -> None:
"""A missing source path classifies as not-ignored."""
ls = _IgnoredPathServer(tmp_path, Language.PYTHON)
missing = "src/app.py"
assert not (tmp_path / missing).exists()
assert ls.is_ignored_path(missing) is False
def test_missing_source_file_under_ignored_dirname_is_ignored(tmp_path: Path) -> None:
ls = _IgnoredPathServer(tmp_path, Language.PYTHON, ignored_dirnames=("generated",))
missing = "generated/app.py"
assert not (tmp_path / missing).exists()
assert ls.is_ignored_path(missing) is True
def test_missing_directory_with_ignored_dirname_leaf_is_ignored(tmp_path: Path) -> None:
"""The leaf of a missing suffixless path may denote a directory and is checked against ignored dirnames."""
ls = _IgnoredPathServer(tmp_path, Language.PYTHON, ignored_dirnames=("generated",))
assert ls.is_ignored_path("generated", ignore_unsupported_files=False) is True
assert ls.is_ignored_path("src/generated", ignore_unsupported_files=False) is True
assert ls.is_ignored_path(".venv", ignore_unsupported_files=False) is True
def test_missing_extensionless_path_is_treated_as_directory(tmp_path: Path) -> None:
"""A missing suffixless path is not subject to the unsupported-extension rule for files."""
ls = _IgnoredPathServer(tmp_path, Language.PYTHON)
assert ls.is_ignored_path("newpkg") is False
def test_missing_ignored_by_pathspec_is_ignored(tmp_path: Path) -> None:
ls = _IgnoredPathServer(tmp_path, Language.PYTHON, ignore_lines=("generated.py",))
missing = "generated.py"
assert not (tmp_path / missing).exists()
assert ls.is_ignored_path(missing) is True
def test_missing_unsupported_extension_can_be_allowed(tmp_path: Path) -> None:
ls = _IgnoredPathServer(tmp_path, Language.PYTHON)
missing = "src/foo.pyc"
assert not (tmp_path / missing).exists()
assert ls.is_ignored_path(missing, ignore_unsupported_files=False) is False
def test_existing_path_under_ignored_dirname_is_ignored(tmp_path: Path) -> None:
ls = _IgnoredPathServer(tmp_path, Language.PYTHON, ignored_dirnames=("generated",))
source_file = tmp_path / "generated" / "app.py"
source_file.parent.mkdir(parents=True)
source_file.write_text("x = 1\n")
assert ls.is_ignored_path("generated/app.py") is True
def test_existing_source_file_is_not_ignored(tmp_path: Path) -> None:
ls = _IgnoredPathServer(tmp_path, Language.PYTHON)
src = tmp_path / "pkg" / "mod.py"
src.parent.mkdir(parents=True)
src.write_text("x = 1\n")
assert ls.is_ignored_path("pkg/mod.py") is False
@pytest.mark.parametrize(
"rel",
[
"target/classes/test_repo/LombokModel$LombokModelBuilder.class",
"build/classes/Foo.class",
"out/production/Foo.class",
],
)
def test_java_build_output_paths_never_raise(tmp_path: Path, rel: str) -> None:
"""Compiled-class paths classify as ignored via the unsupported-extension rule, present or not
(build dirnames are deliberately not hard-ignored for Java).
"""
ls = _IgnoredPathServer(tmp_path, Language.JAVA)
# neither present nor absent should raise
assert ls.is_ignored_path(rel) is True
p = tmp_path / rel
p.parent.mkdir(parents=True, exist_ok=True)
p.write_bytes(b"")
assert ls.is_ignored_path(rel) is True
class _StubSymbolLocationRequest(SolidLanguageServer.SymbolLocationRequest):
"""Minimal concrete subclass exposing only :meth:`convert_location_item` dependencies."""
def send_request(self) -> object | None:
raise AssertionError("The test double must not send LSP requests")
def normalize_response(self, response: object | None) -> list[ls_types.Location]:
raise AssertionError("The test double must not normalize LSP responses")
def _location_request(ls: SolidLanguageServer) -> SolidLanguageServer.SymbolLocationRequest:
return _StubSymbolLocationRequest(ls, "src/app.py", 0, 0, request_name="test_request")
def _location_item(path: Path) -> dict:
zero = {"line": 0, "character": 0}
return {"uri": PathUtils.path_to_uri(str(path)), "range": {"start": zero, "end": zero}}
def test_location_at_missing_path_is_skipped(tmp_path: Path) -> None:
"""Locations whose absolute path is not on disk (e.g. LS-reported build artifacts) are dropped."""
ls = _IgnoredPathServer(tmp_path, Language.JAVA)
missing = tmp_path / "target" / "classes" / "LombokModel$LombokModelBuilder.class"
assert not missing.exists()
assert _location_request(ls).convert_location_item(_location_item(missing)) is None
def test_location_at_missing_source_path_is_skipped(tmp_path: Path) -> None:
"""A missing path that is not ignored is dropped by the existence check alone."""
ls = _IgnoredPathServer(tmp_path, Language.PYTHON)
assert ls.is_ignored_path("pkg/mod.py") is False
assert _location_request(ls).convert_location_item(_location_item(tmp_path / "pkg" / "mod.py")) is None
def test_location_at_existing_ignored_path_is_skipped(tmp_path: Path) -> None:
ls = _IgnoredPathServer(tmp_path, Language.PYTHON, ignored_dirnames=("generated",))
source_file = tmp_path / "generated" / "app.py"
source_file.parent.mkdir(parents=True)
source_file.write_text("x = 1\n")
assert _location_request(ls).convert_location_item(_location_item(source_file)) is None
def test_location_at_existing_source_path_is_converted(tmp_path: Path) -> None:
ls = _IgnoredPathServer(tmp_path, Language.PYTHON)
source_file = tmp_path / "pkg" / "mod.py"
source_file.parent.mkdir(parents=True)
source_file.write_text("x = 1\n")
location = _location_request(ls).convert_location_item(_location_item(source_file))
assert location is not None
assert Path(location["absolutePath"]) == source_file
assert Path(location["relativePath"]) == Path("pkg/mod.py")