This commit is contained in:
Don Syme committed 2025-10-10 12:01:45 +01:00
1 parent a636b98719
commit d13e1744e8
3 files changed
+57 -69

No files matched your search

+30 -38
View File
@@ -19,11 +19,11 @@ class TestFSharpLanguageServer:
def test_find_symbol(self, language_server: SolidLanguageServer) -> None:
"""Test finding symbols in the full symbol tree."""
symbols = language_server.request_full_symbol_tree()
# Check for main program module symbols
assert SymbolUtils.symbol_tree_contains_name(symbols, "Program"), "Program module not found in symbol tree"
assert SymbolUtils.symbol_tree_contains_name(symbols, "main"), "main function not found in symbol tree"
# Check for Calculator module symbols
assert SymbolUtils.symbol_tree_contains_name(symbols, "Calculator"), "Calculator module not found in symbol tree"
assert SymbolUtils.symbol_tree_contains_name(symbols, "add"), "add function not found in symbol tree"
@@ -62,7 +62,7 @@ class TestFSharpLanguageServer:
# Look for expected functions
symbol_names = [s.get("name") for s in symbols]
expected_symbols = ["add", "subtract", "multiply", "divide", "square", "factorial", "CalculatorClass"]
for expected in expected_symbols:
assert expected in symbol_names, f"{expected} function not found in Calculator.fs symbols"
@@ -71,26 +71,24 @@ class TestFSharpLanguageServer:
"""Test finding references using symbol selection range."""
file_path = os.path.join("Calculator.fs")
symbols = language_server.request_document_symbols(file_path)
# Find the 'add' function symbol
add_symbol = None
symbol_list = symbols[0] if symbols and isinstance(symbols[0], list) else symbols
for sym in symbol_list:
if sym.get("name") == "add":
add_symbol = sym
break
assert add_symbol is not None, "Could not find 'add' function symbol in Calculator.fs"
# Try to find references to the add function
sel_start = add_symbol["selectionRange"]["start"]
refs = language_server.request_references(file_path, sel_start["line"], sel_start["character"] + 1)
# The add function should be referenced in Program.fs
assert any(
"Program.fs" in ref.get("relativePath", "") for ref in refs
), "Program.fs should reference add function"
assert any("Program.fs" in ref.get("relativePath", "") for ref in refs), "Program.fs should reference add function"
@pytest.mark.parametrize("language_server", [Language.FSHARP], indirect=True)
def test_nested_module_symbols(self, language_server: SolidLanguageServer) -> None:
@@ -108,7 +106,7 @@ class TestFSharpLanguageServer:
# Check for expected types and modules
symbol_names = [s.get("name") for s in symbols]
expected_symbols = ["Person", "PersonModule", "Address", "Employee"]
for expected in expected_symbols:
assert expected in symbol_names, f"{expected} not found in Person.fs symbols"
@@ -135,20 +133,18 @@ class TestFSharpLanguageServer:
refs = language_server.request_references(file_path, sel_start["line"], sel_start["character"] + 1)
# The subtract function should be referenced in Program.fs
assert any(
"Program.fs" in ref.get("relativePath", "") for ref in refs
), "Program.fs should reference subtract function"
assert any("Program.fs" in ref.get("relativePath", "") for ref in refs), "Program.fs should reference subtract function"
@pytest.mark.parametrize("language_server", [Language.FSHARP], indirect=True)
def test_go_to_definition(self, language_server: SolidLanguageServer) -> None:
"""Test go-to-definition functionality."""
# Test going to definition of 'add' function from Program.fs
program_file = os.path.join("Program.fs")
# Try to find definition of 'add' function used in Program.fs
# This would typically be at the line where 'add 5 3' is called
definitions = language_server.request_definition(program_file, 10, 20) # Approximate position
# We should get at least some definitions
assert len(definitions) >= 0, "Should get definitions (even if empty for complex cases)"
@@ -156,10 +152,10 @@ class TestFSharpLanguageServer:
def test_hover_information(self, language_server: SolidLanguageServer) -> None:
"""Test hover information functionality."""
file_path = os.path.join("Calculator.fs")
# Try to get hover information for a function
hover_info = language_server.request_hover(file_path, 5, 10) # Approximate position of a function
# Hover info might be None or contain information
# This is acceptable as it depends on the LSP server's capabilities and timing
assert hover_info is None or isinstance(hover_info, dict), "Hover info should be None or dict"
@@ -168,18 +164,18 @@ class TestFSharpLanguageServer:
def test_completion(self, language_server: SolidLanguageServer) -> None:
"""Test code completion functionality."""
file_path = os.path.join("Program.fs")
# F# completion can be slow, so we'll test this with a timeout approach
# In real usage, completions work but may take time for large projects
try:
import signal
def timeout_handler(signum, frame):
raise TimeoutError("Completion request timed out")
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(5) # 5 second timeout
try:
completions = language_server.request_completions(file_path, 15, 10)
signal.alarm(0) # Cancel alarm
@@ -197,16 +193,16 @@ class TestFSharpLanguageServer:
def test_diagnostics(self, language_server: SolidLanguageServer) -> None:
"""Test getting diagnostics (errors, warnings) from F# files."""
file_path = os.path.join("Program.fs")
# FsAutoComplete uses publishDiagnostics notifications instead of textDocument/diagnostic requests
# So we'll test that the language server can handle files without crashing
# In real usage, diagnostics would come through the publishDiagnostics notification handler
# Test that we can at least work with the file (open/close cycle)
with language_server.open_file(file_path) as _:
# If we can open and close the file without errors, basic diagnostics support is working
pass
# This is a successful test - FsAutoComplete is working with F# files
assert True, "F# language server can handle files successfully"
@@ -214,21 +210,19 @@ class TestFSharpLanguageServer:
@pytest.mark.fsharp
class TestFSharpLanguageServerSetup:
"""Test F# language server setup and configuration."""
def test_runtime_dependency_setup_without_dotnet(self) -> None:
"""Test that setup fails gracefully when .NET is not installed."""
with patch("shutil.which", return_value=None):
with pytest.raises(RuntimeError, match=".NET SDK is not installed"):
FSharpLanguageServer._setup_runtime_dependencies(
Mock(), Mock(), Mock()
)
FSharpLanguageServer._setup_runtime_dependencies(Mock(), Mock(), Mock())
def test_runtime_dependency_setup_with_dotnet(self) -> None:
"""Test that setup works when .NET is available."""
mock_logger = Mock()
mock_config = Mock()
mock_settings = Mock()
# Mock the ls_resources_dir to return a temp directory
with tempfile.TemporaryDirectory() as temp_dir:
with patch("shutil.which", return_value="/usr/bin/dotnet"):
@@ -237,16 +231,14 @@ class TestFSharpLanguageServerSetup:
# Mock successful dotnet version check
mock_run.return_value.stdout = "8.0.100"
mock_run.return_value.returncode = 0
# Create a fake fsautocomplete executable
fsharp_dir = os.path.join(temp_dir, "fsharp-lsp")
os.makedirs(fsharp_dir, exist_ok=True)
fsautocomplete_path = os.path.join(fsharp_dir, "fsautocomplete")
Path(fsautocomplete_path).touch()
result = FSharpLanguageServer._setup_runtime_dependencies(
mock_logger, mock_config, mock_settings
)
result = FSharpLanguageServer._setup_runtime_dependencies(mock_logger, mock_config, mock_settings)
assert fsautocomplete_path in result
assert "--lsp" in result
assert "--lsp" in result