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
629 lines
33 KiB
Python
629 lines
33 KiB
Python
"""
|
|
Tests for the Ruby language server symbol-related functionality.
|
|
|
|
These tests focus on the following methods:
|
|
- request_containing_symbol
|
|
- request_referencing_symbols
|
|
- request_defining_symbol
|
|
- request_document_symbols integration
|
|
"""
|
|
|
|
import os
|
|
|
|
import pytest
|
|
|
|
from serena.util.text_utils import find_text_coordinates
|
|
from solidlsp import SolidLanguageServer
|
|
from solidlsp.ls_config import Language
|
|
from solidlsp.ls_types import SymbolKind
|
|
|
|
pytestmark = pytest.mark.ruby
|
|
|
|
|
|
class TestRubyLanguageServerSymbols:
|
|
"""Test the Ruby language server's symbol-related functionality."""
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_containing_symbol_method(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_containing_symbol for a method."""
|
|
# Test for a position inside the create_user method
|
|
file_path = os.path.join("services.rb")
|
|
# Look for a position inside the create_user method body
|
|
containing_symbol = language_server.request_containing_symbol(file_path, 11, 10, include_body=True)
|
|
|
|
# Verify that we found the containing symbol
|
|
assert containing_symbol is not None, "Should find containing symbol for method position"
|
|
assert containing_symbol["name"] == "create_user", f"Expected 'create_user', got '{containing_symbol['name']}'"
|
|
assert containing_symbol["kind"] == SymbolKind.Method.value, (
|
|
f"Expected Method kind ({SymbolKind.Method.value}), got {containing_symbol['kind']}"
|
|
)
|
|
|
|
# Verify location information
|
|
assert "location" in containing_symbol, "Containing symbol should have location information"
|
|
location = containing_symbol["location"]
|
|
assert "range" in location, "Location should contain range information"
|
|
assert "start" in location["range"], "Range should have start position"
|
|
assert "end" in location["range"], "Range should have end position"
|
|
|
|
# Verify container information
|
|
if "containerName" in containing_symbol:
|
|
assert containing_symbol["containerName"] in [
|
|
"Services::UserService",
|
|
"UserService",
|
|
], f"Expected UserService container, got '{containing_symbol['containerName']}'"
|
|
|
|
# Verify body content if available
|
|
if "body" in containing_symbol:
|
|
body = containing_symbol["body"].get_text()
|
|
assert "def create_user" in body, "Method body should contain method definition"
|
|
assert len(body.strip()) > 0, "Method body should not be empty"
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_containing_symbol_class(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_containing_symbol for a class."""
|
|
# Test for a position inside the UserService class but outside any method
|
|
file_path = os.path.join("services.rb")
|
|
# Line around the class definition
|
|
containing_symbol = language_server.request_containing_symbol(file_path, 5, 5)
|
|
|
|
# Verify that we found the containing symbol
|
|
assert containing_symbol is not None, "Should find containing symbol for class position"
|
|
assert containing_symbol["name"] == "UserService", f"Expected 'UserService', got '{containing_symbol['name']}'"
|
|
assert containing_symbol["kind"] == SymbolKind.Class.value, (
|
|
f"Expected Class kind ({SymbolKind.Class.value}), got {containing_symbol['kind']}"
|
|
)
|
|
|
|
# Verify location information exists
|
|
assert "location" in containing_symbol, "Class symbol should have location information"
|
|
location = containing_symbol["location"]
|
|
assert "range" in location, "Location should contain range"
|
|
assert "start" in location["range"] and "end" in location["range"], "Range should have start and end positions"
|
|
|
|
# Verify the class is properly nested in the Services module
|
|
if "containerName" in containing_symbol:
|
|
assert containing_symbol["containerName"] == "Services", (
|
|
f"Expected 'Services' as container, got '{containing_symbol['containerName']}'"
|
|
)
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_containing_symbol_module(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_containing_symbol for a module context."""
|
|
# Test that we can find the Services module in document symbols
|
|
file_path = os.path.join("services.rb")
|
|
symbols, _roots = language_server.request_document_symbols(file_path).get_all_symbols_and_roots()
|
|
|
|
# Verify Services module appears in document symbols
|
|
services_module = None
|
|
for symbol in symbols:
|
|
if symbol.get("name") == "Services" and symbol.get("kind") == SymbolKind.Module:
|
|
services_module = symbol
|
|
break
|
|
|
|
assert services_module is not None, "Services module not found in document symbols"
|
|
|
|
# Test that UserService class has Services as container
|
|
# Position inside UserService class
|
|
containing_symbol = language_server.request_containing_symbol(file_path, 4, 8)
|
|
assert containing_symbol is not None
|
|
assert containing_symbol["name"] == "UserService"
|
|
assert containing_symbol["kind"] == SymbolKind.Class
|
|
# Verify the module context is preserved in containerName (if supported by the language server)
|
|
# ruby-lsp doesn't provide containerName, but Solargraph does
|
|
if "containerName" in containing_symbol:
|
|
assert containing_symbol.get("containerName") == "Services"
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_containing_symbol_nested_class(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_containing_symbol with nested classes."""
|
|
# Test for a position inside a nested class method
|
|
file_path = os.path.join("nested.rb")
|
|
# Position inside NestedClass.find_me method
|
|
containing_symbol = language_server.request_containing_symbol(file_path, 20, 10)
|
|
|
|
# Verify that we found the innermost containing symbol
|
|
assert containing_symbol is not None
|
|
assert containing_symbol["name"] == "find_me"
|
|
assert containing_symbol["kind"] == SymbolKind.Method
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_containing_symbol_none(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_containing_symbol for a position with no containing symbol."""
|
|
# Test for a position outside any class/method (e.g., in requires)
|
|
file_path = os.path.join("services.rb")
|
|
# Line 1 is a require statement, not inside any class or method
|
|
containing_symbol = language_server.request_containing_symbol(file_path, 1, 5)
|
|
|
|
# Should return None or an empty dictionary
|
|
assert containing_symbol is None or containing_symbol == {}
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_referencing_symbols_method(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_referencing_symbols for a method."""
|
|
# Test referencing symbols for create_user method
|
|
file_path = os.path.join("services.rb")
|
|
# Line containing the create_user method definition
|
|
symbols, _roots = language_server.request_document_symbols(file_path).get_all_symbols_and_roots()
|
|
create_user_symbol = None
|
|
|
|
# Find create_user method in the document symbols (Ruby returns flat list)
|
|
for symbol in symbols:
|
|
if symbol.get("name") == "create_user":
|
|
create_user_symbol = symbol
|
|
break
|
|
|
|
if not create_user_symbol or "selectionRange" not in create_user_symbol:
|
|
pytest.skip("create_user symbol or its selectionRange not found")
|
|
|
|
sel_start = create_user_symbol["selectionRange"]["start"]
|
|
ref_symbols = [
|
|
ref.symbol for ref in language_server.request_referencing_symbols(file_path, sel_start["line"], sel_start["character"])
|
|
]
|
|
|
|
# We might not have references in our simple test setup, so just verify structure
|
|
for symbol in ref_symbols:
|
|
assert "name" in symbol
|
|
assert "kind" in symbol
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_referencing_symbols_class(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_referencing_symbols for a class."""
|
|
# Test referencing symbols for User class
|
|
file_path = os.path.join("models.rb")
|
|
# Find User class in document symbols
|
|
symbols, _roots = language_server.request_document_symbols(file_path).get_all_symbols_and_roots()
|
|
user_symbol = None
|
|
|
|
for symbol in symbols:
|
|
if symbol.get("name") == "User":
|
|
user_symbol = symbol
|
|
break
|
|
|
|
if not user_symbol or "selectionRange" not in user_symbol:
|
|
pytest.skip("User symbol or its selectionRange not found")
|
|
|
|
sel_start = user_symbol["selectionRange"]["start"]
|
|
ref_symbols = [
|
|
ref.symbol for ref in language_server.request_referencing_symbols(file_path, sel_start["line"], sel_start["character"])
|
|
]
|
|
|
|
# Verify structure of referencing symbols
|
|
for symbol in ref_symbols:
|
|
assert "name" in symbol
|
|
assert "kind" in symbol
|
|
if "location" in symbol and "range" in symbol["location"]:
|
|
assert "start" in symbol["location"]["range"]
|
|
assert "end" in symbol["location"]["range"]
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_defining_symbol_variable(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_defining_symbol for a variable usage."""
|
|
# Test finding the definition of a variable in a method
|
|
file_path = os.path.join("services.rb")
|
|
# Look for @users variable usage
|
|
defining_symbol = language_server.request_defining_symbol(file_path, 12, 10)
|
|
|
|
# This test might fail if the language server doesn't support it well
|
|
if defining_symbol is not None:
|
|
assert "name" in defining_symbol
|
|
assert "kind" in defining_symbol
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_defining_symbol_class(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_defining_symbol for a class reference."""
|
|
# Test finding the definition of the User class used in services
|
|
file_path = os.path.join("services.rb")
|
|
# Line that references User class
|
|
defining_symbol = language_server.request_defining_symbol(file_path, 11, 15)
|
|
|
|
# This might not work perfectly in all Ruby language servers
|
|
if defining_symbol is not None:
|
|
assert "name" in defining_symbol
|
|
# The name might be "User" or the method that contains it
|
|
assert defining_symbol.get("name") is not None
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_defining_symbol_none(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_defining_symbol for a position with no symbol."""
|
|
# Test for a position with no symbol (e.g., whitespace or comment)
|
|
file_path = os.path.join("services.rb")
|
|
# Line 3 is likely a blank line or comment
|
|
defining_symbol = language_server.request_defining_symbol(file_path, 3, 0)
|
|
|
|
# Should return None for positions with no symbol
|
|
assert defining_symbol is None or defining_symbol == {}
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_defining_symbol_nested_class(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_defining_symbol for nested class access."""
|
|
# Test finding definition of NestedClass
|
|
file_path = os.path.join("nested.rb")
|
|
# Position where NestedClass is referenced
|
|
defining_symbol = language_server.request_defining_symbol(file_path, 44, 25)
|
|
|
|
# This is challenging for many language servers
|
|
if defining_symbol is not None:
|
|
assert "name" in defining_symbol
|
|
assert defining_symbol.get("name") in ["NestedClass", "OuterClass"]
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_symbol_methods_integration(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test the integration between different symbol-related methods."""
|
|
file_path = os.path.join("models.rb")
|
|
|
|
# Step 1: Find a method we know exists
|
|
containing_symbol = language_server.request_containing_symbol(file_path, 8, 5) # inside initialize method
|
|
if containing_symbol is not None:
|
|
assert containing_symbol["name"] == "initialize"
|
|
|
|
# Step 2: Get the defining symbol for the same position
|
|
defining_symbol = language_server.request_defining_symbol(file_path, 8, 5)
|
|
if defining_symbol is not None:
|
|
assert defining_symbol["name"] == "initialize"
|
|
|
|
# Step 3: Verify that they refer to the same symbol type
|
|
assert defining_symbol["kind"] == containing_symbol["kind"]
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_symbol_tree_structure_basic(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test that the symbol tree structure includes Ruby symbols."""
|
|
# Get all symbols in the test repository
|
|
repo_structure = language_server.request_full_symbol_tree()
|
|
assert len(repo_structure) >= 1
|
|
|
|
# Look for our Ruby files in the structure
|
|
found_ruby_files = False
|
|
for root in repo_structure:
|
|
if "children" in root:
|
|
for child in root["children"]:
|
|
if child.get("name") in ["models", "services", "nested"]:
|
|
found_ruby_files = True
|
|
break
|
|
|
|
# We should find at least some Ruby files in the symbol tree
|
|
assert found_ruby_files, "Ruby files not found in symbol tree"
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_document_symbols_detailed(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test document symbols for detailed Ruby file structure."""
|
|
file_path = os.path.join("models.rb")
|
|
symbols, roots = language_server.request_document_symbols(file_path).get_all_symbols_and_roots()
|
|
|
|
# Verify we have symbols
|
|
assert len(symbols) > 0 or len(roots) > 0
|
|
|
|
# Look for expected class names
|
|
symbol_names = set()
|
|
all_symbols = symbols if symbols else roots
|
|
|
|
for symbol in all_symbols:
|
|
symbol_names.add(symbol.get("name"))
|
|
# Add children names too
|
|
if "children" in symbol:
|
|
for child in symbol["children"]:
|
|
symbol_names.add(child.get("name"))
|
|
|
|
# We should find at least some of our defined classes/methods
|
|
expected_symbols = {"User", "Item", "Order", "ItemHelpers"}
|
|
found_symbols = symbol_names.intersection(expected_symbols)
|
|
assert len(found_symbols) > 0, f"Expected symbols not found. Found: {symbol_names}"
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_module_and_class_hierarchy(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test symbol detection for modules and nested class hierarchies."""
|
|
file_path = os.path.join("nested.rb")
|
|
symbols, roots = language_server.request_document_symbols(file_path).get_all_symbols_and_roots()
|
|
|
|
# Verify we can detect the nested structure
|
|
assert len(symbols) > 0 or len(roots) > 0
|
|
|
|
# Look for OuterClass and its nested elements
|
|
symbol_names = set()
|
|
all_symbols = symbols if symbols else roots
|
|
|
|
for symbol in all_symbols:
|
|
symbol_names.add(symbol.get("name"))
|
|
if "children" in symbol:
|
|
for child in symbol["children"]:
|
|
symbol_names.add(child.get("name"))
|
|
# Check deeply nested too
|
|
if "children" in child:
|
|
for grandchild in child["children"]:
|
|
symbol_names.add(grandchild.get("name"))
|
|
|
|
# Should find the outer class at minimum
|
|
assert "OuterClass" in symbol_names, f"OuterClass not found in symbols: {symbol_names}"
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_references_to_variables(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_referencing_symbols for a variable with detailed verification."""
|
|
file_path = os.path.join("variables.rb")
|
|
# Test references to @status variable in DataContainer class (around line 9)
|
|
ref_symbols = [ref.symbol for ref in language_server.request_referencing_symbols(file_path, 8, 4)]
|
|
|
|
if len(ref_symbols) > 0:
|
|
# Verify we have references
|
|
assert len(ref_symbols) > 0, "Should find references to @status variable"
|
|
|
|
# Check that we have location information
|
|
ref_with_locations = [ref for ref in ref_symbols if "location" in ref and "range" in ref["location"]]
|
|
assert len(ref_with_locations) > 0, "References should include location information"
|
|
|
|
# Verify line numbers are reasonable (should be within the file)
|
|
ref_lines = [ref["location"]["range"]["start"]["line"] for ref in ref_with_locations]
|
|
assert all(line >= 0 for line in ref_lines), "Reference lines should be valid"
|
|
|
|
# Check for specific reference locations we expect
|
|
# Lines where @status is modified/accessed
|
|
expected_line_ranges = [(20, 40), (45, 70)] # Approximate ranges
|
|
found_in_expected_range = any(any(start <= line <= end for start, end in expected_line_ranges) for line in ref_lines)
|
|
assert found_in_expected_range, f"Expected references in ranges {expected_line_ranges}, found lines: {ref_lines}"
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_referencing_symbols_parameter(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_referencing_symbols for a method parameter."""
|
|
# Test referencing symbols for a method parameter in get_user method
|
|
file_path = os.path.join("services.rb")
|
|
# Find get_user method and test parameter references
|
|
symbols, _roots = language_server.request_document_symbols(file_path).get_all_symbols_and_roots()
|
|
get_user_symbol = None
|
|
|
|
for symbol in symbols:
|
|
if symbol.get("name") == "get_user":
|
|
get_user_symbol = symbol
|
|
break
|
|
|
|
if not get_user_symbol or "selectionRange" not in get_user_symbol:
|
|
pytest.skip("get_user symbol or its selectionRange not found")
|
|
|
|
# Test parameter reference within method body
|
|
method_start_line = get_user_symbol["selectionRange"]["start"]["line"]
|
|
ref_symbols = [
|
|
ref.symbol
|
|
for ref in language_server.request_referencing_symbols(file_path, method_start_line + 1, 10) # Position within method body
|
|
]
|
|
|
|
# Verify structure of referencing symbols
|
|
for symbol in ref_symbols:
|
|
assert "name" in symbol, "Symbol should have name"
|
|
assert "kind" in symbol, "Symbol should have kind"
|
|
if "location" in symbol and "range" in symbol["location"]:
|
|
range_info = symbol["location"]["range"]
|
|
assert "start" in range_info, "Range should have start"
|
|
assert "end" in range_info, "Range should have end"
|
|
# Verify line number is valid (references can be before method definition too)
|
|
assert range_info["start"]["line"] >= 0, "Reference line should be valid"
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_referencing_symbols_none(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_referencing_symbols for a position with no symbol."""
|
|
# Test for a position with no symbol (comment or blank line)
|
|
file_path = os.path.join("services.rb")
|
|
|
|
# Try multiple positions that should have no symbols
|
|
test_positions = [(1, 0), (2, 0)] # Comment/require lines
|
|
|
|
for line, char in test_positions:
|
|
try:
|
|
ref_symbols = [ref.symbol for ref in language_server.request_referencing_symbols(file_path, line, char)]
|
|
# If we get here, make sure we got an empty result or minimal results
|
|
if ref_symbols:
|
|
# Some language servers might return minimal info, verify it's reasonable
|
|
assert len(ref_symbols) <= 3, f"Expected few/no references at line {line}, got {len(ref_symbols)}"
|
|
|
|
except Exception as e:
|
|
# Some language servers throw exceptions for invalid positions, which is acceptable
|
|
assert "symbol" in str(e).lower() or "position" in str(e).lower() or "reference" in str(e).lower(), (
|
|
f"Exception should be related to symbol/position/reference issues, got: {e}"
|
|
)
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_dir_overview(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test that request_dir_overview returns correct symbol information for files in a directory."""
|
|
# Get overview of the test repo directory
|
|
overview = language_server.request_dir_overview(".")
|
|
|
|
# Verify that we have entries for our main files
|
|
expected_files = ["services.rb", "models.rb", "variables.rb", "nested.rb"]
|
|
found_files = []
|
|
|
|
for file_path in overview.keys():
|
|
for expected in expected_files:
|
|
if expected in file_path:
|
|
found_files.append(expected)
|
|
break
|
|
|
|
assert len(found_files) >= 2, f"Should find at least 2 expected files, found: {found_files}"
|
|
|
|
# Test specific symbols from services.rb if it exists
|
|
services_file_key = None
|
|
for file_path in overview.keys():
|
|
if "services.rb" in file_path:
|
|
services_file_key = file_path
|
|
break
|
|
|
|
if services_file_key:
|
|
services_symbols = overview[services_file_key]
|
|
assert len(services_symbols) > 0, "services.rb should have symbols"
|
|
|
|
# Check for expected symbols with detailed verification
|
|
symbol_names = [s[0] for s in services_symbols if isinstance(s, tuple) and len(s) > 0]
|
|
if not symbol_names: # If not tuples, try different format
|
|
symbol_names = [s.get("name") for s in services_symbols if hasattr(s, "get")]
|
|
|
|
expected_symbols = ["Services", "UserService", "ItemService"]
|
|
found_expected = [name for name in expected_symbols if name in symbol_names]
|
|
assert len(found_expected) >= 1, f"Should find at least one expected symbol, found: {found_expected} in {symbol_names}"
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_document_overview(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test that request_document_overview returns correct symbol information for a file."""
|
|
# Get overview of the user_management.rb file
|
|
file_path = os.path.join("examples", "user_management.rb")
|
|
overview = language_server.request_document_overview(file_path)
|
|
|
|
# Verify that we have symbol information
|
|
assert len(overview) > 0, "Document overview should contain symbols"
|
|
|
|
# Look for expected symbols from the file
|
|
symbol_names = set()
|
|
for s_info in overview:
|
|
if isinstance(s_info, tuple) and len(s_info) > 0:
|
|
symbol_names.add(s_info[0])
|
|
elif hasattr(s_info, "get"):
|
|
symbol_names.add(s_info.get("name"))
|
|
elif isinstance(s_info, str):
|
|
symbol_names.add(s_info)
|
|
|
|
# We should find some of our defined classes/methods
|
|
expected_symbols = {"UserStats", "UserManager", "process_user_data", "main"}
|
|
found_symbols = symbol_names.intersection(expected_symbols)
|
|
assert len(found_symbols) > 0, f"Expected to find some symbols from {expected_symbols}, found: {symbol_names}"
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_containing_symbol_variable(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_containing_symbol where the target is a variable."""
|
|
# Test for a position inside a variable definition or usage
|
|
file_path = os.path.join("variables.rb")
|
|
# Position around a variable assignment (e.g., @status = "pending")
|
|
containing_symbol = language_server.request_containing_symbol(file_path, 10, 5)
|
|
|
|
# Verify that we found a containing symbol (likely the method or class)
|
|
if containing_symbol is not None:
|
|
assert "name" in containing_symbol, "Containing symbol should have a name"
|
|
assert "kind" in containing_symbol, "Containing symbol should have a kind"
|
|
# The containing symbol should be a method, class, or similar construct
|
|
expected_kinds = [SymbolKind.Method, SymbolKind.Class, SymbolKind.Function, SymbolKind.Constructor]
|
|
assert containing_symbol["kind"] in [k.value for k in expected_kinds], (
|
|
f"Expected containing symbol to be method/class/function, got kind: {containing_symbol['kind']}"
|
|
)
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_containing_symbol_function(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_containing_symbol for a function (not method)."""
|
|
# Test for a position inside a standalone function
|
|
file_path = os.path.join("variables.rb")
|
|
# Position inside the demonstrate_variable_usage function
|
|
containing_symbol = language_server.request_containing_symbol(file_path, 100, 10)
|
|
|
|
if containing_symbol is not None:
|
|
assert containing_symbol["name"] in [
|
|
"demonstrate_variable_usage",
|
|
"main",
|
|
], f"Expected function name, got: {containing_symbol['name']}"
|
|
assert containing_symbol["kind"] in [
|
|
SymbolKind.Function.value,
|
|
SymbolKind.Method.value,
|
|
], f"Expected function or method kind, got: {containing_symbol['kind']}"
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_containing_symbol_nested(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_containing_symbol with nested scopes."""
|
|
# Test for a position inside a method which is inside a class
|
|
file_path = os.path.join("services.rb")
|
|
# Position inside create_user method within UserService class
|
|
containing_symbol = language_server.request_containing_symbol(file_path, 12, 15)
|
|
|
|
# Verify that we found the innermost containing symbol (the method)
|
|
assert containing_symbol is not None
|
|
assert containing_symbol["name"] == "create_user"
|
|
assert containing_symbol["kind"] == SymbolKind.Method
|
|
|
|
# Verify the container context is preserved
|
|
if "containerName" in containing_symbol:
|
|
assert "UserService" in containing_symbol["containerName"]
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_symbol_tree_structure_subdir(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test that the symbol tree structure correctly handles subdirectories."""
|
|
# Get symbols within the examples subdirectory
|
|
examples_structure = language_server.request_full_symbol_tree(within_relative_path="examples")
|
|
|
|
if len(examples_structure) > 0:
|
|
# Should find the examples directory structure
|
|
assert len(examples_structure) >= 1, "Should find examples directory structure"
|
|
|
|
# Look for the user_management file in the structure
|
|
found_user_management = False
|
|
for root in examples_structure:
|
|
if "children" in root:
|
|
for child in root["children"]:
|
|
if "user_management" in child.get("name", ""):
|
|
found_user_management = True
|
|
# Verify the structure includes symbol information
|
|
if "children" in child:
|
|
child_names = [c.get("name") for c in child["children"]]
|
|
expected_names = ["UserStats", "UserManager", "process_user_data"]
|
|
found_expected = [name for name in expected_names if name in child_names]
|
|
assert len(found_expected) > 0, (
|
|
f"Should find symbols in user_management, expected {expected_names}, found {child_names}"
|
|
)
|
|
break
|
|
|
|
if not found_user_management:
|
|
pytest.skip("user_management file not found in examples subdirectory structure")
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_defining_symbol_imported_class(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_defining_symbol for an imported/required class."""
|
|
# Test finding the definition of a class used from another file
|
|
file_path = os.path.join("examples", "user_management.rb")
|
|
# Position where Services::UserService is referenced
|
|
defining_symbol = language_server.request_defining_symbol(file_path, 25, 20)
|
|
|
|
# This might not work perfectly in all Ruby language servers due to require complexity
|
|
if defining_symbol is not None:
|
|
assert "name" in defining_symbol
|
|
# The defining symbol should relate to UserService or Services
|
|
# The defining symbol should relate to UserService, Services, or the containing class
|
|
# Different language servers may resolve this differently
|
|
expected_names = ["UserService", "Services", "new", "UserManager"]
|
|
assert defining_symbol.get("name") in expected_names, f"Expected one of {expected_names}, got: {defining_symbol.get('name')}"
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_defining_symbol_method_call(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_defining_symbol for a method call."""
|
|
# Test finding the definition of a method being called
|
|
file_path = os.path.join("examples", "user_management.rb")
|
|
|
|
# Position at a method call like create_user
|
|
with language_server.open_file(file_path, open_in_ls=False) as fb:
|
|
pos = find_text_coordinates(fb.contents, r"user = @service\.(create_user)")
|
|
|
|
# Verify that we can find the method definition
|
|
defining_symbol = language_server.request_defining_symbol(file_path, pos.line, pos.col)
|
|
assert "name" in defining_symbol
|
|
assert "kind" in defining_symbol
|
|
assert defining_symbol.get("name") == "create_user"
|
|
assert defining_symbol.get("kind") == SymbolKind.Method.value
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_request_defining_symbol_nested_function(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test request_defining_symbol for a nested function or block."""
|
|
# Test finding definition within nested contexts
|
|
file_path = os.path.join("nested.rb")
|
|
# Position inside or referencing nested functionality
|
|
defining_symbol = language_server.request_defining_symbol(file_path, 15, 10)
|
|
|
|
# This is challenging for many language servers
|
|
if defining_symbol is not None:
|
|
assert "name" in defining_symbol
|
|
assert "kind" in defining_symbol
|
|
# Could be method, function, or variable depending on implementation
|
|
valid_kinds = [SymbolKind.Method.value, SymbolKind.Function.value, SymbolKind.Variable.value, SymbolKind.Class.value]
|
|
assert defining_symbol.get("kind") in valid_kinds
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.RUBY], indirect=True)
|
|
def test_containing_symbol_of_var_is_file(self, language_server: SolidLanguageServer) -> None:
|
|
"""Test that the containing symbol of a file-level variable is handled appropriately."""
|
|
# Test behavior with file-level variables or constants
|
|
file_path = os.path.join("variables.rb")
|
|
# Position at file-level variable/constant
|
|
containing_symbol = language_server.request_containing_symbol(file_path, 5, 5)
|
|
|
|
# Different language servers handle file-level symbols differently
|
|
# Some return None, others return file-level containers
|
|
if containing_symbol is not None:
|
|
# If we get a symbol, verify its structure
|
|
assert "name" in containing_symbol
|
|
assert "kind" in containing_symbol
|