mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-15 04:18:56 +00:00
* Add SystemVerilog language server support using slang-langserver - Add Language.SYSTEMVERILOG enum with .sv, .svh, .v, .vh file support - Create SystemVerilogLanguageServer class with auto-download for Linux, macOS, Windows - Add pytest marker and basic test cases for symbol retrieval Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Fix ruff lint errors by updating configuration - Add UP042, PLW0108, PLC0207 to ruff ignore list (style preferences) - Update test repo pyproject.toml to use lint.select and lint.isort sections Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Replace slang-langserver with verible-verilog-ls for SystemVerilog slang v7.0 release URLs return 404 as the project stopped providing pre-built binaries after v3.0. Migrate to Verible (CHIPS Alliance) which actively maintains binary releases for all major platforms. Changes: - Update RuntimeDependency URLs to Verible v0.0-4051-g9fdb4057 - Add Linux ARM64 platform support - Update fallback and error messages Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add SystemVerilog language server support using slang-langserver - Add Language.SYSTEMVERILOG enum with .sv, .svh, .v, .vh file support - Create SystemVerilogLanguageServer class with auto-download for Linux, macOS, Windows - Add pytest marker and basic test cases for symbol retrieval Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Replace slang-langserver with verible-verilog-ls for SystemVerilog slang v7.0 release URLs return 404 as the project stopped providing pre-built binaries after v3.0. Migrate to Verible (CHIPS Alliance) which actively maintains binary releases for all major platforms. Changes: - Update RuntimeDependency URLs to Verible v0.0-4051-g9fdb4057 - Add Linux ARM64 platform support - Update fallback and error messages Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Fix Verible binary paths for macOS and Windows The archive directory names differ per platform: - Linux: verible-v0.0-4051-g9fdb4057/bin/ - macOS: verible-v0.0-4051-g9fdb4057-macOS/bin/ - Windows: verible-v0.0-4051-g9fdb4057-win64/ (no bin/ subdirectory) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Update docs and comments: slang-langserver → verible-verilog-ls - Update pyproject.toml marker description - Update ls_config.py docstring - Update test file docstrings - Remove unused _slang_available() function Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add verible-verilog-ls to CI and improve SystemVerilog LS - Add verible-verilog-ls installation step to pytest.yml for Linux/macOS/Windows (GitHub Releases, follows ZLS pattern) - Remove unused threading import and server_ready Event - Add capability validation (assert textDocumentSync, warn on missing documentSymbolProvider/definitionProvider) - Add window/logMessage notification handler - Add codeAction, formatting, documentHighlight capabilities - Improve error message with conda/Homebrew/GitHub install methods Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add verible detection tests and remove redundant ALU tests Add comprehensive unit tests for verible-verilog-ls binary detection logic (PATH lookup, version logging, graceful degradation, download fallback). Remove duplicate ALU symbol tests that tested the same LSP operations already covered by counter tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix SystemVerilog LSP convention deviations from code review - Add client/registerCapability handler (all other 31 LSPs have it) - Add locale: en to initialize params (project convention) - Change log.debug to log.info for window/logMessage handler - Fix dead code in test_find_references_cross_file skip/assert logic - Move repeated imports to module top level in detection tests - Remove unused IS_WINDOWS/IS_UNIX variables and sys import - Fix hardcoded line number comment to semantic description Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix Windows CI verible path: flat zip structure has no bin/ subdir The Windows verible zip extracts to verible-VERSION-win64/ with executables directly in the root (no bin/ subdirectory), unlike Linux/macOS tar.gz which have bin/ subdirectory. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix macOS CI verible path: tar.gz extracts with -macOS suffix Each platform has different directory naming in verible releases: - Linux: verible-VERSION/bin/ (no suffix) - macOS: verible-VERSION-macOS/bin/ (has -macOS suffix) - Windows: verible-VERSION-win64/ (flat, no bin/ subdir) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Refactor: read Verible version from settings instead of hardcoding Extract hardcoded version string into _custom_settings.get("verible_version") with the current version as default. This allows version override via project.yml without changing source code. Also introduce DEFAULT_VERIBLE_VERSION constant in detection tests for consistency. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add rename tests and strengthen existing SystemVerilog tests - Add TestSystemVerilogRename: within-file, cross-file, module rename - Strengthen all existing tests with exact position/value verification - Remove all pytest.skip, replace with assert (review feedback) - Remove isinstance check in _find_symbol_by_name (review feedback) - Extract _extract_hover_text and _extract_changes helpers - Remove test_diagnostics (requires shared ls.py fix, separate PR) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
333 lines
16 KiB
Python
333 lines
16 KiB
Python
"""
|
|
Tests for verible-verilog-ls detection logic.
|
|
|
|
These tests describe the expected behavior of SystemVerilogLanguageServer.DependencyProvider._get_or_install_core_dependency():
|
|
|
|
1. System PATH should be checked FIRST (prefers user-installed verible)
|
|
2. Runtime download should be fallback when not in PATH
|
|
3. Version information should be logged when available
|
|
4. Version check failures should be handled gracefully
|
|
5. Helpful error messages when verible is not available on unsupported platforms
|
|
|
|
WHY these tests matter:
|
|
- Users install verible via conda, Homebrew, system packages, or GitHub releases
|
|
- Detection failing means Serena is unusable for SystemVerilog, even when verible is correctly installed
|
|
- Without these tests, the detection logic can silently break for users with system installations
|
|
- Version logging helps debug compatibility issues
|
|
"""
|
|
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
from unittest.mock import MagicMock, Mock, patch
|
|
|
|
import pytest
|
|
|
|
from solidlsp.language_servers.systemverilog_server import SystemVerilogLanguageServer
|
|
from solidlsp.settings import SolidLSPSettings
|
|
|
|
DEFAULT_VERIBLE_VERSION = "v0.0-4051-g9fdb4057"
|
|
|
|
|
|
class TestVeribleVerilogLsDetection:
|
|
"""Unit tests for verible-verilog-ls binary detection logic."""
|
|
|
|
@pytest.mark.systemverilog
|
|
def test_detect_from_path_returns_system_verible(self):
|
|
"""
|
|
GIVEN verible-verilog-ls is in system PATH
|
|
WHEN _get_or_install_core_dependency is called
|
|
THEN it returns the system path without downloading
|
|
|
|
WHY: Users with system-installed verible (via conda, Homebrew, apt)
|
|
should use that version instead of downloading. This is faster and
|
|
respects user's environment management.
|
|
"""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
custom_settings = SolidLSPSettings.CustomLSSettings({})
|
|
provider = SystemVerilogLanguageServer.DependencyProvider(custom_settings, temp_dir)
|
|
|
|
with patch("shutil.which") as mock_which:
|
|
mock_which.return_value = "/usr/local/bin/verible-verilog-ls"
|
|
with patch("subprocess.run") as mock_run:
|
|
mock_run.return_value = MagicMock(
|
|
returncode=0,
|
|
stdout="Verible v0.0-4051-g9fdb4057 (2024-01-01)\nCommit: 9fdb4057",
|
|
stderr="",
|
|
)
|
|
result = provider._get_or_install_core_dependency()
|
|
|
|
assert result == "/usr/local/bin/verible-verilog-ls"
|
|
mock_which.assert_called_once_with("verible-verilog-ls")
|
|
mock_run.assert_called_once()
|
|
assert mock_run.call_args[0][0] == ["/usr/local/bin/verible-verilog-ls", "--version"]
|
|
|
|
@pytest.mark.systemverilog
|
|
def test_detect_from_path_logs_version(self):
|
|
"""
|
|
GIVEN verible-verilog-ls is in PATH with version output
|
|
WHEN detected
|
|
THEN version info is logged
|
|
|
|
WHY: Version information helps debug compatibility issues.
|
|
Users and developers need to know which verible version is being used.
|
|
"""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
custom_settings = SolidLSPSettings.CustomLSSettings({})
|
|
provider = SystemVerilogLanguageServer.DependencyProvider(custom_settings, temp_dir)
|
|
|
|
with patch("shutil.which", return_value="/usr/bin/verible-verilog-ls"):
|
|
with patch("subprocess.run") as mock_run:
|
|
mock_run.return_value = MagicMock(returncode=0, stdout="Verible v0.0-4051-g9fdb4057", stderr="")
|
|
with patch("solidlsp.language_servers.systemverilog_server.log") as mock_log:
|
|
result = provider._get_or_install_core_dependency()
|
|
|
|
# Verify version check was called
|
|
assert mock_run.call_args[0][0] == ["/usr/bin/verible-verilog-ls", "--version"]
|
|
# Verify version was logged
|
|
assert mock_log.info.called
|
|
log_message = mock_log.info.call_args[0][0]
|
|
assert "Verible v0.0-4051" in log_message
|
|
assert result == "/usr/bin/verible-verilog-ls"
|
|
|
|
@pytest.mark.systemverilog
|
|
def test_detect_from_path_handles_version_failure_gracefully(self):
|
|
"""
|
|
GIVEN verible-verilog-ls is in PATH but --version fails (returncode=1)
|
|
WHEN detected
|
|
THEN it still returns the system path (graceful degradation)
|
|
|
|
WHY: Some verible builds might not support --version or have different flags.
|
|
Detection should not fail just because version check fails - the binary
|
|
might still work fine for LSP operations.
|
|
"""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
custom_settings = SolidLSPSettings.CustomLSSettings({})
|
|
provider = SystemVerilogLanguageServer.DependencyProvider(custom_settings, temp_dir)
|
|
|
|
with patch("shutil.which", return_value="/custom/bin/verible-verilog-ls"):
|
|
with patch("subprocess.run") as mock_run:
|
|
# Version check fails
|
|
mock_run.return_value = MagicMock(returncode=1, stdout="", stderr="Unknown option: --version")
|
|
result = provider._get_or_install_core_dependency()
|
|
|
|
# Should still return the path despite version check failure
|
|
assert result == "/custom/bin/verible-verilog-ls"
|
|
|
|
@pytest.mark.systemverilog
|
|
def test_detect_from_path_handles_version_timeout_gracefully(self):
|
|
"""
|
|
GIVEN verible-verilog-ls is in PATH but --version times out
|
|
WHEN detected
|
|
THEN it still returns the system path (graceful degradation)
|
|
|
|
WHY: Version check has a timeout to avoid hanging. If it times out,
|
|
we should still use the detected binary.
|
|
"""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
custom_settings = SolidLSPSettings.CustomLSSettings({})
|
|
provider = SystemVerilogLanguageServer.DependencyProvider(custom_settings, temp_dir)
|
|
|
|
with patch("shutil.which", return_value="/opt/verible/bin/verible-verilog-ls"):
|
|
with patch("subprocess.run") as mock_run:
|
|
# Version check times out
|
|
mock_run.side_effect = subprocess.TimeoutExpired(cmd=["verible-verilog-ls", "--version"], timeout=5)
|
|
result = provider._get_or_install_core_dependency()
|
|
|
|
# Should still return the path despite timeout
|
|
assert result == "/opt/verible/bin/verible-verilog-ls"
|
|
|
|
@pytest.mark.systemverilog
|
|
def test_error_message_when_not_found_anywhere(self):
|
|
"""
|
|
GIVEN verible is NOT in PATH AND platform is unsupported
|
|
WHEN _get_or_install_core_dependency is called
|
|
THEN raises FileNotFoundError with helpful installation instructions
|
|
|
|
WHY: Users need clear guidance on how to install verible when it's missing.
|
|
Error message should mention conda, Homebrew, and GitHub releases.
|
|
"""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
custom_settings = SolidLSPSettings.CustomLSSettings({})
|
|
provider = SystemVerilogLanguageServer.DependencyProvider(custom_settings, temp_dir)
|
|
|
|
with patch("shutil.which", return_value=None):
|
|
# Mock RuntimeDependencyCollection to raise RuntimeError for unsupported platform
|
|
with patch("solidlsp.language_servers.systemverilog_server.RuntimeDependencyCollection") as mock_deps_class:
|
|
mock_deps = Mock()
|
|
mock_deps.get_single_dep_for_current_platform.side_effect = RuntimeError("Unsupported platform")
|
|
mock_deps_class.return_value = mock_deps
|
|
|
|
with pytest.raises(FileNotFoundError) as exc_info:
|
|
provider._get_or_install_core_dependency()
|
|
|
|
error_message = str(exc_info.value)
|
|
# Error should mention installation methods
|
|
assert "conda" in error_message.lower()
|
|
assert "Homebrew" in error_message or "brew" in error_message.lower()
|
|
assert "GitHub" in error_message or "github.com" in error_message.lower()
|
|
assert "verible" in error_message.lower()
|
|
|
|
@pytest.mark.systemverilog
|
|
def test_downloads_when_not_in_path(self):
|
|
"""
|
|
GIVEN verible is NOT in PATH AND platform IS supported AND binary exists after download
|
|
WHEN _get_or_install_core_dependency is called
|
|
THEN returns the downloaded executable path
|
|
|
|
WHY: When verible is not installed system-wide and platform is supported,
|
|
Serena should auto-download it. This enables zero-setup experience.
|
|
"""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
custom_settings = SolidLSPSettings.CustomLSSettings({})
|
|
provider = SystemVerilogLanguageServer.DependencyProvider(custom_settings, temp_dir)
|
|
|
|
expected_path = os.path.join(temp_dir, "verible-ls", f"verible-{DEFAULT_VERIBLE_VERSION}", "bin", "verible-verilog-ls")
|
|
|
|
with patch("shutil.which", return_value=None):
|
|
with patch("solidlsp.language_servers.systemverilog_server.RuntimeDependencyCollection") as mock_deps_class:
|
|
# Create mock dependency and collection
|
|
mock_dep = Mock()
|
|
mock_dep.url = "https://github.com/chipsalliance/verible/releases/download/v0.0-4051/verible.tar.gz"
|
|
|
|
mock_deps = Mock()
|
|
mock_deps.get_single_dep_for_current_platform.return_value = mock_dep
|
|
mock_deps.binary_path.return_value = expected_path
|
|
mock_deps.install.return_value = expected_path
|
|
|
|
mock_deps_class.return_value = mock_deps
|
|
|
|
with patch("os.path.exists") as mock_exists:
|
|
# Before download: binary doesn't exist yet → after download: binary exists
|
|
mock_exists.side_effect = [False, True]
|
|
|
|
with patch("os.chmod"):
|
|
result = provider._get_or_install_core_dependency()
|
|
|
|
assert result == expected_path
|
|
mock_deps.install.assert_called_once()
|
|
|
|
@pytest.mark.systemverilog
|
|
def test_detection_prefers_path_over_download(self):
|
|
"""
|
|
GIVEN verible is in PATH AND download would also work
|
|
WHEN _get_or_install_core_dependency is called
|
|
THEN PATH version is used (download never attempted)
|
|
|
|
WHY: System-installed verible should always take precedence.
|
|
This respects user's environment and avoids unnecessary downloads.
|
|
"""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
custom_settings = SolidLSPSettings.CustomLSSettings({})
|
|
provider = SystemVerilogLanguageServer.DependencyProvider(custom_settings, temp_dir)
|
|
|
|
with patch("shutil.which", return_value="/usr/bin/verible-verilog-ls"):
|
|
with patch("subprocess.run") as mock_run:
|
|
mock_run.return_value = MagicMock(returncode=0, stdout="Verible v0.0-4051", stderr="")
|
|
|
|
with patch("solidlsp.language_servers.systemverilog_server.RuntimeDependencyCollection") as mock_deps_class:
|
|
result = provider._get_or_install_core_dependency()
|
|
|
|
# RuntimeDependencyCollection should never be instantiated
|
|
mock_deps_class.assert_not_called()
|
|
|
|
assert result == "/usr/bin/verible-verilog-ls"
|
|
|
|
@pytest.mark.systemverilog
|
|
def test_download_fails_if_binary_not_found_after_install(self):
|
|
"""
|
|
GIVEN verible is NOT in PATH AND platform IS supported
|
|
WHEN download completes BUT binary still doesn't exist at expected path
|
|
THEN raises FileNotFoundError
|
|
|
|
WHY: If download/extraction fails silently, we should catch it and report clearly.
|
|
"""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
custom_settings = SolidLSPSettings.CustomLSSettings({})
|
|
provider = SystemVerilogLanguageServer.DependencyProvider(custom_settings, temp_dir)
|
|
|
|
expected_path = os.path.join(temp_dir, "verible-ls", f"verible-{DEFAULT_VERIBLE_VERSION}", "bin", "verible-verilog-ls")
|
|
|
|
with patch("shutil.which", return_value=None):
|
|
with patch("solidlsp.language_servers.systemverilog_server.RuntimeDependencyCollection") as mock_deps_class:
|
|
mock_dep = Mock()
|
|
mock_deps = Mock()
|
|
mock_deps.get_single_dep_for_current_platform.return_value = mock_dep
|
|
mock_deps.binary_path.return_value = expected_path
|
|
mock_deps.install.return_value = expected_path
|
|
mock_deps_class.return_value = mock_deps
|
|
|
|
# Binary never appears after install
|
|
with patch("os.path.exists", return_value=False):
|
|
with pytest.raises(FileNotFoundError) as exc_info:
|
|
provider._get_or_install_core_dependency()
|
|
|
|
error_message = str(exc_info.value)
|
|
assert "verible-verilog-ls not found" in error_message
|
|
assert expected_path in error_message
|
|
|
|
@pytest.mark.systemverilog
|
|
def test_uses_already_downloaded_binary_without_reinstalling(self):
|
|
"""
|
|
GIVEN verible is NOT in PATH AND platform IS supported
|
|
AND binary already exists at download location
|
|
WHEN _get_or_install_core_dependency is called
|
|
THEN returns existing path without downloading again
|
|
|
|
WHY: Avoid redundant downloads if verible was already downloaded in previous session.
|
|
This speeds up subsequent runs.
|
|
"""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
custom_settings = SolidLSPSettings.CustomLSSettings({})
|
|
provider = SystemVerilogLanguageServer.DependencyProvider(custom_settings, temp_dir)
|
|
|
|
expected_path = os.path.join(temp_dir, "verible-ls", f"verible-{DEFAULT_VERIBLE_VERSION}", "bin", "verible-verilog-ls")
|
|
|
|
with patch("shutil.which", return_value=None):
|
|
with patch("solidlsp.language_servers.systemverilog_server.RuntimeDependencyCollection") as mock_deps_class:
|
|
mock_dep = Mock()
|
|
mock_deps = Mock()
|
|
mock_deps.get_single_dep_for_current_platform.return_value = mock_dep
|
|
mock_deps.binary_path.return_value = expected_path
|
|
mock_deps_class.return_value = mock_deps
|
|
|
|
# Binary already exists
|
|
with patch("os.path.exists", return_value=True):
|
|
with patch("os.chmod"):
|
|
result = provider._get_or_install_core_dependency()
|
|
|
|
# Should NOT call install since binary already exists
|
|
mock_deps.install.assert_not_called()
|
|
assert result == expected_path
|
|
|
|
|
|
class TestVeribleVerilogLsDetectionIntegration:
|
|
"""
|
|
Integration tests that verify detection works on the current system.
|
|
These tests are skipped if verible-verilog-ls is not installed.
|
|
"""
|
|
|
|
@pytest.mark.systemverilog
|
|
def test_integration_finds_installed_verible(self):
|
|
"""
|
|
GIVEN verible-verilog-ls is installed on this system (via any method)
|
|
WHEN _get_or_install_core_dependency is called
|
|
THEN it returns a valid executable path
|
|
|
|
This test verifies the detection logic works end-to-end on the current system.
|
|
"""
|
|
# Skip if verible-verilog-ls is not installed
|
|
if not shutil.which("verible-verilog-ls"):
|
|
pytest.skip("verible-verilog-ls not installed on this system")
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
custom_settings = SolidLSPSettings.CustomLSSettings({})
|
|
provider = SystemVerilogLanguageServer.DependencyProvider(custom_settings, temp_dir)
|
|
|
|
result = provider._get_or_install_core_dependency()
|
|
|
|
assert result is not None
|
|
assert os.path.isfile(result)
|
|
assert os.access(result, os.X_OK)
|