Files
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

168 lines
8.0 KiB
Python

"""
Tests for Lean 4 Language Server integration with Serena.
Tests prove that Serena's symbol tools can:
1. Start the Lean 4 language server
2. Discover all expected symbols with precise matching
3. Track within-file references
4. Track cross-file references
Test Repository Structure:
- Helper.lean: Calculator structure, arithmetic functions (add, subtract), predicates (isPositive, absolute)
- Main.lean: Main entry point using Helper, plus multiply and calculate functions
"""
import pytest
from solidlsp.ls import SolidLanguageServer
from solidlsp.ls_config import Language
from test.conftest import language_tests_enabled
pytestmark = pytest.mark.skipif(not language_tests_enabled(Language.LEAN4), reason="Lean4 tests are disabled (lean not available)")
@pytest.mark.lean4
class TestLean4LanguageServer:
@pytest.mark.parametrize("language_server", [Language.LEAN4], indirect=True)
def test_ls_is_running(self, language_server: SolidLanguageServer) -> None:
"""Test that the Lean 4 language server starts successfully."""
assert language_server.is_running()
@pytest.mark.parametrize("language_server", [Language.LEAN4], indirect=True)
def test_helper_symbols(self, language_server: SolidLanguageServer) -> None:
"""
Test symbol discovery in Helper.lean.
Verifies that Serena can identify:
- Structure definition (Calculator)
- All functions (add, subtract, isPositive, absolute)
"""
all_symbols, _ = language_server.request_document_symbols("Helper.lean").get_all_symbols_and_roots()
symbol_names = {s["name"] for s in all_symbols}
expected_symbols = {
"Calculator",
"add",
"subtract",
"isPositive",
"absolute",
}
missing = expected_symbols - symbol_names
assert not missing, f"Missing expected symbols in Helper.lean: {missing}"
@pytest.mark.parametrize("language_server", [Language.LEAN4], indirect=True)
def test_main_symbols(self, language_server: SolidLanguageServer) -> None:
"""
Test symbol discovery in Main.lean.
Verifies that Serena can identify locally defined functions.
"""
all_symbols, _ = language_server.request_document_symbols("Main.lean").get_all_symbols_and_roots()
symbol_names = {s["name"] for s in all_symbols}
expected_symbols = {
"multiply",
"calculate",
"main",
}
missing = expected_symbols - symbol_names
assert not missing, f"Missing expected symbols in Main.lean: {missing}"
@pytest.mark.parametrize("language_server", [Language.LEAN4], indirect=True)
def test_within_file_references(self, language_server: SolidLanguageServer) -> None:
"""
Test within-file reference tracking for isPositive.
isPositive is defined in Helper.lean line 11 (0-indexed) and used by absolute on line 15.
"""
# isPositive defined at line 11, column 4
references = language_server.request_references("Helper.lean", line=11, column=4)
assert len(references) >= 1, f"Expected at least 1 reference to isPositive (used in absolute), got {len(references)}"
# Check that isPositive is referenced within Helper.lean at line 15 (absolute calls isPositive)
ref_locations = [(ref["relativePath"], ref["range"]["start"]["line"]) for ref in references]
helper_refs = [(path, line) for path, line in ref_locations if "Helper.lean" in path]
assert any(line == 15 for _, line in helper_refs), (
f"Expected isPositive reference at Helper.lean:15 (in absolute), got: {ref_locations}"
)
@pytest.mark.parametrize("language_server", [Language.LEAN4], indirect=True)
def test_cross_file_references_add(self, language_server: SolidLanguageServer) -> None:
"""
Test cross-file reference tracking for add function.
add is defined in Helper.lean line 5 (0-indexed) and used in Main.lean on lines 7 and 15.
"""
# add defined at line 5, column 4
references = language_server.request_references("Helper.lean", line=5, column=4)
assert len(references) >= 1, f"Expected at least 1 reference to add in Main.lean, got {len(references)}"
# Check for references in Main.lean with specific lines
ref_locations = [(ref["relativePath"], ref["range"]["start"]["line"]) for ref in references]
main_refs = [(path, line) for path, line in ref_locations if "Main.lean" in path]
assert len(main_refs) >= 1, f"Expected at least 1 reference to add in Main.lean, got: {ref_locations}"
main_ref_lines = {line for _, line in main_refs}
# add is used in Main.lean line 7 (in calculate) and line 15 (in main)
assert 7 in main_ref_lines or 15 in main_ref_lines, (
f"Expected add references at Main.lean lines 7 or 15, got lines: {main_ref_lines}"
)
@pytest.mark.parametrize("language_server", [Language.LEAN4], indirect=True)
def test_cross_file_references_calculator(self, language_server: SolidLanguageServer) -> None:
"""
Test cross-file reference tracking for Calculator structure.
Calculator is defined in Helper.lean line 0 (0-indexed) and used in Main.lean lines 5 and 13.
"""
# Calculator defined at line 0, column 10
references = language_server.request_references("Helper.lean", line=0, column=10)
assert len(references) >= 1, f"Expected at least 1 reference to Calculator in Main.lean, got {len(references)}"
ref_locations = [(ref["relativePath"], ref["range"]["start"]["line"]) for ref in references]
main_refs = [(path, line) for path, line in ref_locations if "Main.lean" in path]
assert len(main_refs) >= 1, f"Expected at least 1 reference to Calculator in Main.lean, got: {ref_locations}"
main_ref_lines = {line for _, line in main_refs}
# Calculator is used in Main.lean line 5 (calculate signature) and line 13 (let c : Calculator)
assert 5 in main_ref_lines or 13 in main_ref_lines, (
f"Expected Calculator references at Main.lean lines 5 or 13, got lines: {main_ref_lines}"
)
@pytest.mark.parametrize("language_server", [Language.LEAN4], indirect=True)
def test_go_to_definition_within_file(self, language_server: SolidLanguageServer) -> None:
"""
Test go-to-definition within a file.
In Main.lean line 19, calculate is called: 'match calculate c "multiply" 6 7 with'.
calculate is defined at Main.lean line 5.
"""
# calculate usage in Main.lean line 19, 'calculate' starts at col 8
definitions = language_server.request_definition("Main.lean", line=19, column=8)
assert len(definitions) >= 1, f"Expected at least 1 definition for calculate, got {len(definitions)}"
def_location = definitions[0]
assert def_location["uri"].endswith("Main.lean"), f"Expected definition in Main.lean, got: {def_location['uri']}"
assert def_location["range"]["start"]["line"] == 5, f"Expected definition at line 5, got: {def_location['range']['start']['line']}"
@pytest.mark.parametrize("language_server", [Language.LEAN4], indirect=True)
def test_go_to_definition_across_files(self, language_server: SolidLanguageServer) -> None:
"""
Test go-to-definition across files.
In Main.lean line 15, add is called: 'add 5 3'.
add is defined in Helper.lean line 5.
"""
# add usage in Main.lean line 15, 'add' starts at col 19
definitions = language_server.request_definition("Main.lean", line=15, column=19)
assert len(definitions) >= 1, f"Expected at least 1 definition for add, got {len(definitions)}"
def_location = definitions[0]
assert def_location["uri"].endswith("Helper.lean"), f"Expected definition in Helper.lean, got: {def_location['uri']}"
assert def_location["range"]["start"]["line"] == 5, f"Expected definition at line 5, got: {def_location['range']['start']['line']}"