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
150 lines
7.0 KiB
Python
150 lines
7.0 KiB
Python
"""
|
|
Basic integration tests for the Elixir language server functionality.
|
|
|
|
These tests validate the functionality of the language server APIs
|
|
like request_references using the test repository.
|
|
"""
|
|
|
|
import os
|
|
|
|
import pytest
|
|
|
|
from solidlsp import SolidLanguageServer
|
|
from solidlsp.ls_config import Language
|
|
from solidlsp.ls_types import SymbolKind
|
|
from test.conftest import 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
|
|
|
|
# These marks will be applied to all tests in this module
|
|
pytestmark = [pytest.mark.elixir, pytest.mark.skipif(not language_tests_enabled(Language.ELIXIR), reason="Elixir tests are disabled")]
|
|
|
|
|
|
class TestElixirBasic:
|
|
"""Basic Elixir language server functionality tests."""
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.ELIXIR], indirect=True)
|
|
def test_request_references_function_definition(self, language_server: SolidLanguageServer):
|
|
"""Test finding references to a function definition."""
|
|
file_path = os.path.join("lib", "models.ex")
|
|
symbols = language_server.request_document_symbols(file_path).get_all_symbols_and_roots()
|
|
|
|
# Find the User module's 'new' function
|
|
user_new_symbol = None
|
|
for symbol in symbols[0]: # Top level symbols
|
|
if symbol.get("name") == "User" and symbol.get("kind") == 2: # Module
|
|
for child in symbol.get("children", []):
|
|
if child.get("name", "").startswith("def new(") and child.get("kind") == 12: # Function
|
|
user_new_symbol = child
|
|
break
|
|
break
|
|
|
|
if not user_new_symbol or "selectionRange" not in user_new_symbol:
|
|
pytest.skip("User.new function or its selectionRange not found")
|
|
|
|
sel_start = user_new_symbol["selectionRange"]["start"]
|
|
references = language_server.request_references(file_path, sel_start["line"], sel_start["character"])
|
|
|
|
assert references is not None
|
|
assert len(references) > 0
|
|
|
|
# Should find at least one reference (the definition itself)
|
|
found_definition = any(ref["uri"].endswith("models.ex") for ref in references)
|
|
assert found_definition, "Should find the function definition"
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.ELIXIR], indirect=True)
|
|
def test_request_references_create_user_function(self, language_server: SolidLanguageServer):
|
|
"""Test finding references to create_user function."""
|
|
file_path = os.path.join("lib", "services.ex")
|
|
symbols = language_server.request_document_symbols(file_path).get_all_symbols_and_roots()
|
|
|
|
# Find the UserService module's 'create_user' function
|
|
create_user_symbol = None
|
|
for symbol in symbols[0]: # Top level symbols
|
|
if symbol.get("name") == "UserService" and symbol.get("kind") == 2: # Module
|
|
for child in symbol.get("children", []):
|
|
if child.get("name", "").startswith("def create_user(") and child.get("kind") == 12: # Function
|
|
create_user_symbol = child
|
|
break
|
|
break
|
|
|
|
if not create_user_symbol or "selectionRange" not in create_user_symbol:
|
|
pytest.skip("UserService.create_user function or its selectionRange not found")
|
|
|
|
sel_start = create_user_symbol["selectionRange"]["start"]
|
|
references = language_server.request_references(file_path, sel_start["line"], sel_start["character"])
|
|
|
|
assert references is not None
|
|
assert len(references) > 0
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.ELIXIR], indirect=True)
|
|
def test_request_referencing_symbols_function(self, language_server: SolidLanguageServer):
|
|
"""Test finding symbols that reference a specific function."""
|
|
file_path = os.path.join("lib", "models.ex")
|
|
symbols = language_server.request_document_symbols(file_path).get_all_symbols_and_roots()
|
|
|
|
# Find the User module's 'new' function
|
|
user_new_symbol = None
|
|
for symbol in symbols[0]: # Top level symbols
|
|
if symbol.get("name") == "User" and symbol.get("kind") == 2: # Module
|
|
for child in symbol.get("children", []):
|
|
if child.get("name", "").startswith("def new(") and child.get("kind") == 12: # Function
|
|
user_new_symbol = child
|
|
break
|
|
break
|
|
|
|
if not user_new_symbol or "selectionRange" not in user_new_symbol:
|
|
pytest.skip("User.new function or its selectionRange not found")
|
|
|
|
sel_start = user_new_symbol["selectionRange"]["start"]
|
|
referencing_symbols = language_server.request_referencing_symbols(file_path, sel_start["line"], sel_start["character"])
|
|
|
|
assert referencing_symbols is not None
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.ELIXIR], indirect=True)
|
|
def test_timeout_enumeration_bug(self, language_server: SolidLanguageServer):
|
|
"""Test that enumeration doesn't timeout (regression test)."""
|
|
# This should complete without timing out
|
|
symbols = language_server.request_document_symbols("lib/models.ex").get_all_symbols_and_roots()
|
|
assert symbols is not None
|
|
|
|
# Test multiple symbol requests in succession
|
|
for _ in range(3):
|
|
symbols = language_server.request_document_symbols("lib/services.ex").get_all_symbols_and_roots()
|
|
assert symbols is not None
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.ELIXIR], 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 s["kind"] in {SymbolKind.Module, SymbolKind.Namespace, SymbolKind.Struct}:
|
|
continue
|
|
|
|
allow_test_style_name = s["name"].startswith(('test "', 'describe "'))
|
|
allow_struct_name = s["name"].startswith("%")
|
|
allow_type_name = s["name"].startswith("@type ")
|
|
if has_malformed_name(
|
|
s,
|
|
whitespace_allowed=allow_test_style_name or allow_type_name,
|
|
period_allowed=allow_struct_name,
|
|
colon_allowed=allow_test_style_name,
|
|
brace_allowed=allow_test_style_name or allow_struct_name,
|
|
comma_allowed=allow_test_style_name,
|
|
):
|
|
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,
|
|
)
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.ELIXIR], indirect=True)
|
|
def test_file_diagnostics(self, language_server: SolidLanguageServer) -> None:
|
|
assert_file_diagnostics(
|
|
language_server,
|
|
"lib/diagnostics_sample.ex",
|
|
(),
|
|
min_count=1,
|
|
)
|