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

221 lines
11 KiB
Python

import os
import threading
from typing import Any
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import Language
from solidlsp.ls_utils import SymbolUtils
from test.conftest import (
find_identifier_position,
get_repo_path,
language_has_verified_implementation_support,
language_tests_enabled,
)
from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols
from test.solidlsp.util.diagnostics import assert_file_diagnostics
# Currently, most F# tests fail (regression/instability), so the suite is disabled on CI.
pytestmark = [pytest.mark.fsharp, pytest.mark.skipif(not language_tests_enabled(Language.FSHARP), reason="F# tests are disabled")]
class TestFSharpLanguageServer:
@pytest.mark.parametrize("language_server", [Language.FSHARP], indirect=True)
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"
assert SymbolUtils.symbol_tree_contains_name(symbols, "CalculatorClass"), "CalculatorClass not found in symbol tree"
@pytest.mark.parametrize("language_server", [Language.FSHARP], indirect=True)
def test_get_document_symbols_program(self, language_server: SolidLanguageServer) -> None:
"""Test getting document symbols from the main Program.fs file."""
file_path = os.path.join("Program.fs")
symbols = language_server.request_document_symbols(file_path).get_all_symbols_and_roots()[0]
# Look for expected functions and modules
symbol_names = [s.get("name") for s in symbols]
assert "main" in symbol_names, "main function not found in Program.fs symbols"
if language_has_verified_implementation_support(Language.FSHARP):
@pytest.mark.parametrize("language_server", [Language.FSHARP], indirect=True)
def test_find_implementations(self, language_server: SolidLanguageServer) -> None:
repo_path = get_repo_path(Language.FSHARP)
pos = find_identifier_position(repo_path / "Formatter.fs", "FormatGreeting")
assert pos is not None, "Could not find IGreeter.FormatGreeting in fixture"
implementations = language_server.request_implementation("Formatter.fs", *pos)
assert implementations, "Expected at least one implementation of IGreeter.FormatGreeting"
assert any("Formatter.fs" in implementation.get("relativePath", "") for implementation in implementations), (
f"Expected ConsoleGreeter.FormatGreeting in implementations, got: {implementations}"
)
@pytest.mark.parametrize("language_server", [Language.FSHARP], indirect=True)
def test_request_implementing_symbols(self, language_server: SolidLanguageServer) -> None:
repo_path = get_repo_path(Language.FSHARP)
pos = find_identifier_position(repo_path / "Formatter.fs", "FormatGreeting")
assert pos is not None, "Could not find IGreeter.FormatGreeting in fixture"
implementing_symbols = language_server.request_implementing_symbols("Formatter.fs", *pos)
assert implementing_symbols, "Expected implementing symbols for IGreeter.FormatGreeting"
assert any(
symbol.get("name") == "FormatGreeting" and "Formatter.fs" in symbol["location"].get("relativePath", "")
for symbol in implementing_symbols
), f"Expected ConsoleGreeter.FormatGreeting symbol, got: {implementing_symbols}"
@pytest.mark.parametrize("language_server", [Language.FSHARP], indirect=True)
def test_get_document_symbols_calculator(self, language_server: SolidLanguageServer) -> None:
"""Test getting document symbols from Calculator.fs file."""
file_path = os.path.join("Calculator.fs")
symbols = language_server.request_document_symbols(file_path).get_all_symbols_and_roots()[0]
# 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"
@pytest.mark.parametrize("language_server", [Language.FSHARP], indirect=True)
def test_find_referencing_symbols(self, language_server: SolidLanguageServer) -> None:
"""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
for sym in symbols.iter_symbols():
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"
@pytest.mark.parametrize("language_server", [Language.FSHARP], indirect=True)
def test_nested_module_symbols(self, language_server: SolidLanguageServer) -> None:
"""Test getting symbols from nested Models namespace."""
file_path = os.path.join("Models", "Person.fs")
symbols = language_server.request_document_symbols(file_path).get_all_symbols_and_roots()[0]
# 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"
@pytest.mark.parametrize("language_server", [Language.FSHARP], indirect=True)
def test_find_referencing_symbols_across_files(self, language_server: SolidLanguageServer) -> None:
"""Test finding references to Calculator functions across files."""
# Find the subtract function in Calculator.fs
file_path = os.path.join("Calculator.fs")
symbols = language_server.request_document_symbols(file_path)
subtract_symbol = None
for sym in symbols.iter_symbols():
if sym.get("name") == "subtract":
subtract_symbol = sym
break
assert subtract_symbol is not None, "Could not find 'subtract' function symbol"
# Find references to subtract function
sel_start = subtract_symbol["selectionRange"]["start"]
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"
@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)"
@pytest.mark.parametrize("language_server", [Language.FSHARP], indirect=True)
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"
@pytest.mark.parametrize("language_server", [Language.FSHARP], indirect=True)
def test_completion(self, language_server: SolidLanguageServer) -> None:
"""Test code completion functionality."""
file_path = os.path.join("Program.fs")
# Use threading for cross-platform timeout (signal.SIGALRM is Unix-only)
result: dict[str, Any] = dict(value=None)
exception: dict[str, Any] = dict(value=None)
def run_completion():
try:
result["value"] = language_server.request_completions(file_path, 15, 10)
except Exception as e:
exception["value"] = e
thread = threading.Thread(target=run_completion, daemon=True)
thread.start()
thread.join(timeout=5) # 5 second timeout
if thread.is_alive():
# Completion timed out, but this is acceptable for F# in some cases
# The important thing is that the language server doesn't crash
return
if exception["value"]:
raise exception["value"]
assert isinstance(result["value"], list), "Completions should be a list"
@pytest.mark.parametrize("language_server", [Language.FSHARP], indirect=True)
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
assert_file_diagnostics(
language_server,
"DiagnosticsSample.fs",
(),
min_count=1,
)
@pytest.mark.parametrize("language_server", [Language.FSHARP], indirect=True)
def test_bare_symbol_names(self, language_server) -> None:
all_symbols = request_all_symbols(language_server)
malformed_symbols = []
for s in all_symbols:
if has_malformed_name(s):
malformed_symbols.append(s)
if malformed_symbols:
pytest.fail(
f"Found malformed symbols: {[format_symbol_for_assert(sym) for sym in malformed_symbols]}",
pytrace=False,
)