mirror of
https://github.com/tiennm99/serena.git
synced 2026-07-15 04:32:36 +00:00
Added tests for PhP language server
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
import time # Import time module
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from multilspy.language_server import SyncLanguageServer
|
||||
from multilspy.multilspy_config import Language
|
||||
|
||||
|
||||
@pytest.mark.php
|
||||
class TestPhpLanguageServer:
|
||||
@pytest.mark.parametrize("language_server", [Language.PHP], indirect=True)
|
||||
@pytest.mark.parametrize("repo_path", [Language.PHP], indirect=True)
|
||||
def test_initialize_and_shutdown(self, language_server: SyncLanguageServer, 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()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("language_server", [Language.PHP], indirect=True)
|
||||
@pytest.mark.parametrize("repo_path", [Language.PHP], indirect=True)
|
||||
def test_find_definition_within_file(self, language_server: SyncLanguageServer, repo_path: Path) -> None:
|
||||
|
||||
# In index.php:
|
||||
# Line 9 (1-indexed): $greeting = greet($userName);
|
||||
# Line 11 (1-indexed): echo $greeting;
|
||||
# We want to find the definition of $greeting (defined on line 9)
|
||||
# from its usage in echo $greeting; on line 11.
|
||||
# LSP is 0-indexed: definition on line 8, usage on line 10.
|
||||
# $greeting in echo $greeting; is at char 5 on line 11 (0-indexed: line 10, char 5)
|
||||
# e c h o $ g r e e t i n g
|
||||
# ^ char 5
|
||||
definition_location_list = language_server.request_definition(str(repo_path / "index.php"), 10, 6) # cursor on 'g' in $greeting
|
||||
|
||||
assert definition_location_list is not None
|
||||
assert len(definition_location_list) == 1
|
||||
definition_location = definition_location_list[0]
|
||||
assert definition_location["uri"].endswith("index.php")
|
||||
# Definition of $greeting is on line 10 (1-indexed) / line 9 (0-indexed), char 0
|
||||
assert definition_location["range"]["start"]["line"] == 9
|
||||
assert definition_location["range"]["start"]["character"] == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize("language_server", [Language.PHP], indirect=True)
|
||||
@pytest.mark.parametrize("repo_path", [Language.PHP], indirect=True)
|
||||
def test_find_definition_across_files(self, language_server: SyncLanguageServer, repo_path: Path) -> None:
|
||||
definition_location_list = language_server.request_definition(str(repo_path / "index.php"), 12, 5) # helperFunction
|
||||
|
||||
assert definition_location_list is not None
|
||||
assert len(definition_location_list) == 1
|
||||
definition_location = definition_location_list[0]
|
||||
assert definition_location["uri"].endswith("helper.php")
|
||||
assert definition_location["range"]["start"]["line"] == 2
|
||||
assert definition_location["range"]["start"]["character"] == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize("language_server", [Language.PHP], indirect=True)
|
||||
@pytest.mark.parametrize("repo_path", [Language.PHP], indirect=True)
|
||||
def test_find_definition_simple_variable(self, language_server: SyncLanguageServer, repo_path: Path) -> None:
|
||||
file_path = str(repo_path / "simple_var.php")
|
||||
|
||||
# In simple_var.php:
|
||||
# Line 2 (1-indexed): $localVar = "test";
|
||||
# Line 3 (1-indexed): echo $localVar;
|
||||
# LSP is 0-indexed: definition on line 1, usage on line 2
|
||||
# Find definition of $localVar (char 5 on line 3 / 0-indexed: line 2, char 5)
|
||||
# $localVar in echo $localVar; (e c h o $ l o c a l V a r)
|
||||
# ^ char 5
|
||||
definition_location_list = language_server.request_definition(file_path, 2, 6) # cursor on 'l' in $localVar
|
||||
|
||||
assert definition_location_list is not None
|
||||
assert len(definition_location_list) == 1
|
||||
definition_location = definition_location_list[0]
|
||||
assert definition_location["uri"].endswith("simple_var.php")
|
||||
assert definition_location["range"]["start"]["line"] == 1 # Definition of $localVar (0-indexed)
|
||||
assert definition_location["range"]["start"]["character"] == 0 # $localVar (0-indexed)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("language_server", [Language.PHP], indirect=True)
|
||||
@pytest.mark.parametrize("repo_path", [Language.PHP], indirect=True)
|
||||
def test_find_references_within_file(self, language_server: SyncLanguageServer, repo_path: Path) -> None:
|
||||
index_php_path = str(repo_path / "index.php")
|
||||
|
||||
# In index.php (0-indexed lines):
|
||||
# Line 9: $greeting = greet($userName); // Definition of $greeting
|
||||
# Line 11: echo $greeting; // Usage of $greeting
|
||||
# Find references for $greeting from its usage in "echo $greeting;" (line 11, char 6 for 'g')
|
||||
references = language_server.request_references(index_php_path, 11, 6)
|
||||
|
||||
assert references is not None
|
||||
# Intelephense, when asked for references from usage, seems to only return the usage itself.
|
||||
assert len(references) == 1, "Expected to find 1 reference for $greeting (the usage itself)"
|
||||
|
||||
expected_locations = [{"uri_suffix": "index.php", "line": 11, "character": 5}] # Usage: echo $greeting (points to $)
|
||||
|
||||
# Convert actual references to a comparable format and sort
|
||||
actual_locations = sorted(
|
||||
[
|
||||
{
|
||||
"uri_suffix": loc["uri"].split("/")[-1],
|
||||
"line": loc["range"]["start"]["line"],
|
||||
"character": loc["range"]["start"]["character"],
|
||||
}
|
||||
for loc in references
|
||||
],
|
||||
key=lambda x: (x["uri_suffix"], x["line"], x["character"]),
|
||||
)
|
||||
|
||||
expected_locations = sorted(expected_locations, key=lambda x: (x["uri_suffix"], x["line"], x["character"]))
|
||||
|
||||
assert actual_locations == expected_locations
|
||||
|
||||
|
||||
@pytest.mark.parametrize("language_server", [Language.PHP], indirect=True)
|
||||
@pytest.mark.parametrize("repo_path", [Language.PHP], indirect=True)
|
||||
def test_find_references_across_files(self, language_server: SyncLanguageServer, repo_path: Path) -> None:
|
||||
index_php_path = str(repo_path / "index.php")
|
||||
# In index.php (0-indexed lines):
|
||||
# Line 13: helperFunction(); // Usage of helperFunction
|
||||
# Find references for helperFunction from its usage (line 13, char 0 for 'h')
|
||||
references = language_server.request_references(index_php_path, 13, 0)
|
||||
|
||||
assert references is not None
|
||||
# Intelephense might return 1 (usage) or 2 (usage + definition) references.
|
||||
# Let's check for at least the usage in index.php
|
||||
# Definition is in helper.php, line 2, char 0 (based on previous findings)
|
||||
# Usage is in index.php, line 13, char 0
|
||||
|
||||
actual_locations_comparable = []
|
||||
for loc in references:
|
||||
actual_locations_comparable.append(
|
||||
{
|
||||
"uri_suffix": loc["uri"].split("/")[-1],
|
||||
"line": loc["range"]["start"]["line"],
|
||||
"character": loc["range"]["start"]["character"],
|
||||
}
|
||||
)
|
||||
|
||||
usage_in_index_php = {"uri_suffix": "index.php", "line": 13, "character": 0}
|
||||
definition_in_helper_php = {"uri_suffix": "helper.php", "line": 2, "character": 0}
|
||||
|
||||
assert usage_in_index_php in actual_locations_comparable, "Usage of helperFunction in index.php not found"
|
||||
|
||||
# Depending on Intelephense's behavior, it might also include the definition.
|
||||
# For now, we are flexible: it must find the usage. If it finds more, that's also okay.
|
||||
# If we want to be strict about finding both or only one, this part would need adjustment.
|
||||
if len(references) == 2:
|
||||
assert (
|
||||
definition_in_helper_php in actual_locations_comparable
|
||||
), "Definition of helperFunction in helper.php expected but not found when 2 references returned"
|
||||
elif len(references) == 1:
|
||||
# If only one reference, ensure it's the usage we definitely expect
|
||||
assert actual_locations_comparable[0] == usage_in_index_php
|
||||
else:
|
||||
assert False, f"Expected 1 or 2 references, but got {len(references)}"
|
||||
Reference in New Issue
Block a user