Skip to main content
Glama
test_dart_basic.py20.7 kB
import os from pathlib import Path import pytest from solidlsp import SolidLanguageServer from solidlsp.ls_config import Language from solidlsp.ls_types import SymbolKind from solidlsp.ls_utils import SymbolUtils @pytest.mark.dart class TestDartLanguageServer: @pytest.mark.parametrize("language_server", [Language.DART], indirect=True) @pytest.mark.parametrize("repo_path", [Language.DART], indirect=True) def test_ls_is_running(self, language_server: SolidLanguageServer, repo_path: Path) -> None: """Test that the language server starts and stops successfully.""" # The fixture already handles start and stop assert language_server.is_running() assert Path(language_server.language_server.repository_root_path).resolve() == repo_path.resolve() @pytest.mark.parametrize("language_server", [Language.DART], indirect=True) @pytest.mark.parametrize("repo_path", [Language.DART], indirect=True) def test_find_definition_within_file(self, language_server: SolidLanguageServer, repo_path: Path) -> None: """Test finding definition of a method within the same file.""" # In lib/main.dart: # Line 105: final result1 = calc.add(5, 3); // Reference to add method # Line 12: int add(int a, int b) { // Definition of add method # Find definition of 'add' method from its usage main_dart_path = str(repo_path / "lib" / "main.dart") # Position: calc.add(5, 3) - cursor on 'add' # Line 105 (1-indexed) = line 104 (0-indexed), char position around 22 definition_location_list = language_server.request_definition(main_dart_path, 104, 22) assert definition_location_list, f"Expected non-empty definition_location_list but got {definition_location_list=}" assert len(definition_location_list) >= 1 definition_location = definition_location_list[0] assert definition_location["uri"].endswith("main.dart") # Definition of add method should be around line 11 (0-indexed) # But language server may return different positions assert definition_location["range"]["start"]["line"] >= 0 @pytest.mark.parametrize("language_server", [Language.DART], indirect=True) @pytest.mark.parametrize("repo_path", [Language.DART], indirect=True) def test_find_definition_across_files(self, language_server: SolidLanguageServer, repo_path: Path) -> None: """Test finding definition across different files.""" # Test finding definition of MathHelper class which is in helper.dart # In lib/main.dart line 50: MathHelper.power(step1, 2) main_dart_path = str(repo_path / "lib" / "main.dart") # Position: MathHelper.power(step1, 2) - cursor on 'MathHelper' # Line 50 (1-indexed) = line 49 (0-indexed), char position around 18 definition_location_list = language_server.request_definition(main_dart_path, 49, 18) # Skip the test if language server doesn't find cross-file references # This is acceptable for a basic test - the important thing is that LS is working if not definition_location_list: pytest.skip("Language server doesn't support cross-file definition lookup for this case") assert len(definition_location_list) >= 1 definition_location = definition_location_list[0] assert definition_location["uri"].endswith("helper.dart") assert definition_location["range"]["start"]["line"] >= 0 @pytest.mark.parametrize("language_server", [Language.DART], indirect=True) @pytest.mark.parametrize("repo_path", [Language.DART], indirect=True) def test_find_definition_class_method(self, language_server: SolidLanguageServer, repo_path: Path) -> None: """Test finding definition of a class method.""" # In lib/main.dart: # Line 50: final step2 = MathHelper.power(step1, 2); // Reference to MathHelper.power method # In lib/helper.dart: # Line 14: static double power(double base, int exponent) { // Definition of power method main_dart_path = str(repo_path / "lib" / "main.dart") # Position: MathHelper.power(step1, 2) - cursor on 'power' # Line 50 (1-indexed) = line 49 (0-indexed), char position around 30 definition_location_list = language_server.request_definition(main_dart_path, 49, 30) assert definition_location_list, f"Expected non-empty definition_location_list but got {definition_location_list=}" assert len(definition_location_list) >= 1 definition_location = definition_location_list[0] assert definition_location["uri"].endswith("helper.dart") # Definition of power method should be around line 13 (0-indexed) assert 12 <= definition_location["range"]["start"]["line"] <= 16 @pytest.mark.parametrize("language_server", [Language.DART], indirect=True) @pytest.mark.parametrize("repo_path", [Language.DART], indirect=True) def test_find_references_within_file(self, language_server: SolidLanguageServer, repo_path: Path) -> None: """Test finding references to a method within the same file.""" main_dart_path = str(repo_path / "lib" / "main.dart") # Find references to the 'add' method from its definition # Line 12: int add(int a, int b) { // Definition of add method # Line 105: final result1 = calc.add(5, 3); // Usage of add method references = language_server.request_references(main_dart_path, 11, 6) # cursor on 'add' in definition assert references, f"Expected non-empty references but got {references=}" # Should find at least the usage of add method assert len(references) >= 1 # Check that we have a reference in main.dart main_dart_references = [ref for ref in references if ref["uri"].endswith("main.dart")] assert len(main_dart_references) >= 1 @pytest.mark.parametrize("language_server", [Language.DART], indirect=True) @pytest.mark.parametrize("repo_path", [Language.DART], indirect=True) def test_find_references_across_files(self, language_server: SolidLanguageServer, repo_path: Path) -> None: """Test finding references across different files.""" helper_dart_path = str(repo_path / "lib" / "helper.dart") # Find references to the 'subtract' function from its definition in helper.dart # Definition is in helper.dart, usage is in main.dart references = language_server.request_references(helper_dart_path, 4, 4) # cursor on 'subtract' in definition assert references, f"Expected non-empty references for subtract function but got {references=}" # Should find references in main.dart main_dart_references = [ref for ref in references if ref["uri"].endswith("main.dart")] assert len(main_dart_references) >= 1 @pytest.mark.parametrize("language_server", [Language.DART], indirect=True) @pytest.mark.parametrize("repo_path", [Language.DART], indirect=True) def test_find_definition_constructor(self, language_server: SolidLanguageServer, repo_path: Path) -> None: """Test finding definition of a constructor call.""" main_dart_path = str(repo_path / "lib" / "main.dart") # In lib/main.dart: # Line 104: final calc = Calculator(); // Reference to Calculator constructor # Line 4: class Calculator { // Definition of Calculator class definition_location_list = language_server.request_definition(main_dart_path, 103, 18) # cursor on 'Calculator' assert definition_location_list, f"Expected non-empty definition_location_list but got {definition_location_list=}" assert len(definition_location_list) >= 1 definition_location = definition_location_list[0] assert definition_location["uri"].endswith("main.dart") # Definition of Calculator class should be around line 3 (0-indexed) assert 3 <= definition_location["range"]["start"]["line"] <= 7 @pytest.mark.parametrize("language_server", [Language.DART], indirect=True) @pytest.mark.parametrize("repo_path", [Language.DART], indirect=True) def test_find_definition_import(self, language_server: SolidLanguageServer, repo_path: Path) -> None: """Test finding definition through imports.""" models_dart_path = str(repo_path / "lib" / "models.dart") # Test finding definition of User class name where it's used # In lib/models.dart line 27 (constructor): User(this.id, this.name, this.email, this._age); definition_location_list = language_server.request_definition(models_dart_path, 26, 2) # cursor on 'User' in constructor # Skip if language server doesn't find definition in this case if not definition_location_list: pytest.skip("Language server doesn't support definition lookup for this case") assert len(definition_location_list) >= 1 definition_location = definition_location_list[0] # Language server might return SDK files instead of local files # This is acceptable behavior - the important thing is that it found a definition assert "dart" in definition_location["uri"].lower() @pytest.mark.parametrize("language_server", [Language.DART], 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() assert SymbolUtils.symbol_tree_contains_name(symbols, "Calculator"), "Calculator class not found in symbol tree" assert SymbolUtils.symbol_tree_contains_name(symbols, "add"), "add method not found in symbol tree" assert SymbolUtils.symbol_tree_contains_name(symbols, "subtract"), "subtract function not found in symbol tree" assert SymbolUtils.symbol_tree_contains_name(symbols, "MathHelper"), "MathHelper class not found in symbol tree" assert SymbolUtils.symbol_tree_contains_name(symbols, "User"), "User class not found in symbol tree" @pytest.mark.parametrize("language_server", [Language.DART], indirect=True) def test_find_referencing_symbols(self, language_server: SolidLanguageServer) -> None: """Test finding references using symbol selection range.""" file_path = os.path.join("lib", "main.dart") symbols = language_server.request_document_symbols(file_path).get_all_symbols_and_roots() # Handle nested symbol structure - symbols can be nested in lists symbol_list = symbols[0] if symbols and isinstance(symbols[0], list) else symbols # Find the 'add' method symbol in Calculator class add_symbol = None for sym in symbol_list: if sym.get("name") == "add": add_symbol = sym break # Check for nested symbols (methods inside classes) if "children" in sym and sym.get("name") == "Calculator": for child in sym["children"]: if child.get("name") == "add": add_symbol = child break if add_symbol: break assert add_symbol is not None, "Could not find 'add' method symbol in main.dart" sel_start = add_symbol["selectionRange"]["start"] refs = language_server.request_references(file_path, sel_start["line"], sel_start["character"]) # Check that we found references - at least one should be in main.dart assert any( "main.dart" in ref.get("relativePath", "") or "main.dart" in ref.get("uri", "") for ref in refs ), "main.dart should reference add method (tried all positions in selectionRange)" @pytest.mark.parametrize("language_server", [Language.DART], indirect=True) def test_request_containing_symbol_method(self, language_server: SolidLanguageServer) -> None: """Test request_containing_symbol for a method.""" file_path = os.path.join("lib", "main.dart") # Line 14 is inside the add method body (around 'final result = a + b;') containing_symbol = language_server.request_containing_symbol(file_path, 13, 10, include_body=True) # Verify that we found the containing symbol if containing_symbol is not None: assert containing_symbol["name"] == "add" assert containing_symbol["kind"] == SymbolKind.Method if "body" in containing_symbol: assert "add" in containing_symbol["body"] or "final result" in containing_symbol["body"] @pytest.mark.parametrize("language_server", [Language.DART], indirect=True) def test_request_containing_symbol_class(self, language_server: SolidLanguageServer) -> None: """Test request_containing_symbol for a class.""" file_path = os.path.join("lib", "main.dart") # Line 4 is the Calculator class definition line containing_symbol = language_server.request_containing_symbol(file_path, 4, 6) # Verify that we found the containing symbol if containing_symbol is not None: assert containing_symbol["name"] == "Calculator" assert containing_symbol["kind"] == SymbolKind.Class @pytest.mark.parametrize("language_server", [Language.DART], indirect=True) def test_request_containing_symbol_nested(self, language_server: SolidLanguageServer) -> None: """Test request_containing_symbol with nested scopes.""" file_path = os.path.join("lib", "main.dart") # Line 14 is inside the add method inside Calculator class containing_symbol = language_server.request_containing_symbol(file_path, 13, 20) # Verify that we found the innermost containing symbol (the method) if containing_symbol is not None: assert containing_symbol["name"] == "add" assert containing_symbol["kind"] == SymbolKind.Method @pytest.mark.parametrize("language_server", [Language.DART], indirect=True) def test_request_defining_symbol_variable(self, language_server: SolidLanguageServer) -> None: """Test request_defining_symbol for a variable usage.""" file_path = os.path.join("lib", "main.dart") # Line 14 contains 'final result = a + b;' - test position on 'result' defining_symbol = language_server.request_defining_symbol(file_path, 13, 10) # The defining symbol might be the variable itself or the containing method # This is acceptable behavior - different language servers handle this differently if defining_symbol is not None: assert defining_symbol.get("name") in ["result", "add"] if defining_symbol.get("name") == "add": assert defining_symbol.get("kind") == SymbolKind.Method.value @pytest.mark.parametrize("language_server", [Language.DART], indirect=True) def test_request_defining_symbol_imported_class(self, language_server: SolidLanguageServer) -> None: """Test request_defining_symbol for an imported class/function.""" file_path = os.path.join("lib", "main.dart") # Line 20 references 'subtract' which was imported from helper.dart defining_symbol = language_server.request_defining_symbol(file_path, 19, 18) # Verify that we found the defining symbol - this should be the subtract function from helper.dart if defining_symbol is not None: assert defining_symbol.get("name") == "subtract" # Could be Function or Method depending on language server interpretation assert defining_symbol.get("kind") in [SymbolKind.Function.value, SymbolKind.Method.value] @pytest.mark.parametrize("language_server", [Language.DART], indirect=True) def test_request_defining_symbol_class_method(self, language_server: SolidLanguageServer) -> None: """Test request_defining_symbol for a static class method.""" file_path = os.path.join("lib", "main.dart") # Line 50 references MathHelper.power - test position on 'power' defining_symbol = language_server.request_defining_symbol(file_path, 49, 30) # Verify that we found the defining symbol - should be the power method if defining_symbol is not None: assert defining_symbol.get("name") == "power" assert defining_symbol.get("kind") == SymbolKind.Method.value @pytest.mark.parametrize("language_server", [Language.DART], indirect=True) def test_request_document_symbols(self, language_server: SolidLanguageServer) -> None: """Test getting document symbols from a Dart file.""" file_path = os.path.join("lib", "main.dart") symbols = language_server.request_document_symbols(file_path).get_all_symbols_and_roots() # Check that we have symbols assert len(symbols) > 0 # Flatten the symbols if they're nested symbol_list = symbols[0] if symbols and isinstance(symbols[0], list) else symbols # Look for expected classes and methods symbol_names = [s.get("name") for s in symbol_list] assert "Calculator" in symbol_names # Check for nested symbols (methods inside classes) - optional calculator_symbol = next((s for s in symbol_list if s.get("name") == "Calculator"), None) if calculator_symbol and "children" in calculator_symbol and calculator_symbol["children"]: method_names = [child.get("name") for child in calculator_symbol["children"]] # If children are populated, we should find the add method assert "add" in method_names else: # Some language servers may not populate children in document symbols # This is acceptable behavior - the important thing is we found the class pass @pytest.mark.parametrize("language_server", [Language.DART], indirect=True) def test_request_referencing_symbols_comprehensive(self, language_server: SolidLanguageServer) -> None: """Test comprehensive referencing symbols functionality.""" file_path = os.path.join("lib", "main.dart") symbols = language_server.request_document_symbols(file_path).get_all_symbols_and_roots() # Handle nested symbol structure symbol_list = symbols[0] if symbols and isinstance(symbols[0], list) else symbols # Find Calculator class and test its references calculator_symbol = None for sym in symbol_list: if sym.get("name") == "Calculator": calculator_symbol = sym break if calculator_symbol and "selectionRange" in calculator_symbol: sel_start = calculator_symbol["selectionRange"]["start"] refs = language_server.request_references(file_path, sel_start["line"], sel_start["character"]) # Should find references to Calculator (constructor calls, etc.) if refs: # Verify the structure of referencing symbols for ref in refs: assert "uri" in ref or "relativePath" in ref if "range" in ref: assert "start" in ref["range"] assert "end" in ref["range"] @pytest.mark.parametrize("language_server", [Language.DART], indirect=True) def test_cross_file_symbol_resolution(self, language_server: SolidLanguageServer) -> None: """Test symbol resolution across multiple files.""" helper_file_path = os.path.join("lib", "helper.dart") # Test finding references to subtract function from helper.dart in main.dart helper_symbols = language_server.request_document_symbols(helper_file_path).get_all_symbols_and_roots() symbol_list = helper_symbols[0] if helper_symbols and isinstance(helper_symbols[0], list) else helper_symbols subtract_symbol = next((s for s in symbol_list if s.get("name") == "subtract"), None) if subtract_symbol and "selectionRange" in subtract_symbol: sel_start = subtract_symbol["selectionRange"]["start"] refs = language_server.request_references(helper_file_path, sel_start["line"], sel_start["character"]) # Should find references in main.dart main_dart_refs = [ref for ref in refs if "main.dart" in ref.get("uri", "") or "main.dart" in ref.get("relativePath", "")] # Note: This may not always work depending on language server capabilities # So we don't assert - just verify the structure if we get results if main_dart_refs: for ref in main_dart_refs: assert "range" in ref or "location" in ref

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/oraios/serena'

If you have feedback or need assistance with the MCP directory API, please join our Discord server