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
112 lines
5.5 KiB
Python
112 lines
5.5 KiB
Python
"""
|
|
Basic tests for R Language Server integration
|
|
"""
|
|
|
|
import os
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from solidlsp import SolidLanguageServer
|
|
from solidlsp.ls_config import Language
|
|
from test.conftest import language_tests_enabled
|
|
from test.solidlsp.conftest import format_symbol_for_assert, has_malformed_name, request_all_symbols
|
|
|
|
|
|
@pytest.mark.skipif(not language_tests_enabled(Language.R), reason="R tests are disabled (R not available)")
|
|
@pytest.mark.r
|
|
class TestRLanguageServer:
|
|
"""Test basic functionality of the R language server."""
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.R], indirect=True)
|
|
@pytest.mark.parametrize("repo_path", [Language.R], indirect=True)
|
|
def test_server_initialization(self, language_server: SolidLanguageServer, repo_path: Path):
|
|
"""Test that the R language server initializes properly."""
|
|
assert language_server is not None
|
|
assert language_server.language_id == "r"
|
|
assert language_server.is_running()
|
|
assert Path(language_server.language_server.repository_root_path).resolve() == repo_path.resolve()
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.R], indirect=True)
|
|
def test_symbol_retrieval(self, language_server: SolidLanguageServer):
|
|
"""Test R document symbol extraction."""
|
|
all_symbols, _root_symbols = language_server.request_document_symbols(os.path.join("R", "utils.R")).get_all_symbols_and_roots()
|
|
|
|
# Should find the three exported functions
|
|
function_symbols = [s for s in all_symbols if s.get("kind") == 12] # Function kind
|
|
assert len(function_symbols) >= 3
|
|
|
|
# Check that we found the expected functions
|
|
function_names = {s.get("name") for s in function_symbols}
|
|
expected_functions = {"calculate_mean", "process_data", "create_data_frame"}
|
|
assert expected_functions.issubset(function_names), f"Expected functions {expected_functions} but found {function_names}"
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.R], indirect=True)
|
|
def test_find_definition_across_files(self, language_server: SolidLanguageServer):
|
|
"""Test finding function definitions across files."""
|
|
analysis_file = os.path.join("examples", "analysis.R")
|
|
|
|
# In analysis.R line 7: create_data_frame(n = 50)
|
|
# The function create_data_frame is defined in R/utils.R
|
|
# Find definition of create_data_frame function call (0-indexed: line 6)
|
|
definition_location_list = language_server.request_definition(analysis_file, 6, 17) # cursor on 'create_data_frame'
|
|
|
|
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("utils.R")
|
|
# Definition should be around line 37 (0-indexed: 36) where create_data_frame is defined
|
|
assert definition_location["range"]["start"]["line"] >= 35
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.R], indirect=True)
|
|
def test_find_references_across_files(self, language_server: SolidLanguageServer):
|
|
"""Test finding function references across files."""
|
|
analysis_file = os.path.join("examples", "analysis.R")
|
|
|
|
# Test from usage side: find references to calculate_mean from its usage in analysis.R
|
|
# In analysis.R line 13: calculate_mean(clean_data$value)
|
|
# calculate_mean function call is at line 13 (0-indexed: line 12)
|
|
references = language_server.request_references(analysis_file, 12, 15) # cursor on 'calculate_mean'
|
|
|
|
assert references, f"Expected non-empty references for calculate_mean but got {references=}"
|
|
|
|
# Must find the definition in utils.R (cross-file reference)
|
|
reference_files = [ref["uri"] for ref in references]
|
|
assert any(uri.endswith("utils.R") for uri in reference_files), "Cross-file reference to definition in utils.R not found"
|
|
|
|
# Verify we actually found the right location in utils.R
|
|
utils_refs = [ref for ref in references if ref["uri"].endswith("utils.R")]
|
|
assert len(utils_refs) >= 1, "Should find at least one reference in utils.R"
|
|
utils_ref = utils_refs[0]
|
|
# Should be around line 6 where calculate_mean is defined (0-indexed: line 5)
|
|
assert utils_ref["range"]["start"]["line"] == 5, (
|
|
f"Expected reference at line 5 in utils.R, got line {utils_ref['range']['start']['line']}"
|
|
)
|
|
|
|
def test_file_matching(self):
|
|
"""Test that R files are properly matched."""
|
|
matcher = Language.R.get_source_fn_matcher()
|
|
|
|
assert matcher.is_relevant_filename("script.R")
|
|
assert matcher.is_relevant_filename("analysis.r")
|
|
assert not matcher.is_relevant_filename("script.py")
|
|
assert not matcher.is_relevant_filename("README.md")
|
|
|
|
def test_r_language_enum(self):
|
|
"""Test R language enum value."""
|
|
assert Language.R == "r"
|
|
assert str(Language.R) == "r"
|
|
|
|
@pytest.mark.parametrize("language_server", [Language.R], 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 has_malformed_name(s):
|
|
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,
|
|
)
|