mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-13 16:15:29 +00:00
Restructured serena_agent tests, reifying the cases
Removed some cases (haxe, msl) during cherry pick due to conflicts. We don't need to cover these languages in this test
This commit is contained in:
1 parent
c55e0c900e
commit
940bbb31de
5 files changed
+912
-1356
No files matched your search
+1
-1
@@ -370,7 +370,7 @@ def find_identifier_position(file_path: Path, identifier: str) -> tuple[int, int
|
||||
return None
|
||||
|
||||
|
||||
def find_identifier_occurrence_position(
|
||||
def find_identifier_pos(
|
||||
file_path: Path,
|
||||
identifier: str,
|
||||
occurrence_index: int = 0,
|
||||
|
||||
@@ -1,230 +0,0 @@
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from typing import cast
|
||||
|
||||
import pytest
|
||||
from _pytest.mark import Mark, MarkDecorator
|
||||
|
||||
from solidlsp.ls_config import Language
|
||||
from test.conftest import get_pytest_markers
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DiagnosticCase:
|
||||
language: Language
|
||||
relative_path: str
|
||||
primary_symbol_name_path: str
|
||||
primary_symbol_identifier: str
|
||||
reference_symbol_name_path: str
|
||||
reference_symbol_identifier: str
|
||||
primary_message_fragment: str
|
||||
reference_message_fragment: str
|
||||
|
||||
|
||||
def diagnostic_case_param(
|
||||
case: DiagnosticCase,
|
||||
*marks: MarkDecorator | Mark,
|
||||
id: str,
|
||||
):
|
||||
return pytest.param(case.language, case, marks=[*get_pytest_markers(case.language), *marks], id=id)
|
||||
|
||||
|
||||
DIAGNOSTIC_CASE_PARAMS = [
|
||||
diagnostic_case_param(
|
||||
DiagnosticCase(
|
||||
language=Language.PYTHON,
|
||||
relative_path=os.path.join("test_repo", "diagnostics_sample.py"),
|
||||
primary_symbol_name_path="broken_factory",
|
||||
primary_symbol_identifier="broken_factory",
|
||||
reference_symbol_name_path="broken_consumer",
|
||||
reference_symbol_identifier="broken_consumer",
|
||||
primary_message_fragment="missing_user",
|
||||
reference_message_fragment="undefined_name",
|
||||
),
|
||||
id="python_missing_user",
|
||||
),
|
||||
diagnostic_case_param(
|
||||
DiagnosticCase(
|
||||
language=Language.PYTHON_TY,
|
||||
relative_path=os.path.join("test_repo", "diagnostics_sample.py"),
|
||||
primary_symbol_name_path="broken_factory",
|
||||
primary_symbol_identifier="broken_factory",
|
||||
reference_symbol_name_path="broken_consumer",
|
||||
reference_symbol_identifier="broken_consumer",
|
||||
primary_message_fragment="missing_user",
|
||||
reference_message_fragment="undefined_name",
|
||||
),
|
||||
id="python_ty_missing_user",
|
||||
),
|
||||
diagnostic_case_param(
|
||||
DiagnosticCase(
|
||||
language=Language.GO,
|
||||
relative_path="diagnostics_sample.go",
|
||||
primary_symbol_name_path="brokenFactory",
|
||||
primary_symbol_identifier="brokenFactory",
|
||||
reference_symbol_name_path="brokenConsumer",
|
||||
reference_symbol_identifier="brokenConsumer",
|
||||
primary_message_fragment="missingGreeting",
|
||||
reference_message_fragment="missingConsumerValue",
|
||||
),
|
||||
id="go_missing_greeting",
|
||||
),
|
||||
diagnostic_case_param(
|
||||
DiagnosticCase(
|
||||
language=Language.JAVA,
|
||||
relative_path=os.path.join("src", "main", "java", "test_repo", "DiagnosticsSample.java"),
|
||||
primary_symbol_name_path="DiagnosticsSample/brokenFactory",
|
||||
primary_symbol_identifier="brokenFactory",
|
||||
reference_symbol_name_path="DiagnosticsSample/brokenConsumer",
|
||||
reference_symbol_identifier="brokenConsumer",
|
||||
primary_message_fragment="missingGreeting",
|
||||
reference_message_fragment="missingConsumerValue",
|
||||
),
|
||||
id="java_missing_greeting",
|
||||
),
|
||||
diagnostic_case_param(
|
||||
DiagnosticCase(
|
||||
language=Language.KOTLIN,
|
||||
relative_path=os.path.join("src", "main", "kotlin", "test_repo", "DiagnosticsSample.kt"),
|
||||
primary_symbol_name_path="brokenFactory",
|
||||
primary_symbol_identifier="brokenFactory",
|
||||
reference_symbol_name_path="brokenConsumer",
|
||||
reference_symbol_identifier="brokenConsumer",
|
||||
primary_message_fragment="missingGreeting",
|
||||
reference_message_fragment="missingConsumerValue",
|
||||
),
|
||||
id="kotlin_missing_greeting",
|
||||
),
|
||||
diagnostic_case_param(
|
||||
DiagnosticCase(
|
||||
language=Language.RUST,
|
||||
relative_path=os.path.join("src", "diagnostics_sample.rs"),
|
||||
primary_symbol_name_path="broken_factory",
|
||||
primary_symbol_identifier="broken_factory",
|
||||
reference_symbol_name_path="broken_consumer",
|
||||
reference_symbol_identifier="broken_consumer",
|
||||
primary_message_fragment="missing_greeting",
|
||||
reference_message_fragment="missing_consumer_value",
|
||||
),
|
||||
pytest.mark.xfail(reason="rust-analyzer does not surface diagnostics for this fixture through Serena currently"),
|
||||
id="rust_missing_greeting",
|
||||
),
|
||||
diagnostic_case_param(
|
||||
DiagnosticCase(
|
||||
language=Language.PHP,
|
||||
relative_path="diagnostics_sample.php",
|
||||
primary_symbol_name_path="brokenFactory",
|
||||
primary_symbol_identifier="brokenFactory",
|
||||
reference_symbol_name_path="brokenConsumer",
|
||||
reference_symbol_identifier="brokenConsumer",
|
||||
primary_message_fragment="missingGreeting",
|
||||
reference_message_fragment="missingConsumerValue",
|
||||
),
|
||||
pytest.mark.xfail(reason="PHP LS integration does not expose document diagnostics in this environment"),
|
||||
id="php_missing_greeting",
|
||||
),
|
||||
diagnostic_case_param(
|
||||
DiagnosticCase(
|
||||
language=Language.CLOJURE,
|
||||
relative_path=os.path.join("src", "test_app", "diagnostics_sample.clj"),
|
||||
primary_symbol_name_path="broken-factory",
|
||||
primary_symbol_identifier="broken-factory",
|
||||
reference_symbol_name_path="broken-consumer",
|
||||
reference_symbol_identifier="broken-consumer",
|
||||
primary_message_fragment="missing-greeting",
|
||||
reference_message_fragment="missing-consumer-value",
|
||||
),
|
||||
id="clojure_missing_greeting",
|
||||
),
|
||||
diagnostic_case_param(
|
||||
DiagnosticCase(
|
||||
language=Language.CSHARP,
|
||||
relative_path="DiagnosticsSample.cs",
|
||||
primary_symbol_name_path="TestProject/DiagnosticsSample/BrokenFactory",
|
||||
primary_symbol_identifier="BrokenFactory",
|
||||
reference_symbol_name_path="TestProject/DiagnosticsSample/BrokenConsumer",
|
||||
reference_symbol_identifier="BrokenConsumer",
|
||||
primary_message_fragment="missingGreeting",
|
||||
reference_message_fragment="missingConsumerValue",
|
||||
),
|
||||
id="csharp_missing_greeting",
|
||||
),
|
||||
diagnostic_case_param(
|
||||
DiagnosticCase(
|
||||
language=Language.POWERSHELL,
|
||||
relative_path="diagnostics_sample.ps1",
|
||||
primary_symbol_name_path="function Invoke-BrokenFactory ()",
|
||||
primary_symbol_identifier="Invoke-BrokenFactory",
|
||||
reference_symbol_name_path="function Invoke-BrokenConsumer ()",
|
||||
reference_symbol_identifier="Invoke-BrokenConsumer",
|
||||
primary_message_fragment="MissingGreeting",
|
||||
reference_message_fragment="MissingConsumerValue",
|
||||
),
|
||||
pytest.mark.xfail(reason="PowerShell LS does not surface document diagnostics in this environment"),
|
||||
id="powershell_missing_greeting",
|
||||
),
|
||||
diagnostic_case_param(
|
||||
DiagnosticCase(
|
||||
language=Language.CPP_CCLS,
|
||||
relative_path="diagnostics_sample.cpp",
|
||||
primary_symbol_name_path="brokenFactory",
|
||||
primary_symbol_identifier="brokenFactory",
|
||||
reference_symbol_name_path="brokenConsumer",
|
||||
reference_symbol_identifier="brokenConsumer",
|
||||
primary_message_fragment="missingGreeting",
|
||||
reference_message_fragment="missingConsumerValue",
|
||||
),
|
||||
pytest.mark.xfail(reason="ccls does not expose document diagnostics through this integration"),
|
||||
id="cpp_missing_greeting",
|
||||
),
|
||||
diagnostic_case_param(
|
||||
DiagnosticCase(
|
||||
language=Language.LEAN4,
|
||||
relative_path="DiagnosticsSample.lean",
|
||||
primary_symbol_name_path="brokenFactory",
|
||||
primary_symbol_identifier="brokenFactory",
|
||||
reference_symbol_name_path="brokenConsumer",
|
||||
reference_symbol_identifier="brokenConsumer",
|
||||
primary_message_fragment="missingGreeting",
|
||||
reference_message_fragment="missingConsumerValue",
|
||||
),
|
||||
pytest.mark.xfail(reason="Lean4 LS does not reliably surface diagnostics in CI"),
|
||||
id="lean_missing_greeting",
|
||||
),
|
||||
diagnostic_case_param(
|
||||
DiagnosticCase(
|
||||
language=Language.TYPESCRIPT,
|
||||
relative_path="diagnostics_sample.ts",
|
||||
primary_symbol_name_path="brokenFactory",
|
||||
primary_symbol_identifier="brokenFactory",
|
||||
reference_symbol_name_path="brokenConsumer",
|
||||
reference_symbol_identifier="brokenConsumer",
|
||||
primary_message_fragment="missingGreeting",
|
||||
reference_message_fragment="missingConsumerValue",
|
||||
),
|
||||
pytest.mark.typescript,
|
||||
pytest.mark.xfail(reason="TypeScript LS does not surface document diagnostics through this integration"),
|
||||
id="typescript_missing_greeting",
|
||||
),
|
||||
diagnostic_case_param(
|
||||
DiagnosticCase(
|
||||
language=Language.FSHARP,
|
||||
relative_path="DiagnosticsSample.fs",
|
||||
primary_symbol_name_path="brokenFactory",
|
||||
primary_symbol_identifier="brokenFactory",
|
||||
reference_symbol_name_path="brokenConsumer",
|
||||
reference_symbol_identifier="brokenConsumer",
|
||||
primary_message_fragment="missingGreeting",
|
||||
reference_message_fragment="missingConsumerValue",
|
||||
),
|
||||
pytest.mark.xfail(reason="F# LS does not expose document diagnostics through this integration"),
|
||||
id="fsharp_missing_greeting",
|
||||
),
|
||||
]
|
||||
|
||||
WORKING_DIAGNOSTIC_TOOL_CASE_PARAMS = [
|
||||
case_param
|
||||
for case_param in DIAGNOSTIC_CASE_PARAMS
|
||||
if cast(DiagnosticCase, case_param.values[1]).language
|
||||
in {Language.PYTHON, Language.PYTHON_TY, Language.GO, Language.JAVA, Language.KOTLIN, Language.CSHARP}
|
||||
]
|
||||
+911
-907
File diff suppressed because it is too large.
Load diff
@@ -1,139 +0,0 @@
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from solidlsp import SolidLanguageServer
|
||||
from solidlsp.ls_config import Language
|
||||
from test.conftest import find_identifier_occurrence_position, is_ci
|
||||
from test.solidlsp import clojure as clj
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"language_server,relative_path,identifier,occurrence_index,column_offset,expected_name,expected_definition_file",
|
||||
[
|
||||
pytest.param(
|
||||
Language.PYTHON,
|
||||
os.path.join("test_repo", "services.py"),
|
||||
"User",
|
||||
1,
|
||||
1,
|
||||
"User",
|
||||
"models.py",
|
||||
marks=pytest.mark.python,
|
||||
),
|
||||
pytest.param(
|
||||
Language.PYTHON_TY,
|
||||
os.path.join("test_repo", "services.py"),
|
||||
"User",
|
||||
1,
|
||||
1,
|
||||
"User",
|
||||
"models.py",
|
||||
marks=pytest.mark.python,
|
||||
),
|
||||
pytest.param(Language.GO, "main.go", "Helper", 0, 1, "Helper", "main.go", marks=pytest.mark.go),
|
||||
pytest.param(
|
||||
Language.JAVA,
|
||||
os.path.join("src", "main", "java", "test_repo", "Main.java"),
|
||||
"Model",
|
||||
0,
|
||||
1,
|
||||
"Model",
|
||||
"Model.java",
|
||||
marks=pytest.mark.java,
|
||||
),
|
||||
pytest.param(
|
||||
Language.KOTLIN,
|
||||
os.path.join("src", "main", "kotlin", "test_repo", "Main.kt"),
|
||||
"Model",
|
||||
0,
|
||||
1,
|
||||
"Model",
|
||||
"Model.kt",
|
||||
marks=[pytest.mark.kotlin] + ([pytest.mark.skip(reason="Kotlin LSP JVM crashes on restart in CI")] if is_ci else []),
|
||||
),
|
||||
pytest.param(
|
||||
Language.RUST,
|
||||
os.path.join("src", "main.rs"),
|
||||
"format_greeting",
|
||||
0,
|
||||
1,
|
||||
"format_greeting",
|
||||
"lib.rs",
|
||||
marks=pytest.mark.rust,
|
||||
),
|
||||
pytest.param(Language.PHP, "index.php", "helperFunction", 0, 5, "helperFunction", "helper.php", marks=pytest.mark.php),
|
||||
pytest.param(
|
||||
Language.CLOJURE,
|
||||
clj.UTILS_PATH,
|
||||
"multiply",
|
||||
0,
|
||||
1,
|
||||
"multiply",
|
||||
clj.CORE_PATH,
|
||||
marks=[
|
||||
pytest.mark.clojure,
|
||||
pytest.mark.skipif(not clj.is_clojure_cli_available(), reason="clojure CLI is not installed"),
|
||||
],
|
||||
),
|
||||
pytest.param(Language.CSHARP, "Program.cs", "Add", 0, 1, "Add", "Program.cs", marks=pytest.mark.csharp),
|
||||
pytest.param(
|
||||
Language.POWERSHELL,
|
||||
"main.ps1",
|
||||
"Convert-ToUpperCase",
|
||||
0,
|
||||
1,
|
||||
"function Convert-ToUpperCase ()",
|
||||
"utils.ps1",
|
||||
marks=pytest.mark.powershell,
|
||||
),
|
||||
pytest.param(Language.CPP_CCLS, "a.cpp", "add", 0, 1, "add", "b.cpp", marks=pytest.mark.cpp),
|
||||
pytest.param(
|
||||
Language.LEAN4,
|
||||
"Main.lean",
|
||||
"add",
|
||||
0,
|
||||
1,
|
||||
"add",
|
||||
"Helper.lean",
|
||||
marks=[
|
||||
pytest.mark.lean4,
|
||||
pytest.mark.skipif(shutil.which("lean") is None, reason="Lean is not installed"),
|
||||
pytest.mark.xfail(reason="Lean4 LS does not reliably resolve cross-file defining symbols in CI"),
|
||||
],
|
||||
),
|
||||
pytest.param(Language.TYPESCRIPT, "index.ts", "helperFunction", 1, 1, "helperFunction", "index.ts", marks=pytest.mark.typescript),
|
||||
pytest.param(
|
||||
Language.FSHARP,
|
||||
"Program.fs",
|
||||
"add",
|
||||
0,
|
||||
1,
|
||||
"add",
|
||||
"Calculator.fs",
|
||||
marks=[pytest.mark.fsharp, pytest.mark.xfail(reason="F# language server cannot reliably resolve defining symbols")],
|
||||
),
|
||||
],
|
||||
indirect=["language_server"],
|
||||
)
|
||||
def test_request_defining_symbol_matrix(
|
||||
language_server: SolidLanguageServer,
|
||||
relative_path: str,
|
||||
identifier: str,
|
||||
occurrence_index: int,
|
||||
column_offset: int,
|
||||
expected_name: str,
|
||||
expected_definition_file: str,
|
||||
) -> None:
|
||||
repo_root = Path(language_server.repository_root_path)
|
||||
position = find_identifier_occurrence_position(repo_root / relative_path, identifier, occurrence_index, column_offset)
|
||||
assert position is not None, f"Could not find occurrence {occurrence_index} of {identifier!r} in {relative_path}"
|
||||
|
||||
defining_symbol = language_server.request_defining_symbol(relative_path, *position)
|
||||
assert defining_symbol is not None, f"Expected a defining symbol for {identifier!r} in {relative_path}"
|
||||
assert defining_symbol.get("name") == expected_name, f"Expected defining symbol name {expected_name!r}, got: {defining_symbol}"
|
||||
assert expected_definition_file in defining_symbol["location"].get("relativePath", ""), (
|
||||
f"Expected defining symbol in {expected_definition_file!r}, got: {defining_symbol}"
|
||||
)
|
||||
@@ -1,79 +0,0 @@
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from serena.symbol import LanguageServerSymbolRetriever
|
||||
from solidlsp import SolidLanguageServer
|
||||
from test.conftest import find_identifier_position
|
||||
from test.diagnostics_cases import DIAGNOSTIC_CASE_PARAMS, DiagnosticCase
|
||||
|
||||
|
||||
@pytest.mark.parametrize("language_server,diagnostic_case", DIAGNOSTIC_CASE_PARAMS, indirect=["language_server"])
|
||||
def test_request_text_document_diagnostics_matrix(
|
||||
language_server: SolidLanguageServer,
|
||||
diagnostic_case: DiagnosticCase,
|
||||
) -> None:
|
||||
diagnostics = language_server.request_text_document_diagnostics(diagnostic_case.relative_path, min_severity=1)
|
||||
assert diagnostics, f"Expected diagnostics for {diagnostic_case.language.value}:{diagnostic_case.relative_path}"
|
||||
|
||||
diagnostic_messages = [diagnostic["message"] for diagnostic in diagnostics]
|
||||
assert any(diagnostic_case.primary_message_fragment in message for message in diagnostic_messages), diagnostic_messages
|
||||
assert any(diagnostic_case.reference_message_fragment in message for message in diagnostic_messages), diagnostic_messages
|
||||
|
||||
repo_root = language_server.repository_root_path
|
||||
primary_symbol_position = find_identifier_position(
|
||||
Path(repo_root) / diagnostic_case.relative_path, diagnostic_case.primary_symbol_identifier
|
||||
)
|
||||
reference_symbol_position = find_identifier_position(
|
||||
Path(repo_root) / diagnostic_case.relative_path, diagnostic_case.reference_symbol_identifier
|
||||
)
|
||||
assert primary_symbol_position is not None
|
||||
assert reference_symbol_position is not None
|
||||
|
||||
primary_diagnostics = language_server.request_text_document_diagnostics(
|
||||
diagnostic_case.relative_path,
|
||||
start_line=primary_symbol_position[0],
|
||||
end_line=reference_symbol_position[0] - 1,
|
||||
min_severity=1,
|
||||
)
|
||||
primary_messages = [diagnostic["message"] for diagnostic in primary_diagnostics]
|
||||
assert primary_messages, f"Expected range-filtered diagnostics for {diagnostic_case.primary_symbol_identifier}"
|
||||
assert any(diagnostic_case.primary_message_fragment in message for message in primary_messages), primary_messages
|
||||
assert all(diagnostic_case.reference_message_fragment not in message for message in primary_messages), primary_messages
|
||||
|
||||
|
||||
@pytest.mark.parametrize("project_with_ls,diagnostic_case", DIAGNOSTIC_CASE_PARAMS, indirect=["project_with_ls"])
|
||||
def test_get_symbol_diagnostics_matrix(project_with_ls, diagnostic_case: DiagnosticCase) -> None:
|
||||
symbol_retriever = LanguageServerSymbolRetriever(project_with_ls)
|
||||
|
||||
diagnostics_by_symbol = symbol_retriever.get_symbol_diagnostics(
|
||||
diagnostic_case.primary_symbol_name_path,
|
||||
reference_file=diagnostic_case.relative_path,
|
||||
min_severity=1,
|
||||
)
|
||||
diagnostic_messages_by_symbol = {
|
||||
symbol.get_name_path(): [diagnostic["message"] for diagnostic in diagnostics]
|
||||
for symbol, diagnostics in diagnostics_by_symbol.items()
|
||||
}
|
||||
assert diagnostic_case.primary_symbol_name_path in diagnostic_messages_by_symbol, diagnostic_messages_by_symbol
|
||||
assert any(
|
||||
diagnostic_case.primary_message_fragment in message
|
||||
for message in diagnostic_messages_by_symbol[diagnostic_case.primary_symbol_name_path]
|
||||
), diagnostic_messages_by_symbol
|
||||
|
||||
diagnostics_with_references = symbol_retriever.get_symbol_diagnostics(
|
||||
diagnostic_case.primary_symbol_name_path,
|
||||
reference_file=diagnostic_case.relative_path,
|
||||
check_symbol_references=True,
|
||||
min_severity=1,
|
||||
)
|
||||
diagnostic_messages_with_references = {
|
||||
symbol.get_name_path(): [diagnostic["message"] for diagnostic in diagnostics]
|
||||
for symbol, diagnostics in diagnostics_with_references.items()
|
||||
}
|
||||
assert diagnostic_case.primary_symbol_name_path in diagnostic_messages_with_references, diagnostic_messages_with_references
|
||||
assert diagnostic_case.reference_symbol_name_path in diagnostic_messages_with_references, diagnostic_messages_with_references
|
||||
assert any(
|
||||
diagnostic_case.reference_message_fragment in message
|
||||
for message in diagnostic_messages_with_references[diagnostic_case.reference_symbol_name_path]
|
||||
), diagnostic_messages_with_references
|
||||
Reference in new issue
Block a user