From a51eb67534af440ab121c95635df736820841c57 Mon Sep 17 00:00:00 2001 From: David Bernazal Date: Wed, 2 Jul 2025 05:27:41 -0500 Subject: [PATCH] Extending times for CI --- .../elixir_tools/elixir_tools.py | 12 ++- test/solidlsp/elixir/conftest.py | 101 ++++++++++++++++-- 2 files changed, 100 insertions(+), 13 deletions(-) diff --git a/src/solidlsp/language_servers/elixir_tools/elixir_tools.py b/src/solidlsp/language_servers/elixir_tools/elixir_tools.py index 1517f1c4..a3203f21 100644 --- a/src/solidlsp/language_servers/elixir_tools/elixir_tools.py +++ b/src/solidlsp/language_servers/elixir_tools/elixir_tools.py @@ -5,6 +5,7 @@ import pathlib import stat import subprocess import threading +import time from pathlib import PurePath from overrides import override @@ -164,7 +165,7 @@ class ElixirTools(SolidLanguageServer): self.request_id = 0 # Set generous timeout for Next LS which can be slow to initialize and respond - self.set_request_timeout(60.0) # 60 seconds for all environments + self.set_request_timeout(180.0) # 60 seconds for all environments def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: """ @@ -269,11 +270,18 @@ class ElixirTools(SolidLanguageServer): # Wait for Next LS to send the specific "Runtime for folder X is ready..." log message # This is the authoritative signal that Next LS is truly ready for requests - ready_timeout = 60.0 + ready_timeout = 180.0 self.logger.log(f"Waiting up to {ready_timeout} seconds for Next LS runtime readiness...", logging.INFO) if self.server_ready.wait(timeout=ready_timeout): self.logger.log("Next LS is ready and available for requests", logging.INFO) + + # Add a small settling period to ensure background indexing is complete + # Next LS often continues compilation/indexing in background after ready signal + settling_time = 10.0 + self.logger.log(f"Allowing {settling_time} seconds for Next LS background indexing to complete...", logging.INFO) + time.sleep(settling_time) + self.logger.log("Next LS settling period complete", logging.INFO) else: error_msg = f"Next LS failed to initialize within {ready_timeout} seconds. This may indicate a problem with the Elixir installation, project compilation, or Next LS itself." self.logger.log(error_msg, logging.ERROR) diff --git a/test/solidlsp/elixir/conftest.py b/test/solidlsp/elixir/conftest.py index f83d4208..6dc31cf5 100644 --- a/test/solidlsp/elixir/conftest.py +++ b/test/solidlsp/elixir/conftest.py @@ -4,6 +4,7 @@ Elixir-specific test configuration and fixtures. import os import subprocess import pytest +import time from pathlib import Path @@ -24,43 +25,118 @@ def ensure_elixir_test_repo_compiled(repo_path: str) -> None: mix_file = os.path.join(repo_path, "mix.exs") if not os.path.exists(mix_file): return + + # Check if already compiled (optimization for repeated runs) + build_path = os.path.join(repo_path, "_build") + deps_path = os.path.join(repo_path, "deps") + + if os.path.exists(build_path) and os.path.exists(deps_path): + print(f"Elixir test repository already compiled in {repo_path}") + return try: print(f"Installing dependencies and compiling Elixir test repository for optimal Next LS performance...") - # First, install dependencies + # First, install dependencies with increased timeout for CI + print("=" * 60) + print("Step 1/2: Installing Elixir dependencies...") + print("=" * 60) + start_time = time.time() + deps_result = subprocess.run( ["mix", "deps.get"], cwd=repo_path, capture_output=True, text=True, - timeout=60 # 60 second timeout for dependency installation + timeout=180 # 3 minutes for dependency installation (CI can be slow) ) + deps_duration = time.time() - start_time + print(f"Dependencies installation completed in {deps_duration:.2f} seconds") + + # Always log the output for transparency + if deps_result.stdout.strip(): + print("Dependencies stdout:") + print("-" * 40) + print(deps_result.stdout) + print("-" * 40) + + if deps_result.stderr.strip(): + print("Dependencies stderr:") + print("-" * 40) + print(deps_result.stderr) + print("-" * 40) + if deps_result.returncode != 0: - print(f"Warning: Failed to install dependencies: {deps_result.stderr}") + print(f"⚠️ Warning: Dependencies installation failed with exit code {deps_result.returncode}") # Continue anyway - some projects might not have dependencies + else: + print("✓ Dependencies installed successfully") + + # Then compile the project with increased timeout for CI + print("=" * 60) + print("Step 2/2: Compiling Elixir project...") + print("=" * 60) + start_time = time.time() - # Then compile the project compile_result = subprocess.run( ["mix", "compile"], cwd=repo_path, capture_output=True, text=True, - timeout=60 # 60 second timeout for compilation + timeout=300 # 5 minutes for compilation (Credo compilation can be slow in CI) ) + compile_duration = time.time() - start_time + print(f"Compilation completed in {compile_duration:.2f} seconds") + + # Always log the output for transparency + if compile_result.stdout.strip(): + print("Compilation stdout:") + print("-" * 40) + print(compile_result.stdout) + print("-" * 40) + + if compile_result.stderr.strip(): + print("Compilation stderr:") + print("-" * 40) + print(compile_result.stderr) + print("-" * 40) + if compile_result.returncode == 0: - print(f"Elixir test repository compiled successfully in {repo_path}") + print(f"✓ Elixir test repository compiled successfully in {repo_path}") else: - print(f"Elixir test compilation completed with warnings/errors: {compile_result.stderr}") + print(f"⚠️ Warning: Compilation completed with exit code {compile_result.returncode}") + # Still continue - warnings are often non-fatal + + print("=" * 60) + print(f"Total setup time: {time.time() - (start_time - compile_duration - deps_duration):.2f} seconds") + print("=" * 60) - except subprocess.TimeoutExpired: - print("Warning: Elixir dependency installation or compilation timed out after 60 seconds") + except subprocess.TimeoutExpired as e: + print("=" * 60) + print(f"❌ TIMEOUT: Elixir setup timed out after {e.timeout} seconds") + print(f"Command: {' '.join(e.cmd)}") + print("This may indicate slow CI environment - Next LS may still work but with reduced functionality") + + # Try to get partial output if available + if hasattr(e, 'stdout') and e.stdout: + print("Partial stdout before timeout:") + print("-" * 40) + print(e.stdout) + print("-" * 40) + if hasattr(e, 'stderr') and e.stderr: + print("Partial stderr before timeout:") + print("-" * 40) + print(e.stderr) + print("-" * 40) + print("=" * 60) + except FileNotFoundError: - print("Warning: 'mix' command not found - Elixir test repository may not be compiled") + print("❌ ERROR: 'mix' command not found - Elixir test repository may not be compiled") + print("Please ensure Elixir is installed and available in PATH") except Exception as e: - print(f"Warning: Failed to prepare Elixir test repository: {e}") + print(f"❌ ERROR: Failed to prepare Elixir test repository: {e}") @pytest.fixture(scope="session", autouse=True) @@ -73,6 +149,9 @@ def setup_elixir_test_environment(): It uses autouse=True so it runs automatically without needing to be explicitly requested by tests. This ensures Next LS has a fully prepared project to work with. + + Uses generous timeouts (3-5 minutes) to accommodate slow CI environments. + All output is logged for transparency and debugging. """ # Get the test repo path relative to this conftest.py file test_repo_path = Path(__file__).parent.parent.parent / "resources" / "repos" / "elixir" / "test_repo"