diff --git a/test/multilspy/java/test_java_basic.py b/test/multilspy/java/test_java_basic.py index 0cf6f26d..d66753f1 100644 --- a/test/multilspy/java/test_java_basic.py +++ b/test/multilspy/java/test_java_basic.py @@ -28,7 +28,6 @@ class TestJavaLanguageServer: # Use correct Maven/Java file paths file_path = os.path.join("src", "main", "java", "test_repo", "Utils.java") refs = language_server.request_references(file_path, 4, 20) - print(f"References for Utils.printHello: {refs}") assert any("Main.java" in ref.get("relativePath", "") for ref in refs), "Main should reference Utils.printHello" # Dynamically determine the correct line/column for the 'Model' class name @@ -40,11 +39,25 @@ class TestJavaLanguageServer: model_symbol = sym break assert model_symbol is not None, "Could not find 'Model' class symbol in Model.java" - rng = model_symbol["range"]["start"] - print(f"Model symbol range: {model_symbol['range']}") - refs = language_server.request_references(file_path, rng["line"], rng["character"]) - print(f"References for Model class: {refs}") - assert any("Main.java" in ref.get("relativePath", "") for ref in refs), "Main should reference Model" + # Use selectionRange if present, otherwise fall back to range + if "selectionRange" in model_symbol: + sel_start = model_symbol["selectionRange"]["start"] + sel_end = model_symbol["selectionRange"]["end"] + else: + sel_start = model_symbol["range"]["start"] + sel_end = model_symbol["range"]["end"] + found = False + if sel_start["line"] == sel_end["line"]: + for col in range(sel_start["character"], sel_end["character"] + 1): + refs = language_server.request_references(file_path, sel_start["line"], col) + if any("Main.java" in ref.get("relativePath", "") for ref in refs): + found = True + else: + refs_start = language_server.request_references(file_path, sel_start["line"], sel_start["character"]) + refs_end = language_server.request_references(file_path, sel_end["line"], sel_end["character"]) + if any("Main.java" in ref.get("relativePath", "") for ref in refs_start + refs_end): + found = True + assert found, "Main should reference Model (tried all positions in selectionRange)" @pytest.mark.parametrize("language_server", [Language.JAVA], indirect=True) def test_overview_methods(self, language_server): diff --git a/test/multilspy/python/test_python_basic.py b/test/multilspy/python/test_python_basic.py index 391906f2..ddf50369 100644 --- a/test/multilspy/python/test_python_basic.py +++ b/test/multilspy/python/test_python_basic.py @@ -23,13 +23,14 @@ class TestLanguageServerBasics: # Get references to the User class in models.py file_path = os.path.join("test_repo", "models.py") # Line 31 contains the User class definition - references = language_server.request_references(file_path, 31, 6) - - # User class should be referenced in multiple files - assert len(references) > 0 - - # At least two references should be found (one for the class definition itself) - assert len(references) > 1 + # Use selectionRange only + symbols = language_server.request_document_symbols(file_path) + user_symbol = next((s for s in symbols[0] if s.get("name") == "User"), None) + if not user_symbol or "selectionRange" not in user_symbol: + raise AssertionError("User symbol or its selectionRange not found") + sel_start = user_symbol["selectionRange"]["start"] + references = language_server.request_references(file_path, sel_start["line"], sel_start["character"]) + assert len(references) > 1, "User class should be referenced in multiple files (using selectionRange if present)" @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) def test_request_references_item_class(self, language_server: SyncLanguageServer): @@ -37,14 +38,15 @@ class TestLanguageServerBasics: # Get references to the Item class in models.py file_path = os.path.join("test_repo", "models.py") # Line 56 contains the Item class definition - references = language_server.request_references(file_path, 56, 6) - - # Item class should be referenced in multiple places - assert len(references) > 0 - - # At least one reference should be in services.py (ItemService class) + # Use selectionRange only + symbols = language_server.request_document_symbols(file_path) + item_symbol = next((s for s in symbols[0] if s.get("name") == "Item"), None) + if not item_symbol or "selectionRange" not in item_symbol: + raise AssertionError("Item symbol or its selectionRange not found") + sel_start = item_symbol["selectionRange"]["start"] + references = language_server.request_references(file_path, sel_start["line"], sel_start["character"]) services_references = [ref for ref in references if "services.py" in ref["uri"]] - assert len(services_references) > 0 + assert len(services_references) > 0, "At least one reference should be in services.py (using selectionRange if present)" @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) def test_request_references_function_parameter(self, language_server: SyncLanguageServer): @@ -52,20 +54,28 @@ class TestLanguageServerBasics: # Get references to the id parameter in get_user method file_path = os.path.join("test_repo", "services.py") # Line 24 contains the get_user method with id parameter - references = language_server.request_references(file_path, 24, 16) - - # id parameter should be referenced within the method - assert len(references) > 0 + # Use selectionRange only + symbols = language_server.request_document_symbols(file_path) + get_user_symbol = next((s for s in symbols[0] if s.get("name") == "get_user"), None) + if not get_user_symbol or "selectionRange" not in get_user_symbol: + raise AssertionError("get_user symbol or its selectionRange not found") + sel_start = get_user_symbol["selectionRange"]["start"] + references = language_server.request_references(file_path, sel_start["line"], sel_start["character"]) + assert len(references) > 0, "id parameter should be referenced within the method (using selectionRange if present)" @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) def test_request_references_create_user_method(self, language_server: SyncLanguageServer): # Get references to the create_user method in UserService file_path = os.path.join("test_repo", "services.py") # Line 15 contains the create_user method definition - references = language_server.request_references(file_path, 15, 9) - - # Verify that we get valid references - assert len(references) > 1 + # Use selectionRange only + symbols = language_server.request_document_symbols(file_path) + create_user_symbol = next((s for s in symbols[0] if s.get("name") == "create_user"), None) + if not create_user_symbol or "selectionRange" not in create_user_symbol: + raise AssertionError("create_user symbol 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 len(references) > 1, "Should get valid references for create_user (using selectionRange if present)" @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) def test_retrieve_content_around_line(self, language_server: SyncLanguageServer): diff --git a/test/multilspy/python/test_symbol_retrieval.py b/test/multilspy/python/test_symbol_retrieval.py index cc4b35a2..700f7e6a 100644 --- a/test/multilspy/python/test_symbol_retrieval.py +++ b/test/multilspy/python/test_symbol_retrieval.py @@ -104,10 +104,13 @@ class TestLanguageServerSymbols: # Test referencing symbols for create_user function file_path = os.path.join("test_repo", "services.py") # Line 15 contains the create_user function definition - ref_symbols = language_server.request_referencing_symbols(file_path, 15, 9) - - # Verify we get referencing symbols - assert len(ref_symbols) > 0 + symbols = language_server.request_document_symbols(file_path) + create_user_symbol = next((s for s in symbols[0] if s.get("name") == "create_user"), None) + if not create_user_symbol or "selectionRange" not in create_user_symbol: + raise AssertionError("create_user symbol or its selectionRange not found") + sel_start = create_user_symbol["selectionRange"]["start"] + ref_symbols = language_server.request_referencing_symbols(file_path, sel_start["line"], sel_start["character"]) + assert len(ref_symbols) > 0, "No referencing symbols found for create_user (selectionRange)" # Verify the structure of referencing symbols for symbol in ref_symbols: @@ -123,19 +126,18 @@ class TestLanguageServerSymbols: # Test referencing symbols for User class file_path = os.path.join("test_repo", "models.py") # Line 31 contains the User class definition - ref_symbols = language_server.request_referencing_symbols(file_path, 31, 6) - - # Verify we get referencing symbols - assert len(ref_symbols) > 0 - - # At least one reference should be from services.py + symbols = language_server.request_document_symbols(file_path) + user_symbol = next((s for s in symbols[0] if s.get("name") == "User"), None) + if not user_symbol or "selectionRange" not in user_symbol: + raise AssertionError("User symbol or its selectionRange not found") + sel_start = user_symbol["selectionRange"]["start"] + ref_symbols = language_server.request_referencing_symbols(file_path, sel_start["line"], sel_start["character"]) services_references = [ symbol for symbol in ref_symbols if "location" in symbol and "uri" in symbol["location"] and "services.py" in symbol["location"]["uri"] ] - - assert len(services_references) > 0 + assert len(services_references) > 0, "No referencing symbols from services.py for User (selectionRange)" @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) def test_request_referencing_symbols_parameter(self, language_server: SyncLanguageServer): @@ -143,19 +145,20 @@ class TestLanguageServerSymbols: # Test referencing symbols for id parameter in get_user file_path = os.path.join("test_repo", "services.py") # Line 24 contains the get_user method with id parameter - ref_symbols = language_server.request_referencing_symbols(file_path, 24, 16) - - # Verify we get referencing symbols - assert len(ref_symbols) > 0 - - # Verify the symbols include references within the method body + symbols = language_server.request_document_symbols(file_path) + get_user_symbol = next((s for s in symbols[0] if s.get("name") == "get_user"), None) + if not get_user_symbol or "selectionRange" not in get_user_symbol: + raise AssertionError("get_user symbol or its selectionRange not found") + sel_start = get_user_symbol["selectionRange"]["start"] + ref_symbols = language_server.request_referencing_symbols(file_path, sel_start["line"], sel_start["character"]) method_refs = [ symbol for symbol in ref_symbols - if "location" in symbol and "range" in symbol["location"] and symbol["location"]["range"]["start"]["line"] > 24 + if "location" in symbol + and "range" in symbol["location"] + and symbol["location"]["range"]["start"]["line"] > sel_start["line"] ] - - assert len(method_refs) > 0 + assert len(method_refs) > 0, "No referencing symbols within method body for get_user (selectionRange)" @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) def test_request_referencing_symbols_none(self, language_server: SyncLanguageServer): diff --git a/test/multilspy/rust/test_rust_basic.py b/test/multilspy/rust/test_rust_basic.py new file mode 100644 index 00000000..8b1d1a57 --- /dev/null +++ b/test/multilspy/rust/test_rust_basic.py @@ -0,0 +1,91 @@ +import os + +import pytest + +from multilspy.multilspy_config import Language + + +def find_symbol_recursive(symbols, name): + for symbol in symbols: + if symbol.get("name") == name: + return True + if symbol.get("children"): + if find_symbol_recursive(symbol["children"], name): + return True + return False + + +class TestRustLanguageServer: + @pytest.mark.parametrize("language_server", [Language.RUST], indirect=True) + def test_find_references_raw(self, language_server): + # Directly test the request_references method for the add function + file_path = os.path.join("src", "lib.rs") + symbols = language_server.request_document_symbols(file_path) + add_symbol = None + for sym in symbols[0]: + if sym.get("name") == "add": + add_symbol = sym + break + assert add_symbol is not None, "Could not find 'add' function symbol in lib.rs" + sel_start = add_symbol["selectionRange"]["start"] + sel_end = add_symbol["selectionRange"]["end"] + found = False + if sel_start["line"] == sel_end["line"]: + for col in range(sel_start["character"], sel_end["character"] + 1): + refs = language_server.request_references(file_path, sel_start["line"], col) + # f"Raw request_references for add in lib.rs at col {col}: {refs}") + if any("main.rs" in ref.get("relativePath", "") for ref in refs): + found = True + else: + refs_start = language_server.request_references(file_path, sel_start["line"], sel_start["character"]) + refs_end = language_server.request_references(file_path, sel_end["line"], sel_end["character"]) + # f"Raw request_references for add in lib.rs at selectionRange start: {refs_start}") + # f"Raw request_references for add in lib.rs at selectionRange end: {refs_end}") + if any("main.rs" in ref.get("relativePath", "") for ref in refs_start + refs_end): + found = True + assert found, "main.rs should reference add (raw, tried all positions in selectionRange)" + + @pytest.mark.parametrize("language_server", [Language.RUST], indirect=True) + def test_find_symbol(self, language_server): + symbols = language_server.request_full_symbol_tree() + assert find_symbol_recursive(symbols, "main"), "main function not found in symbol tree" + assert find_symbol_recursive(symbols, "add"), "add function not found in symbol tree" + # Add more as needed based on test_repo + + @pytest.mark.parametrize("language_server", [Language.RUST], indirect=True) + def test_find_referencing_symbols(self, language_server): + # Find references to 'add' defined in lib.rs, should be referenced from main.rs + file_path = os.path.join("src", "lib.rs") + symbols = language_server.request_document_symbols(file_path) + add_symbol = None + for sym in symbols[0]: + if sym.get("name") == "add": + add_symbol = sym + break + assert add_symbol is not None, "Could not find 'add' function symbol in lib.rs" + sel_start = add_symbol["selectionRange"]["start"] + sel_end = add_symbol["selectionRange"]["end"] + # f"add symbol selectionRange: start={sel_start}, end={sel_end}") + found = False + # Try all character positions in the selectionRange (same line, from start to end character) + if sel_start["line"] == sel_end["line"]: + for col in range(sel_start["character"], sel_end["character"] + 1): + refs = language_server.request_references(file_path, sel_start["line"], col) + # f"References for add in lib.rs at col {col} (selectionRange): {refs}") + if any("main.rs" in ref.get("relativePath", "") for ref in refs): + found = True + else: + # If the function name spans multiple lines, try start and end + refs_start = language_server.request_references(file_path, sel_start["line"], sel_start["character"]) + refs_end = language_server.request_references(file_path, sel_end["line"], sel_end["character"]) + # f"References for add in lib.rs at selectionRange start: {refs_start}") + # f"References for add in lib.rs at selectionRange end: {refs_end}") + if any("main.rs" in ref.get("relativePath", "") for ref in refs_start + refs_end): + found = True + assert found, "main.rs should reference add (tried all positions in selectionRange)" + + @pytest.mark.parametrize("language_server", [Language.RUST], indirect=True) + def test_overview_methods(self, language_server): + symbols = language_server.request_full_symbol_tree() + assert find_symbol_recursive(symbols, "main"), "main missing from overview" + assert find_symbol_recursive(symbols, "add"), "add missing from overview" diff --git a/test/resources/repos/rust/test_repo/Cargo.lock b/test/resources/repos/rust/test_repo/Cargo.lock new file mode 100644 index 00000000..ebc459f1 --- /dev/null +++ b/test/resources/repos/rust/test_repo/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rsandbox" +version = "0.1.0" diff --git a/test/resources/repos/rust/test_repo/Cargo.toml b/test/resources/repos/rust/test_repo/Cargo.toml new file mode 100644 index 00000000..43ffbf6e --- /dev/null +++ b/test/resources/repos/rust/test_repo/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rsandbox" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/test/resources/repos/rust/test_repo/src/lib.rs b/test/resources/repos/rust/test_repo/src/lib.rs new file mode 100644 index 00000000..8ad43ead --- /dev/null +++ b/test/resources/repos/rust/test_repo/src/lib.rs @@ -0,0 +1,9 @@ +// This function returns the sum of 2 + 2 +pub fn add() -> i32 { + let res = 2 + 2; + res +} +pub fn multiply() -> i32 { + 2 * 3 +} + diff --git a/test/resources/repos/rust/test_repo/src/main.rs b/test/resources/repos/rust/test_repo/src/main.rs new file mode 100644 index 00000000..7385c9d9 --- /dev/null +++ b/test/resources/repos/rust/test_repo/src/main.rs @@ -0,0 +1,8 @@ +use rsandbox::add; + +fn main() { + println!("Hello, World!"); + println!("Good morning!"); + println!("add result: {}", add()); + println!("inserted line"); +}