From 3cfe0f06312fb5414b8b6947e3381a36ac112259 Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 14 Jul 2025 14:41:57 +0200 Subject: [PATCH 01/17] Improve setup for e2e tests --- .envrc | 1 + .github/workflows/system-tests-backend.yml | 2 +- .gitignore | 2 ++ Makefile | 15 ++++++++++----- requirements.txt | 5 +++++ 5 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 .envrc create mode 100644 requirements.txt diff --git a/.envrc b/.envrc new file mode 100644 index 00000000000..94840b30d95 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +layout python3 diff --git a/.github/workflows/system-tests-backend.yml b/.github/workflows/system-tests-backend.yml index eee6f7f90cc..e6bf48a139f 100644 --- a/.github/workflows/system-tests-backend.yml +++ b/.github/workflows/system-tests-backend.yml @@ -66,7 +66,7 @@ jobs: - name: Install Python dependencies run: | python3 -m pip install --upgrade pip - python3 -m pip install requests + python3 -m pip install -r requirements.txt - name: Set up Java uses: actions/setup-java@v4 diff --git a/.gitignore b/.gitignore index 52bec85e0ba..90f9e885cf4 100644 --- a/.gitignore +++ b/.gitignore @@ -20,5 +20,7 @@ distributions/ *.vscode/ sentry-spring-boot-starter-jakarta/src/main/resources/META-INF/spring.factories sentry-samples/sentry-samples-spring-boot-jakarta/spy.log +sentry-mock-server.txt +spring-server.txt spy.log .kotlin diff --git a/Makefile b/Makefile index 0a0f2bbf8c2..8bf79bcd2f2 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all clean compile javadocs dryRelease update checkFormat api assembleBenchmarkTestRelease assembleUiTestRelease assembleUiTestCriticalRelease createCoverageReports runUiTestCritical check preMerge publish systemtest systemtest-interactive +.PHONY: all clean compile javadocs dryRelease update checkFormat api assembleBenchmarkTestRelease assembleUiTestRelease assembleUiTestCriticalRelease createCoverageReports runUiTestCritical setupPython systemTest systemTestInteractive check preMerge publish all: stop clean javadocs compile createCoverageReports assembleBenchmarks: assembleBenchmarkTestRelease @@ -10,6 +10,7 @@ publish: clean dryRelease clean: ./gradlew clean --no-configuration-cache rm -rf distributions + rm -rf .venv # build and run tests compile: @@ -59,13 +60,17 @@ createCoverageReports: ./gradlew jacocoTestReport ./gradlew koverXmlReportRelease +# Create the Python virtual environment for system tests, and install the necessary dependencies +setupPython: + @test -d .venv || (python3 -m venv .venv && .venv/bin/pip install --upgrade pip && .venv/bin/pip install -r requirements.txt) + # Run system tests for sample applications -systemtest: - python3 test/system-test-runner.py test --all +systemTest: setupPython + .venv/bin/python test/system-test-runner.py test --all # Run system tests with interactive module selection -systemtest-interactive: - python3 test/system-test-runner.py test --interactive +systemTestInteractive: setupPython + .venv/bin/python test/system-test-runner.py test --interactive # Run tests and lint check: diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000000..08623cdf27b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +certifi==2025.7.14 +charset-normalizer==3.4.2 +idna==3.10 +requests==2.32.4 +urllib3==2.5.0 From 6e9e00c05d99d45da76cb8ba415370c2bd1ed974 Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 14 Jul 2025 14:43:50 +0200 Subject: [PATCH 02/17] improve --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8bf79bcd2f2..55f465a9663 100644 --- a/Makefile +++ b/Makefile @@ -62,7 +62,9 @@ createCoverageReports: # Create the Python virtual environment for system tests, and install the necessary dependencies setupPython: - @test -d .venv || (python3 -m venv .venv && .venv/bin/pip install --upgrade pip && .venv/bin/pip install -r requirements.txt) + @test -d .venv || python3 -m venv .venv + .venv/bin/pip install --upgrade pip + .venv/bin/pip install -r requirements.txt # Run system tests for sample applications systemTest: setupPython From 527f304866e7193df52ec745e23ed34d0c31da8b Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 14 Jul 2025 14:54:43 +0200 Subject: [PATCH 03/17] improve --- .envrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.envrc b/.envrc index 94840b30d95..97b3f16c6f7 100644 --- a/.envrc +++ b/.envrc @@ -1 +1,2 @@ +export VIRTUAL_ENV=".venv" layout python3 From 1c6da75bc6db5e0c778b126c65b87f7ba515bcc5 Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 14 Jul 2025 15:29:23 +0200 Subject: [PATCH 04/17] improve --- .../src/test/kotlin/io/sentry/DummyTest.kt | 12 - test/system-test-runner.py | 609 ++++++++++-------- 2 files changed, 354 insertions(+), 267 deletions(-) delete mode 100644 sentry-samples/sentry-samples-console/src/test/kotlin/io/sentry/DummyTest.kt diff --git a/sentry-samples/sentry-samples-console/src/test/kotlin/io/sentry/DummyTest.kt b/sentry-samples/sentry-samples-console/src/test/kotlin/io/sentry/DummyTest.kt deleted file mode 100644 index 6f762b7e453..00000000000 --- a/sentry-samples/sentry-samples-console/src/test/kotlin/io/sentry/DummyTest.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.sentry - -import kotlin.test.Test -import kotlin.test.assertTrue - -class DummyTest { - @Test - fun `the only test`() { - // only needed to have more than 0 tests and not fail the build - assertTrue(true) - } -} diff --git a/test/system-test-runner.py b/test/system-test-runner.py index aed0d7126a2..3a3f1283d8a 100644 --- a/test/system-test-runner.py +++ b/test/system-test-runner.py @@ -39,9 +39,10 @@ import requests import threading from pathlib import Path -from typing import Optional, List, Tuple +from typing import Optional, List from dataclasses import dataclass + def str_to_bool(value: str) -> str: """Convert true/false string to 1/0 string for internal compatibility.""" if value.lower() in ('true', '1'): @@ -49,32 +50,52 @@ def str_to_bool(value: str) -> str: elif value.lower() in ('false', '0'): return "0" else: - raise ValueError(f"Invalid boolean value: {value}. Use 'true' or 'false'") + raise ValueError( + f"Invalid boolean value: {value}. Use 'true' or 'false'") + + +@dataclass +class ModuleConfig: + """Configuration for running a sample module.""" + module: str + java_agent: str + java_agent_auto_init: str + build_before_run: str + + def uses_agent(self) -> bool: + """Check if this module configuration uses the Java agent.""" + return str_to_bool(self.java_agent) == "1" + + def needs_build(self) -> bool: + """Check if this module configuration needs building before run.""" + return str_to_bool(self.build_before_run) == "1" + @dataclass class InteractiveSelection: """Result of interactive module selection.""" - modules: List[Tuple[str, str, str, str]] + modules: List[ModuleConfig] manual_test_mode: bool build_agent: bool - + def is_empty(self) -> bool: """Check if no modules were selected.""" return len(self.modules) == 0 - + def is_single_module(self) -> bool: """Check if exactly one module was selected.""" return len(self.modules) == 1 - - def get_first_module(self) -> Tuple[str, str, str, str]: + + def get_first_module(self) -> ModuleConfig: """Get the first selected module (for manual test mode).""" if self.is_empty(): raise ValueError("No modules selected") return self.modules[0] - + def has_agent_modules(self) -> bool: """Check if any selected modules use the Java agent.""" - return any(str_to_bool(agent) == "1" for _, agent, _, _ in self.modules) + return any(module.uses_agent() for module in self.modules) + class SystemTestRunner: def __init__(self): @@ -84,16 +105,18 @@ def __init__(self): self.spring_server_pid: Optional[int] = None self.mock_server_pid_file = "sentry-mock-server.pid" self.spring_server_pid_file = "spring-server.pid" - + # Load existing PIDs if available self.mock_server_pid = self.read_pid_file(self.mock_server_pid_file) - self.spring_server_pid = self.read_pid_file(self.spring_server_pid_file) - + self.spring_server_pid = self.read_pid_file( + self.spring_server_pid_file) + if self.mock_server_pid: print(f"Found existing mock server PID: {self.mock_server_pid}") if self.spring_server_pid: - print(f"Found existing Spring server PID: {self.spring_server_pid}") - + print( + f"Found existing Spring server PID: {self.spring_server_pid}") + def read_pid_file(self, pid_file: str) -> Optional[int]: """Read PID from file if it exists.""" try: @@ -103,7 +126,7 @@ def read_pid_file(self, pid_file: str) -> Optional[int]: except (ValueError, IOError) as e: print(f"Error reading PID file {pid_file}: {e}") return None - + def is_process_running(self, pid: int) -> bool: """Check if a process with given PID is still running.""" try: @@ -112,24 +135,23 @@ def is_process_running(self, pid: int) -> bool: return True except (OSError, ProcessLookupError): return False - + def kill_process(self, pid: int, name: str) -> None: """Kill a process by PID.""" try: print(f"Killing existing {name} process with PID {pid}") os.kill(pid, signal.SIGTERM) time.sleep(2) # Give it time to terminate gracefully - + # Check if it's still running and force kill if necessary if self.is_process_running(pid): - print(f"Process {pid} didn't terminate gracefully, force killing...") + print( + f"Process {pid} didn't terminate gracefully, force killing...") os.kill(pid, signal.SIGKILL) time.sleep(1) except (OSError, ProcessLookupError): print(f"Process {pid} was already dead") - - def start_sentry_mock_server(self) -> None: """Start the Sentry mock server.""" print("Starting Sentry mock server...") @@ -141,40 +163,43 @@ def start_sentry_mock_server(self) -> None: stdout=log_file, stderr=subprocess.STDOUT ) - + # Store PID in instance variable and write to file self.mock_server_pid = self.mock_server_process.pid with open(self.mock_server_pid_file, "w") as pid_file: pid_file.write(str(self.mock_server_pid)) - + print(f"Started mock server with PID {self.mock_server_pid}") - + # Wait a moment for the server to start time.sleep(2) - + except Exception as e: print(f"Failed to start mock server: {e}") raise - + def stop_sentry_mock_server(self) -> None: """Stop the Sentry mock server.""" try: # Try graceful shutdown first try: - response = requests.get("http://127.0.0.1:8000/STOP", timeout=5) + response = requests.get( + "http://127.0.0.1:8000/STOP", timeout=5) print("Sent stop signal to mock server") except: print("Could not send graceful stop signal") - + # Kill the process - try process object first, then PID from file if self.mock_server_process and self.mock_server_process.poll() is None: - print(f"Killing mock server process object with PID {self.mock_server_process.pid}") + print( + f"Killing mock server process object with PID {self.mock_server_process.pid}") self.mock_server_process.kill() self.mock_server_process.wait(timeout=5) elif self.mock_server_pid and self.is_process_running(self.mock_server_pid): - print(f"Killing mock server from PID file with PID {self.mock_server_pid}") + print( + f"Killing mock server from PID file with PID {self.mock_server_pid}") self.kill_process(self.mock_server_pid, "mock server") - + except Exception as e: print(f"Error stopping mock server: {e}") finally: @@ -182,56 +207,57 @@ def stop_sentry_mock_server(self) -> None: if os.path.exists(self.mock_server_pid_file): os.remove(self.mock_server_pid_file) self.mock_server_pid = None - + def find_agent_jar(self) -> Optional[str]: """Find the OpenTelemetry agent JAR file.""" - agent_dir = Path("sentry-opentelemetry/sentry-opentelemetry-agent/build/libs/") + agent_dir = Path( + "sentry-opentelemetry/sentry-opentelemetry-agent/build/libs/") if not agent_dir.exists(): return None - + for jar_file in agent_dir.glob("*.jar"): name = jar_file.name - if ("agent" in name and - "javadoc" not in name and - "sources" not in name and - "dontuse" not in name): + if ("agent" in name and + "javadoc" not in name and + "sources" not in name and + "dontuse" not in name): return str(jar_file) return None - + def build_agent_jar(self) -> int: """Build the OpenTelemetry agent JAR file.""" print("Building OpenTelemetry agent JAR...") return self.run_gradle_task(":sentry-opentelemetry:sentry-opentelemetry-agent:assemble") - + def ensure_agent_jar(self, skip_build: bool = False) -> Optional[str]: """Ensure the OpenTelemetry agent JAR exists, building it if necessary.""" agent_jar = self.find_agent_jar() if agent_jar: return agent_jar - + if skip_build: print("OpenTelemetry agent JAR not found and build was skipped") return None - + # Agent JAR doesn't exist, try to build it print("OpenTelemetry agent JAR not found, building it...") build_result = self.build_agent_jar() if build_result != 0: print("Failed to build OpenTelemetry agent JAR") return None - + # Try to find it again after building agent_jar = self.find_agent_jar() if not agent_jar: print("OpenTelemetry agent JAR still not found after building") return None - + return agent_jar - + def start_spring_server(self, sample_module: str, java_agent: str, java_agent_auto_init: str) -> None: """Start a Spring Boot server for testing.""" print(f"Starting Spring server for {sample_module}...") - + # Build environment variables env = os.environ.copy() env.update({ @@ -239,25 +265,26 @@ def start_spring_server(self, sample_module: str, java_agent: str, java_agent_au "SENTRY_AUTO_INIT": java_agent_auto_init, "SENTRY_TRACES_SAMPLE_RATE": "1.0", "OTEL_TRACES_EXPORTER": "none", - "OTEL_METRICS_EXPORTER": "none", + "OTEL_METRICS_EXPORTER": "none", "OTEL_LOGS_EXPORTER": "none", "SENTRY_LOGS_ENABLED": "true" }) - + # Build command jar_path = f"sentry-samples/{sample_module}/build/libs/{sample_module}-0.0.1-SNAPSHOT.jar" cmd = ["java"] - + if java_agent == "1": agent_jar = self.ensure_agent_jar() if agent_jar: cmd.append(f"-javaagent:{agent_jar}") print(f"Using Java Agent: {agent_jar}") else: - print("Warning: Java agent was requested but could not be found or built") - + print( + "Warning: Java agent was requested but could not be found or built") + cmd.extend(["-jar", jar_path]) - + try: # Start the Spring server with open("spring-server.txt", "w") as log_file: @@ -267,22 +294,22 @@ def start_spring_server(self, sample_module: str, java_agent: str, java_agent_au stdout=log_file, stderr=subprocess.STDOUT ) - + # Store PID in instance variable and write to file self.spring_server_pid = self.spring_server_process.pid with open(self.spring_server_pid_file, "w") as pid_file: pid_file.write(str(self.spring_server_pid)) - + print(f"Started Spring server with PID {self.spring_server_pid}") - + except Exception as e: print(f"Failed to start Spring server: {e}") raise - + def wait_for_spring(self, max_attempts: int = 20) -> bool: """Wait for Spring Boot application to be ready.""" print("Waiting for Spring application to be ready...") - + for attempt in range(1, max_attempts + 1): try: response = requests.head( @@ -295,13 +322,13 @@ def wait_for_spring(self, max_attempts: int = 20) -> bool: return True except: pass - + print(f"Waiting... (attempt {attempt}/{max_attempts})") time.sleep(1) - + print("Spring application failed to become ready") return False - + def get_spring_status(self) -> dict: """Get status of Spring Boot application.""" status = { @@ -309,10 +336,10 @@ def get_spring_status(self) -> dict: "pid": self.spring_server_pid, "http_ready": False } - + if self.spring_server_pid and self.is_process_running(self.spring_server_pid): status["process_running"] = True - + # Check HTTP endpoint try: response = requests.head( @@ -324,9 +351,9 @@ def get_spring_status(self) -> dict: status["http_ready"] = True except: pass - + return status - + def get_sentry_status(self) -> dict: """Get status of Sentry mock server.""" status = { @@ -334,51 +361,56 @@ def get_sentry_status(self) -> dict: "pid": self.mock_server_pid, "http_ready": False } - + if self.mock_server_pid and self.is_process_running(self.mock_server_pid): status["process_running"] = True - + # Check HTTP endpoint try: - response = requests.get("http://127.0.0.1:8000/envelope-count", timeout=2) + response = requests.get( + "http://127.0.0.1:8000/envelope-count", timeout=2) if response.status_code == 200: status["http_ready"] = True except: pass - + return status - + def print_status_summary(self) -> None: """Print status summary of all services.""" print("=== Service Status ===") - + sentry_status = self.get_sentry_status() print(f"Sentry Mock Server:") print(f" PID: {sentry_status['pid'] or 'None'}") - print(f" Process Running: {'✅' if sentry_status['process_running'] else '❌'}") + print( + f" Process Running: {'✅' if sentry_status['process_running'] else '❌'}") print(f" HTTP Ready: {'✅' if sentry_status['http_ready'] else '❌'}") - + spring_status = self.get_spring_status() print(f"Spring Boot App:") print(f" PID: {spring_status['pid'] or 'None'}") - print(f" Process Running: {'✅' if spring_status['process_running'] else '❌'}") + print( + f" Process Running: {'✅' if spring_status['process_running'] else '❌'}") print(f" HTTP Ready: {'✅' if spring_status['http_ready'] else '❌'}") - + def stop_spring_server(self) -> None: """Stop the Spring Boot server.""" try: # Kill the process - try process object first, then PID from file if self.spring_server_process and self.spring_server_process.poll() is None: - print(f"Killing Spring server process object with PID {self.spring_server_process.pid}") + print( + f"Killing Spring server process object with PID {self.spring_server_process.pid}") self.spring_server_process.kill() try: self.spring_server_process.wait(timeout=10) except subprocess.TimeoutExpired: print("Spring server did not terminate gracefully") elif self.spring_server_pid and self.is_process_running(self.spring_server_pid): - print(f"Killing Spring server from PID file with PID {self.spring_server_pid}") + print( + f"Killing Spring server from PID file with PID {self.spring_server_pid}") self.kill_process(self.spring_server_pid, "Spring server") - + except Exception as e: print(f"Error stopping Spring server: {e}") finally: @@ -386,17 +418,17 @@ def stop_spring_server(self) -> None: if os.path.exists(self.spring_server_pid_file): os.remove(self.spring_server_pid_file) self.spring_server_pid = None - + def get_build_task(self, sample_module: str) -> str: """Get the appropriate build task for a module.""" return "bootJar" if "spring" in sample_module else "assemble" - + def build_module(self, sample_module: str) -> int: """Build a sample module using the appropriate task.""" build_task = self.get_build_task(sample_module) print(f"Building {sample_module} using {build_task} task") return self.run_gradle_task(f":sentry-samples:{sample_module}:{build_task}") - + def run_gradle_task(self, task: str) -> int: """Run a Gradle task and return the exit code.""" print(f"Running: ./gradlew {task}") @@ -406,9 +438,9 @@ def run_gradle_task(self, task: str) -> int: except Exception as e: print(f"Failed to run Gradle task: {e}") return 1 - - def setup_test_infrastructure(self, sample_module: str, java_agent: str, - java_agent_auto_init: str, build_before_run: str) -> int: + + def setup_test_infrastructure(self, sample_module: str, java_agent: str, + java_agent_auto_init: str, build_before_run: str) -> int: """Set up test infrastructure. Returns 0 on success, error code on failure.""" # Build if requested if build_before_run == "1": @@ -417,77 +449,84 @@ def setup_test_infrastructure(self, sample_module: str, java_agent: str, if build_result != 0: print("Build failed") return build_result - + # Ensure agent JAR is available if needed if java_agent == "1": agent_jar = self.ensure_agent_jar() if not agent_jar: print("Error: Java agent was requested but could not be found or built") return 1 - + # Start mock server print("Starting Sentry mock server...") self.start_sentry_mock_server() - + # Start Spring server if it's a Spring module if "spring" in sample_module: print(f"Starting Spring server for {sample_module}...") - self.start_spring_server(sample_module, java_agent, java_agent_auto_init) + self.start_spring_server( + sample_module, java_agent, java_agent_auto_init) if not self.wait_for_spring(): print("Spring application failed to start!") return 1 print("Spring application is ready!") - + return 0 - - def run_single_test(self, sample_module: str, java_agent: str, - java_agent_auto_init: str, build_before_run: str) -> int: + + def run_single_test(self, sample_module: str, java_agent: str, + java_agent_auto_init: str, build_before_run: str) -> int: """Run a single system test.""" print(f"Running system test for {sample_module}") - + try: # Set up infrastructure - setup_result = self.setup_test_infrastructure(sample_module, java_agent, java_agent_auto_init, build_before_run) + setup_result = self.setup_test_infrastructure( + sample_module, java_agent, java_agent_auto_init, build_before_run) if setup_result != 0: return setup_result - + # Run the system test - test_result = self.run_gradle_task(f":sentry-samples:{sample_module}:systemTest") - + test_result = self.run_gradle_task( + f":sentry-samples:{sample_module}:systemTest") + return test_result - + finally: # Cleanup if "spring" in sample_module: self.stop_spring_server() self.stop_sentry_mock_server() - + def run_all_tests(self) -> int: """Run all system tests.""" test_configs = self.get_available_modules() - + failed_tests = [] - - for sample_module, java_agent, java_agent_auto_init, build_before_run in test_configs: + + for module_config in test_configs: # Convert true/false to internal 1/0 format - agent = str_to_bool(java_agent) - auto_init = java_agent_auto_init # already in correct format - build = str_to_bool(build_before_run) - + agent = str_to_bool(module_config.java_agent) + auto_init = module_config.java_agent_auto_init # already in correct format + build = str_to_bool(module_config.build_before_run) + print(f"\n{'='*60}") - print(f"Running test: {sample_module} (agent={java_agent}, auto_init={java_agent_auto_init})") + print( + f"Running test: {module_config.module} (agent={module_config.java_agent}, auto_init={module_config.java_agent_auto_init})") print(f"{'='*60}") - - result = self.run_single_test(sample_module, agent, auto_init, build) - + + result = self.run_single_test( + module_config.module, agent, auto_init, build) + if result != 0: # Find the module number in the full list for interactive reference - module_number = self._find_module_number(sample_module, java_agent, java_agent_auto_init) - failed_tests.append((module_number, sample_module, java_agent, java_agent_auto_init)) - print(f"❌ Test failed: {sample_module}") + module_number = self._find_module_number( + module_config.module, module_config.java_agent, module_config.java_agent_auto_init) + failed_tests.append((module_number, module_config.module, + module_config.java_agent, module_config.java_agent_auto_init)) + print(f"❌ Test failed: {module_config.module}") else: - print(f"✅ Test passed: {sample_module}") - + print(f"✅ Test passed: {module_config.module}") + # Summary print(f"\n{'='*60}") print("TEST SUMMARY") @@ -495,90 +534,106 @@ def run_all_tests(self) -> int: print(f"Total tests: {len(test_configs)}") print(f"Passed: {len(test_configs) - len(failed_tests)}") print(f"Failed: {len(failed_tests)}") - + if failed_tests: print("\nFailed tests (for interactive mode, use these numbers):") for module_number, sample_module, java_agent, java_agent_auto_init in failed_tests: - print(f" {module_number}. {sample_module} (agent={java_agent}, auto_init={java_agent_auto_init})") + print( + f" {module_number}. {sample_module} (agent={java_agent}, auto_init={java_agent_auto_init})") return 1 else: print("\n🎉 All tests passed!") return 0 - - def run_manual_test_mode(self, sample_module: str, java_agent: str, - java_agent_auto_init: str, build_before_run: str) -> int: + + def run_manual_test_mode(self, sample_module: str, java_agent: str, + java_agent_auto_init: str, build_before_run: str) -> int: """Set up infrastructure for manual testing from IDE.""" print(f"Setting up manual test environment for {sample_module}") - + try: # Set up infrastructure - setup_result = self.setup_test_infrastructure(sample_module, java_agent, java_agent_auto_init, build_before_run) + setup_result = self.setup_test_infrastructure( + sample_module, java_agent, java_agent_auto_init, build_before_run) if setup_result != 0: return setup_result - + # Show status and wait for user print("\n" + "="*60) print("🚀 Manual test environment ready 🚀") print("="*60) self.print_status_summary() - print(f"\nInfrastructure is ready for manual testing of: {sample_module}") + print( + f"\nInfrastructure is ready for manual testing of: {sample_module}") print("You can now run your system tests from your IDE.") print("\nTest configuration:") print(f" - Module: {sample_module}") print(f" - Java Agent: {'Yes' if java_agent == '1' else 'No'}") print(f" - Agent Auto-init: {java_agent_auto_init}") - print(f" - Mock DSN: http://502f25099c204a2fbf4cb16edc5975d1@localhost:8000/0") - + print( + f" - Mock DSN: http://502f25099c204a2fbf4cb16edc5975d1@localhost:8000/0") + if "spring" in sample_module: print("\nSpring Boot app is running on: http://localhost:8080") - + print("\nPress Enter to stop the infrastructure and exit...") - + # Wait for user input try: input() except KeyboardInterrupt: print("\nReceived interrupt signal") - + print("\nStopping infrastructure...") return 0 - + finally: # Cleanup will happen in the finally block of main() pass - - def get_available_modules(self) -> List[Tuple[str, str, str, str]]: + + def get_available_modules(self) -> List[ModuleConfig]: """Get list of all available test modules.""" return [ - ("sentry-samples-spring-boot", "false", "true", "false"), - ("sentry-samples-spring-boot-opentelemetry-noagent", "false", "true", "false"), - ("sentry-samples-spring-boot-opentelemetry", "true", "true", "false"), - ("sentry-samples-spring-boot-opentelemetry", "true", "false", "false"), - ("sentry-samples-spring-boot-webflux-jakarta", "false", "true", "false"), - ("sentry-samples-spring-boot-webflux", "false", "true", "false"), - ("sentry-samples-spring-boot-jakarta", "false", "true", "false"), - ("sentry-samples-spring-boot-jakarta-opentelemetry-noagent", "false", "true", "false"), - ("sentry-samples-spring-boot-jakarta-opentelemetry", "true", "true", "false"), - ("sentry-samples-spring-boot-jakarta-opentelemetry", "true", "false", "false"), - ("sentry-samples-console", "false", "true", "false"), + ModuleConfig("sentry-samples-spring-boot", + "false", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-opentelemetry-noagent", + "false", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-opentelemetry", + "true", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-opentelemetry", + "true", "false", "false"), + ModuleConfig("sentry-samples-spring-boot-webflux-jakarta", + "false", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-webflux", + "false", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-jakarta", + "false", "true", "false"), + ModuleConfig( + "sentry-samples-spring-boot-jakarta-opentelemetry-noagent", "false", "true", "false"), + ModuleConfig( + "sentry-samples-spring-boot-jakarta-opentelemetry", "true", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-jakarta-opentelemetry", + "true", "false", "false"), + ModuleConfig("sentry-samples-console", "false", "true", "false"), ] - + def _find_module_number(self, module_name: str, agent: str, auto_init: str) -> int: """Find the module number in the interactive list (1-based).""" modules = self.get_available_modules() - for i, (mod_name, mod_agent, mod_auto_init, _) in enumerate(modules, 1): - if mod_name == module_name and mod_agent == agent and mod_auto_init == auto_init: + for i, module_config in enumerate(modules, 1): + if (module_config.sample_module == module_name and + module_config.java_agent == agent and + module_config.java_agent_auto_init == auto_init): return i return 0 # Should not happen, but return 0 if not found - + def parse_selection(self, user_input: str, max_index: int) -> List[int]: """Parse user selection string into list of indices.""" if user_input.strip() == "*": return list(range(max_index)) - + indices = [] parts = user_input.split(",") - + for part in parts: part = part.strip() if "-" in part: @@ -596,34 +651,36 @@ def parse_selection(self, user_input: str, max_index: int) -> List[int]: indices.append(int(part) - 1) except ValueError: raise ValueError(f"Invalid number: {part}") - + # Remove duplicates and sort indices = sorted(set(indices)) - + # Validate indices for idx in indices: if idx < 0 or idx >= max_index: - raise ValueError(f"Index {idx + 1} is out of range (1-{max_index})") - + raise ValueError( + f"Index {idx + 1} is out of range (1-{max_index})") + return indices - + def interactive_module_selection(self) -> InteractiveSelection: """Display modules and get user selection.""" modules = self.get_available_modules() - + print("\nAvailable test modules:") print("=" * 80) - for i, (module, agent, auto_init, build) in enumerate(modules, 1): - agent_text = "with agent" if str_to_bool(agent) == "1" else "no agent" - auto_init_text = f"auto-init: {auto_init}" - print(f"{i:2d}. {module:<50} ({agent_text}, {auto_init_text})") - + for i, module_config in enumerate(modules, 1): + agent_text = "with agent" if module_config.uses_agent() else "no agent" + auto_init_text = f"auto-init: {module_config.java_agent_auto_init}" + print( + f"{i:2d}. {module_config.sample_module:<50} ({agent_text}, {auto_init_text})") + print("\nSelection options:") print(" * = all modules") print(" Single: 1, 5, 8") print(" Range: 1-4, 6-8") print(" Combined: 1,2,4-5,8") - + selected_modules = [] while True: try: @@ -631,34 +688,38 @@ def interactive_module_selection(self) -> InteractiveSelection: if not user_input: print("Please enter a selection.") continue - - selected_indices = self.parse_selection(user_input, len(modules)) + + selected_indices = self.parse_selection( + user_input, len(modules)) selected_modules = [modules[i] for i in selected_indices] - + # Show confirmation print(f"\nSelected {len(selected_modules)} module(s):") - for i, (module, agent, auto_init, build) in enumerate(selected_modules, 1): - agent_text = "with agent" if str_to_bool(agent) == "1" else "no agent" - print(f" {i}. {module} ({agent_text}, auto-init: {auto_init})") - - confirm = input("\nProceed with these selections? [Y/n]: ").strip().lower() + for i, module_config in enumerate(selected_modules, 1): + agent_text = "with agent" if module_config.uses_agent() else "no agent" + print( + f" {i}. {module_config.sample_module} ({agent_text}, auto-init: {module_config.java_agent_auto_init})") + + confirm = input( + "\nProceed with these selections? [Y/n]: ").strip().lower() if confirm in ('', 'y', 'yes'): break else: print("Please make a new selection.") - + except ValueError as e: print(f"Error: {e}") print("Please try again.") except KeyboardInterrupt: print("\nOperation cancelled.") return InteractiveSelection(modules=[], manual_test_mode=False, build_agent=False) - + # Ask about test mode manual_test_mode = False while True: try: - mode_input = input("\nRun tests automatically (n = only set up infrastucture for testing in IDE)? [Y/n]: ").strip().lower() + mode_input = input( + "\nRun tests automatically (n = only set up infrastucture for testing in IDE)? [Y/n]: ").strip().lower() if not mode_input or mode_input in ('y', 'yes'): manual_test_mode = False break @@ -670,14 +731,16 @@ def interactive_module_selection(self) -> InteractiveSelection: except KeyboardInterrupt: print("\nOperation cancelled.") return InteractiveSelection(modules=[], manual_test_mode=False, build_agent=False) - + # Ask about building agent if any modules use it build_agent = False - has_agent_modules = any(str_to_bool(agent) == "1" for _, agent, _, _ in selected_modules) + has_agent_modules = any(module_config.uses_agent() + for module_config in selected_modules) if has_agent_modules: while True: try: - agent_input = input("\nBuild OpenTelemetry agent JAR (recommended to ensure latest version)? [Y/n]: ").strip().lower() + agent_input = input( + "\nBuild OpenTelemetry agent JAR (recommended to ensure latest version)? [Y/n]: ").strip().lower() if not agent_input or agent_input in ('y', 'yes'): build_agent = True break @@ -689,17 +752,17 @@ def interactive_module_selection(self) -> InteractiveSelection: except KeyboardInterrupt: print("\nOperation cancelled.") return InteractiveSelection(modules=[], manual_test_mode=False, build_agent=False) - + return InteractiveSelection(modules=selected_modules, manual_test_mode=manual_test_mode, build_agent=build_agent) - + def run_interactive_tests(self, agent: str, auto_init: str, build: str) -> int: """Run tests with interactive module selection.""" selection = self.interactive_module_selection() - + if selection.is_empty(): print("No modules selected. Exiting.") return 0 - + # Build agent JAR if requested and modules use agent if selection.build_agent and selection.has_agent_modules(): print("\nBuilding OpenTelemetry agent JAR...") @@ -708,47 +771,53 @@ def run_interactive_tests(self, agent: str, auto_init: str, build: str) -> int: print("Failed to build OpenTelemetry agent JAR") return build_result print("✅ OpenTelemetry agent JAR built successfully") - + # Handle manual test mode if selection.manual_test_mode: if not selection.is_single_module(): print("Error: Manual test mode can only be used with a single module.") print("Please select only one module for manual testing.") return 1 - - sample_module, test_agent, test_auto_init, test_build = selection.get_first_module() + + module_config = selection.get_first_module() # Convert true/false to internal 1/0 format - agent = str_to_bool(test_agent) - auto_init = test_auto_init # already in correct format - build = str_to_bool(test_build) - - print(f"\nSetting up manual test environment for: {sample_module}") - return self.run_manual_test_mode(sample_module, agent, auto_init, build) - + agent = str_to_bool(module_config.java_agent) + auto_init = module_config.java_agent_auto_init # already in correct format + build = str_to_bool(module_config.build_before_run) + + print( + f"\nSetting up manual test environment for: {module_config.module}") + return self.run_manual_test_mode(module_config.module, agent, auto_init, build) + # Handle automatic test running failed_tests = [] - - for i, (sample_module, test_agent, test_auto_init, test_build) in enumerate(selection.modules, 1): + + for i, module_config in enumerate(selection.modules, 1): # Convert true/false to internal 1/0 format - agent = str_to_bool(test_agent) - auto_init = test_auto_init # already in correct format - build = str_to_bool(test_build) - + agent = str_to_bool(module_config.java_agent) + auto_init = module_config.java_agent_auto_init # already in correct format + build = str_to_bool(module_config.build_before_run) + print(f"\n{'='*60}") - print(f"Running test {i}/{len(selection.modules)}: {sample_module}") - print(f"Agent: {test_agent}, Auto-init: {test_auto_init}") + print( + f"Running test {i}/{len(selection.modules)}: {module_config.module}") + print( + f"Agent: {module_config.java_agent}, Auto-init: {module_config.java_agent_auto_init}") print(f"{'='*60}") - - result = self.run_single_test(sample_module, agent, auto_init, build) - + + result = self.run_single_test( + module_config.module, agent, auto_init, build) + if result != 0: # Find the module number in the full list for interactive reference - module_number = self._find_module_number(sample_module, test_agent, test_auto_init) - failed_tests.append((module_number, sample_module, test_agent, test_auto_init)) - print(f"❌ Test failed: {sample_module}") + module_number = self._find_module_number( + module_config.module, module_config.java_agent, module_config.java_agent_auto_init) + failed_tests.append((module_number, module_config.module, + module_config.java_agent, module_config.java_agent_auto_init)) + print(f"❌ Test failed: {module_config.sample_module}") else: - print(f"✅ Test passed: {sample_module}") - + print(f"✅ Test passed: {module_config.sample_module}") + # Summary print(f"\n{'='*60}") print("TEST SUMMARY") @@ -756,16 +825,17 @@ def run_interactive_tests(self, agent: str, auto_init: str, build: str) -> int: print(f"Total tests: {len(selection.modules)}") print(f"Passed: {len(selection.modules) - len(failed_tests)}") print(f"Failed: {len(failed_tests)}") - + if failed_tests: print("\nFailed tests (for interactive mode, use these numbers):") for module_number, sample_module, test_agent, test_auto_init in failed_tests: - print(f" {module_number}. {sample_module} (agent={test_agent}, auto_init={test_auto_init})") + print( + f" {module_number}. {sample_module} (agent={test_agent}, auto_init={test_auto_init})") return 1 else: print("\n🎉 All tests passed!") return 0 - + def cleanup_on_exit(self, signum, frame): """Cleanup handler for signals.""" print(f"\nReceived signal {signum}, cleaning up...") @@ -773,72 +843,98 @@ def cleanup_on_exit(self, signum, frame): self.stop_sentry_mock_server() sys.exit(1) + def main(): - parser = argparse.ArgumentParser(description="System Test Runner for Sentry Java") - subparsers = parser.add_subparsers(dest="command", help="Available commands") - + parser = argparse.ArgumentParser( + description="System Test Runner for Sentry Java") + subparsers = parser.add_subparsers( + dest="command", help="Available commands") + # Test subcommand test_parser = subparsers.add_parser("test", help="Run system tests") test_group = test_parser.add_mutually_exclusive_group(required=True) - test_group.add_argument("--all", action="store_true", help="Run all system tests") + test_group.add_argument("--all", action="store_true", + help="Run all system tests") test_group.add_argument("--module", help="Sample module to test") - test_group.add_argument("--interactive", "-i", action="store_true", help="Interactive module selection") - test_parser.add_argument("--agent", default="false", help="Use Java agent (true or false)") - test_parser.add_argument("--auto-init", default="true", help="Auto-init agent (true or false)") - test_parser.add_argument("--build", default="false", help="Build before running (true or false)") - test_parser.add_argument("--manual-test", action="store_true", help="Set up infrastructure but pause for manual testing from IDE") - + test_group.add_argument( + "--interactive", "-i", action="store_true", help="Interactive module selection") + test_parser.add_argument("--agent", default="false", + help="Use Java agent (true or false)") + test_parser.add_argument( + "--auto-init", default="true", help="Auto-init agent (true or false)") + test_parser.add_argument("--build", default="false", + help="Build before running (true or false)") + test_parser.add_argument("--manual-test", action="store_true", + help="Set up infrastructure but pause for manual testing from IDE") + # Spring subcommand - spring_parser = subparsers.add_parser("spring", help="Manage Spring Boot applications") - spring_subparsers = spring_parser.add_subparsers(dest="spring_action", help="Spring actions") - - spring_start_parser = spring_subparsers.add_parser("start", help="Start Spring Boot application") + spring_parser = subparsers.add_parser( + "spring", help="Manage Spring Boot applications") + spring_subparsers = spring_parser.add_subparsers( + dest="spring_action", help="Spring actions") + + spring_start_parser = spring_subparsers.add_parser( + "start", help="Start Spring Boot application") spring_start_parser.add_argument("module", help="Sample module to start") - spring_start_parser.add_argument("--agent", default="false", help="Use Java agent (true or false)") - spring_start_parser.add_argument("--auto-init", default="true", help="Auto-init agent (true or false)") - spring_start_parser.add_argument("--build", default="false", help="Build before starting (true or false)") - - spring_stop_parser = spring_subparsers.add_parser("stop", help="Stop Spring Boot application") - - spring_wait_parser = spring_subparsers.add_parser("wait", help="Wait for Spring Boot application to be ready") - spring_wait_parser.add_argument("--timeout", type=int, default=20, help="Max attempts to wait (default: 20)") - - spring_status_parser = spring_subparsers.add_parser("status", help="Check Spring Boot application status") - + spring_start_parser.add_argument( + "--agent", default="false", help="Use Java agent (true or false)") + spring_start_parser.add_argument( + "--auto-init", default="true", help="Auto-init agent (true or false)") + spring_start_parser.add_argument( + "--build", default="false", help="Build before starting (true or false)") + + spring_stop_parser = spring_subparsers.add_parser( + "stop", help="Stop Spring Boot application") + + spring_wait_parser = spring_subparsers.add_parser( + "wait", help="Wait for Spring Boot application to be ready") + spring_wait_parser.add_argument( + "--timeout", type=int, default=20, help="Max attempts to wait (default: 20)") + + spring_status_parser = spring_subparsers.add_parser( + "status", help="Check Spring Boot application status") + # Sentry subcommand - sentry_parser = subparsers.add_parser("sentry", help="Manage Sentry mock server") - sentry_subparsers = sentry_parser.add_subparsers(dest="sentry_action", help="Sentry actions") - - sentry_start_parser = sentry_subparsers.add_parser("start", help="Start Sentry mock server") - sentry_stop_parser = sentry_subparsers.add_parser("stop", help="Stop Sentry mock server") - sentry_status_parser = sentry_subparsers.add_parser("status", help="Check Sentry mock server status") - + sentry_parser = subparsers.add_parser( + "sentry", help="Manage Sentry mock server") + sentry_subparsers = sentry_parser.add_subparsers( + dest="sentry_action", help="Sentry actions") + + sentry_start_parser = sentry_subparsers.add_parser( + "start", help="Start Sentry mock server") + sentry_stop_parser = sentry_subparsers.add_parser( + "stop", help="Stop Sentry mock server") + sentry_status_parser = sentry_subparsers.add_parser( + "status", help="Check Sentry mock server status") + # Status subcommand - status_parser = subparsers.add_parser("status", help="Show status of all services") - + status_parser = subparsers.add_parser( + "status", help="Show status of all services") + args = parser.parse_args() - + if not args.command: parser.print_help() return 1 - + runner = SystemTestRunner() - + # Set up signal handlers for cleanup signal.signal(signal.SIGINT, runner.cleanup_on_exit) signal.signal(signal.SIGTERM, runner.cleanup_on_exit) - + try: if args.command == "test": # Convert true/false arguments to internal 1/0 format agent = str_to_bool(args.agent) auto_init = args.auto_init # already accepts true/false build = str_to_bool(args.build) - + if args.manual_test and args.module: return runner.run_manual_test_mode(args.module, agent, auto_init, build) elif args.manual_test and args.all: - print("Error: --manual-test requires a specific --module, cannot be used with --all") + print( + "Error: --manual-test requires a specific --module, cannot be used with --all") return 1 elif args.manual_test and args.interactive: print("Error: --manual-test cannot be used with --interactive") @@ -849,14 +945,14 @@ def main(): return runner.run_single_test(args.module, agent, auto_init, build) elif args.interactive: return runner.run_interactive_tests(agent, auto_init, build) - + elif args.command == "spring": if args.spring_action == "start": # Convert true/false arguments to internal format agent = str_to_bool(args.agent) auto_init = args.auto_init # already accepts true/false build = str_to_bool(args.build) - + # Build if requested if build == "1": print("Building before starting Spring application") @@ -864,7 +960,7 @@ def main(): if build_result != 0: print("Build failed") return build_result - + runner.start_spring_server(args.module, agent, auto_init) if runner.wait_for_spring(): print("Spring application started successfully!") @@ -887,13 +983,14 @@ def main(): status = runner.get_spring_status() print(f"Spring Boot Application Status:") print(f" PID: {status['pid'] or 'None'}") - print(f" Process Running: {'✅' if status['process_running'] else '❌'}") + print( + f" Process Running: {'✅' if status['process_running'] else '❌'}") print(f" HTTP Ready: {'✅' if status['http_ready'] else '❌'}") return 0 if (status['process_running'] and status['http_ready']) else 1 else: spring_parser.print_help() return 1 - + elif args.command == "sentry": if args.sentry_action == "start": runner.start_sentry_mock_server() @@ -907,20 +1004,21 @@ def main(): status = runner.get_sentry_status() print(f"Sentry Mock Server Status:") print(f" PID: {status['pid'] or 'None'}") - print(f" Process Running: {'✅' if status['process_running'] else '❌'}") + print( + f" Process Running: {'✅' if status['process_running'] else '❌'}") print(f" HTTP Ready: {'✅' if status['http_ready'] else '❌'}") return 0 if (status['process_running'] and status['http_ready']) else 1 else: sentry_parser.print_help() return 1 - + elif args.command == "status": runner.print_status_summary() return 0 else: parser.print_help() return 1 - + except KeyboardInterrupt: print("\nInterrupted by user") return 1 @@ -933,5 +1031,6 @@ def main(): runner.stop_spring_server() runner.stop_sentry_mock_server() + if __name__ == "__main__": - sys.exit(main()) \ No newline at end of file + sys.exit(main()) From f929919f5ed5e9c228de81ded0fda6e9af4cb041 Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 14 Jul 2025 15:37:14 +0200 Subject: [PATCH 05/17] improve --- .../src/main/java/io/sentry/samples/console/Main.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry-samples/sentry-samples-console/src/main/java/io/sentry/samples/console/Main.java b/sentry-samples/sentry-samples-console/src/main/java/io/sentry/samples/console/Main.java index 7ee46649e97..86ab6a54f39 100644 --- a/sentry-samples/sentry-samples-console/src/main/java/io/sentry/samples/console/Main.java +++ b/sentry-samples/sentry-samples-console/src/main/java/io/sentry/samples/console/Main.java @@ -66,8 +66,8 @@ public static void main(String[] args) throws InterruptedException { // Enable SDK logging with Debug level options.setDebug(true); // To change the verbosity, use: - // By default it's DEBUG. // options.setDiagnosticLevel(SentryLevel.ERROR); + // By default it's DEBUG. // A good option to have SDK debug log in prod is to use only level ERROR here. // Exclude frames from some packages from being "inApp" so are hidden by default in Sentry From 98dbe4d2d7bd18e715a302461fbe2f5bad050751 Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 14 Jul 2025 15:42:36 +0200 Subject: [PATCH 06/17] improve --- test/system-test-runner.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/system-test-runner.py b/test/system-test-runner.py index 3a3f1283d8a..80d01ebcf58 100644 --- a/test/system-test-runner.py +++ b/test/system-test-runner.py @@ -503,16 +503,16 @@ def run_all_tests(self) -> int: failed_tests = [] - for module_config in test_configs: + for i, module_config in enumerate(test_configs): # Convert true/false to internal 1/0 format agent = str_to_bool(module_config.java_agent) auto_init = module_config.java_agent_auto_init # already in correct format build = str_to_bool(module_config.build_before_run) - print(f"\n{'='*60}") + print(f"\n{'='*80}") print( - f"Running test: {module_config.module} (agent={module_config.java_agent}, auto_init={module_config.java_agent_auto_init})") - print(f"{'='*60}") + f"Running test {i + 1}/{len(test_configs)}: {module_config.module} (agent={module_config.java_agent}, auto_init={module_config.java_agent_auto_init})") + print(f"{'='*80}") result = self.run_single_test( module_config.module, agent, auto_init, build) From 576424b7afb054a360a7fea10ac092582554d667 Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 14 Jul 2025 15:43:08 +0200 Subject: [PATCH 07/17] improve --- test/system-test-runner.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/system-test-runner.py b/test/system-test-runner.py index 80d01ebcf58..62c2f7d58e8 100644 --- a/test/system-test-runner.py +++ b/test/system-test-runner.py @@ -528,9 +528,9 @@ def run_all_tests(self) -> int: print(f"✅ Test passed: {module_config.module}") # Summary - print(f"\n{'='*60}") + print(f"\n{'='*80}") print("TEST SUMMARY") - print(f"{'='*60}") + print(f"{'='*80}") print(f"Total tests: {len(test_configs)}") print(f"Passed: {len(test_configs) - len(failed_tests)}") print(f"Failed: {len(failed_tests)}") @@ -558,9 +558,9 @@ def run_manual_test_mode(self, sample_module: str, java_agent: str, return setup_result # Show status and wait for user - print("\n" + "="*60) + print("\n" + "="*80) print("🚀 Manual test environment ready 🚀") - print("="*60) + print("="*80) self.print_status_summary() print( f"\nInfrastructure is ready for manual testing of: {sample_module}") @@ -798,12 +798,12 @@ def run_interactive_tests(self, agent: str, auto_init: str, build: str) -> int: auto_init = module_config.java_agent_auto_init # already in correct format build = str_to_bool(module_config.build_before_run) - print(f"\n{'='*60}") + print(f"\n{'='*80}") print( f"Running test {i}/{len(selection.modules)}: {module_config.module}") print( f"Agent: {module_config.java_agent}, Auto-init: {module_config.java_agent_auto_init}") - print(f"{'='*60}") + print(f"{'='*80}") result = self.run_single_test( module_config.module, agent, auto_init, build) @@ -819,9 +819,9 @@ def run_interactive_tests(self, agent: str, auto_init: str, build: str) -> int: print(f"✅ Test passed: {module_config.sample_module}") # Summary - print(f"\n{'='*60}") + print(f"\n{'='*80}") print("TEST SUMMARY") - print(f"{'='*60}") + print(f"{'='*80}") print(f"Total tests: {len(selection.modules)}") print(f"Passed: {len(selection.modules) - len(failed_tests)}") print(f"Failed: {len(failed_tests)}") From db28d2a3a27d20029486887ead26ae753293b4ec Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 14 Jul 2025 15:46:42 +0200 Subject: [PATCH 08/17] improve --- test/system-test-runner.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/test/system-test-runner.py b/test/system-test-runner.py index 62c2f7d58e8..f38ad036469 100644 --- a/test/system-test-runner.py +++ b/test/system-test-runner.py @@ -42,6 +42,8 @@ from typing import Optional, List from dataclasses import dataclass +TERMINAL_COLUMNS = os.get_terminal_size()[0] + def str_to_bool(value: str) -> str: """Convert true/false string to 1/0 string for internal compatibility.""" @@ -509,10 +511,10 @@ def run_all_tests(self) -> int: auto_init = module_config.java_agent_auto_init # already in correct format build = str_to_bool(module_config.build_before_run) - print(f"\n{'='*80}") + print(f"\n{'='*TERMINAL_COLUMNS}") print( f"Running test {i + 1}/{len(test_configs)}: {module_config.module} (agent={module_config.java_agent}, auto_init={module_config.java_agent_auto_init})") - print(f"{'='*80}") + print(f"{'='*TERMINAL_COLUMNS}") result = self.run_single_test( module_config.module, agent, auto_init, build) @@ -528,9 +530,9 @@ def run_all_tests(self) -> int: print(f"✅ Test passed: {module_config.module}") # Summary - print(f"\n{'='*80}") + print(f"\n{'='*TERMINAL_COLUMNS}") print("TEST SUMMARY") - print(f"{'='*80}") + print(f"{'='*TERMINAL_COLUMNS}") print(f"Total tests: {len(test_configs)}") print(f"Passed: {len(test_configs) - len(failed_tests)}") print(f"Failed: {len(failed_tests)}") @@ -558,9 +560,9 @@ def run_manual_test_mode(self, sample_module: str, java_agent: str, return setup_result # Show status and wait for user - print("\n" + "="*80) + print("\n" + "="*TERMINAL_COLUMNS) print("🚀 Manual test environment ready 🚀") - print("="*80) + print("="*TERMINAL_COLUMNS) self.print_status_summary() print( f"\nInfrastructure is ready for manual testing of: {sample_module}") @@ -668,7 +670,7 @@ def interactive_module_selection(self) -> InteractiveSelection: modules = self.get_available_modules() print("\nAvailable test modules:") - print("=" * 80) + print("=" * TERMINAL_COLUMNS) for i, module_config in enumerate(modules, 1): agent_text = "with agent" if module_config.uses_agent() else "no agent" auto_init_text = f"auto-init: {module_config.java_agent_auto_init}" @@ -798,12 +800,12 @@ def run_interactive_tests(self, agent: str, auto_init: str, build: str) -> int: auto_init = module_config.java_agent_auto_init # already in correct format build = str_to_bool(module_config.build_before_run) - print(f"\n{'='*80}") + print(f"\n{'='*TERMINAL_COLUMNS}") print( f"Running test {i}/{len(selection.modules)}: {module_config.module}") print( f"Agent: {module_config.java_agent}, Auto-init: {module_config.java_agent_auto_init}") - print(f"{'='*80}") + print(f"{'='*TERMINAL_COLUMNS}") result = self.run_single_test( module_config.module, agent, auto_init, build) @@ -819,9 +821,9 @@ def run_interactive_tests(self, agent: str, auto_init: str, build: str) -> int: print(f"✅ Test passed: {module_config.sample_module}") # Summary - print(f"\n{'='*80}") + print(f"\n{'='*TERMINAL_COLUMNS}") print("TEST SUMMARY") - print(f"{'='*80}") + print(f"{'='*TERMINAL_COLUMNS}") print(f"Total tests: {len(selection.modules)}") print(f"Passed: {len(selection.modules) - len(failed_tests)}") print(f"Failed: {len(failed_tests)}") From 4c585f6e720bed0b0c0893473cf508f6c6663401 Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 14 Jul 2025 18:41:04 +0200 Subject: [PATCH 09/17] improve --- test/system-test-runner.py | 267 +++++++++++++------------------------ 1 file changed, 93 insertions(+), 174 deletions(-) diff --git a/test/system-test-runner.py b/test/system-test-runner.py index f38ad036469..7cd646752db 100644 --- a/test/system-test-runner.py +++ b/test/system-test-runner.py @@ -42,8 +42,7 @@ from typing import Optional, List from dataclasses import dataclass -TERMINAL_COLUMNS = os.get_terminal_size()[0] - +TERMINAL_COLUMNS: int = os.get_terminal_size()[0] or 60 def str_to_bool(value: str) -> str: """Convert true/false string to 1/0 string for internal compatibility.""" @@ -52,14 +51,12 @@ def str_to_bool(value: str) -> str: elif value.lower() in ('false', '0'): return "0" else: - raise ValueError( - f"Invalid boolean value: {value}. Use 'true' or 'false'") - + raise ValueError(f"Invalid boolean value: {value}. Use 'true' or 'false'") @dataclass class ModuleConfig: - """Configuration for running a sample module.""" - module: str + """Configuration for running a test module.""" + sample_module: str java_agent: str java_agent_auto_init: str build_before_run: str @@ -72,7 +69,6 @@ def needs_build(self) -> bool: """Check if this module configuration needs building before run.""" return str_to_bool(self.build_before_run) == "1" - @dataclass class InteractiveSelection: """Result of interactive module selection.""" @@ -98,7 +94,6 @@ def has_agent_modules(self) -> bool: """Check if any selected modules use the Java agent.""" return any(module.uses_agent() for module in self.modules) - class SystemTestRunner: def __init__(self): self.mock_server_process: Optional[subprocess.Popen] = None @@ -110,14 +105,12 @@ def __init__(self): # Load existing PIDs if available self.mock_server_pid = self.read_pid_file(self.mock_server_pid_file) - self.spring_server_pid = self.read_pid_file( - self.spring_server_pid_file) + self.spring_server_pid = self.read_pid_file(self.spring_server_pid_file) if self.mock_server_pid: print(f"Found existing mock server PID: {self.mock_server_pid}") if self.spring_server_pid: - print( - f"Found existing Spring server PID: {self.spring_server_pid}") + print(f"Found existing Spring server PID: {self.spring_server_pid}") def read_pid_file(self, pid_file: str) -> Optional[int]: """Read PID from file if it exists.""" @@ -147,13 +140,14 @@ def kill_process(self, pid: int, name: str) -> None: # Check if it's still running and force kill if necessary if self.is_process_running(pid): - print( - f"Process {pid} didn't terminate gracefully, force killing...") + print(f"Process {pid} didn't terminate gracefully, force killing...") os.kill(pid, signal.SIGKILL) time.sleep(1) except (OSError, ProcessLookupError): print(f"Process {pid} was already dead") + + def start_sentry_mock_server(self) -> None: """Start the Sentry mock server.""" print("Starting Sentry mock server...") @@ -185,21 +179,18 @@ def stop_sentry_mock_server(self) -> None: try: # Try graceful shutdown first try: - response = requests.get( - "http://127.0.0.1:8000/STOP", timeout=5) + response = requests.get("http://127.0.0.1:8000/STOP", timeout=5) print("Sent stop signal to mock server") except: print("Could not send graceful stop signal") # Kill the process - try process object first, then PID from file if self.mock_server_process and self.mock_server_process.poll() is None: - print( - f"Killing mock server process object with PID {self.mock_server_process.pid}") + print(f"Killing mock server process object with PID {self.mock_server_process.pid}") self.mock_server_process.kill() self.mock_server_process.wait(timeout=5) elif self.mock_server_pid and self.is_process_running(self.mock_server_pid): - print( - f"Killing mock server from PID file with PID {self.mock_server_pid}") + print(f"Killing mock server from PID file with PID {self.mock_server_pid}") self.kill_process(self.mock_server_pid, "mock server") except Exception as e: @@ -212,8 +203,7 @@ def stop_sentry_mock_server(self) -> None: def find_agent_jar(self) -> Optional[str]: """Find the OpenTelemetry agent JAR file.""" - agent_dir = Path( - "sentry-opentelemetry/sentry-opentelemetry-agent/build/libs/") + agent_dir = Path("sentry-opentelemetry/sentry-opentelemetry-agent/build/libs/") if not agent_dir.exists(): return None @@ -222,7 +212,7 @@ def find_agent_jar(self) -> Optional[str]: if ("agent" in name and "javadoc" not in name and "sources" not in name and - "dontuse" not in name): + "dontuse" not in name): return str(jar_file) return None @@ -282,8 +272,7 @@ def start_spring_server(self, sample_module: str, java_agent: str, java_agent_au cmd.append(f"-javaagent:{agent_jar}") print(f"Using Java Agent: {agent_jar}") else: - print( - "Warning: Java agent was requested but could not be found or built") + print("Warning: Java agent was requested but could not be found or built") cmd.extend(["-jar", jar_path]) @@ -369,8 +358,7 @@ def get_sentry_status(self) -> dict: # Check HTTP endpoint try: - response = requests.get( - "http://127.0.0.1:8000/envelope-count", timeout=2) + response = requests.get("http://127.0.0.1:8000/envelope-count", timeout=2) if response.status_code == 200: status["http_ready"] = True except: @@ -385,15 +373,13 @@ def print_status_summary(self) -> None: sentry_status = self.get_sentry_status() print(f"Sentry Mock Server:") print(f" PID: {sentry_status['pid'] or 'None'}") - print( - f" Process Running: {'✅' if sentry_status['process_running'] else '❌'}") + print(f" Process Running: {'✅' if sentry_status['process_running'] else '❌'}") print(f" HTTP Ready: {'✅' if sentry_status['http_ready'] else '❌'}") spring_status = self.get_spring_status() print(f"Spring Boot App:") print(f" PID: {spring_status['pid'] or 'None'}") - print( - f" Process Running: {'✅' if spring_status['process_running'] else '❌'}") + print(f" Process Running: {'✅' if spring_status['process_running'] else '❌'}") print(f" HTTP Ready: {'✅' if spring_status['http_ready'] else '❌'}") def stop_spring_server(self) -> None: @@ -401,16 +387,14 @@ def stop_spring_server(self) -> None: try: # Kill the process - try process object first, then PID from file if self.spring_server_process and self.spring_server_process.poll() is None: - print( - f"Killing Spring server process object with PID {self.spring_server_process.pid}") + print(f"Killing Spring server process object with PID {self.spring_server_process.pid}") self.spring_server_process.kill() try: self.spring_server_process.wait(timeout=10) except subprocess.TimeoutExpired: print("Spring server did not terminate gracefully") elif self.spring_server_pid and self.is_process_running(self.spring_server_pid): - print( - f"Killing Spring server from PID file with PID {self.spring_server_pid}") + print(f"Killing Spring server from PID file with PID {self.spring_server_pid}") self.kill_process(self.spring_server_pid, "Spring server") except Exception as e: @@ -442,7 +426,7 @@ def run_gradle_task(self, task: str) -> int: return 1 def setup_test_infrastructure(self, sample_module: str, java_agent: str, - java_agent_auto_init: str, build_before_run: str) -> int: + java_agent_auto_init: str, build_before_run: str) -> int: """Set up test infrastructure. Returns 0 on success, error code on failure.""" # Build if requested if build_before_run == "1": @@ -466,8 +450,7 @@ def setup_test_infrastructure(self, sample_module: str, java_agent: str, # Start Spring server if it's a Spring module if "spring" in sample_module: print(f"Starting Spring server for {sample_module}...") - self.start_spring_server( - sample_module, java_agent, java_agent_auto_init) + self.start_spring_server(sample_module, java_agent, java_agent_auto_init) if not self.wait_for_spring(): print("Spring application failed to start!") return 1 @@ -476,20 +459,18 @@ def setup_test_infrastructure(self, sample_module: str, java_agent: str, return 0 def run_single_test(self, sample_module: str, java_agent: str, - java_agent_auto_init: str, build_before_run: str) -> int: + java_agent_auto_init: str, build_before_run: str) -> int: """Run a single system test.""" print(f"Running system test for {sample_module}") try: # Set up infrastructure - setup_result = self.setup_test_infrastructure( - sample_module, java_agent, java_agent_auto_init, build_before_run) + setup_result = self.setup_test_infrastructure(sample_module, java_agent, java_agent_auto_init, build_before_run) if setup_result != 0: return setup_result # Run the system test - test_result = self.run_gradle_task( - f":sentry-samples:{sample_module}:systemTest") + test_result = self.run_gradle_task(f":sentry-samples:{sample_module}:systemTest") return test_result @@ -505,29 +486,25 @@ def run_all_tests(self) -> int: failed_tests = [] - for i, module_config in enumerate(test_configs): + for module_config in test_configs: # Convert true/false to internal 1/0 format agent = str_to_bool(module_config.java_agent) auto_init = module_config.java_agent_auto_init # already in correct format build = str_to_bool(module_config.build_before_run) print(f"\n{'='*TERMINAL_COLUMNS}") - print( - f"Running test {i + 1}/{len(test_configs)}: {module_config.module} (agent={module_config.java_agent}, auto_init={module_config.java_agent_auto_init})") + print(f"Running test: {module_config.sample_module} (agent={module_config.java_agent}, auto_init={module_config.java_agent_auto_init})") print(f"{'='*TERMINAL_COLUMNS}") - result = self.run_single_test( - module_config.module, agent, auto_init, build) + result = self.run_single_test(module_config.sample_module, agent, auto_init, build) if result != 0: # Find the module number in the full list for interactive reference - module_number = self._find_module_number( - module_config.module, module_config.java_agent, module_config.java_agent_auto_init) - failed_tests.append((module_number, module_config.module, - module_config.java_agent, module_config.java_agent_auto_init)) - print(f"❌ Test failed: {module_config.module}") + module_number = self._find_module_number(module_config.sample_module, module_config.java_agent, module_config.java_agent_auto_init) + failed_tests.append((module_number, module_config.sample_module, module_config.java_agent, module_config.java_agent_auto_init)) + print(f"❌ Test failed: {module_config.sample_module}") else: - print(f"✅ Test passed: {module_config.module}") + print(f"✅ Test passed: {module_config.sample_module}") # Summary print(f"\n{'='*TERMINAL_COLUMNS}") @@ -540,22 +517,20 @@ def run_all_tests(self) -> int: if failed_tests: print("\nFailed tests (for interactive mode, use these numbers):") for module_number, sample_module, java_agent, java_agent_auto_init in failed_tests: - print( - f" {module_number}. {sample_module} (agent={java_agent}, auto_init={java_agent_auto_init})") + print(f" {module_number}. {sample_module} (agent={java_agent}, auto_init={java_agent_auto_init})") return 1 else: print("\n🎉 All tests passed!") return 0 def run_manual_test_mode(self, sample_module: str, java_agent: str, - java_agent_auto_init: str, build_before_run: str) -> int: + java_agent_auto_init: str, build_before_run: str) -> int: """Set up infrastructure for manual testing from IDE.""" print(f"Setting up manual test environment for {sample_module}") try: # Set up infrastructure - setup_result = self.setup_test_infrastructure( - sample_module, java_agent, java_agent_auto_init, build_before_run) + setup_result = self.setup_test_infrastructure(sample_module, java_agent, java_agent_auto_init, build_before_run) if setup_result != 0: return setup_result @@ -564,15 +539,13 @@ def run_manual_test_mode(self, sample_module: str, java_agent: str, print("🚀 Manual test environment ready 🚀") print("="*TERMINAL_COLUMNS) self.print_status_summary() - print( - f"\nInfrastructure is ready for manual testing of: {sample_module}") + print(f"\nInfrastructure is ready for manual testing of: {sample_module}") print("You can now run your system tests from your IDE.") print("\nTest configuration:") print(f" - Module: {sample_module}") print(f" - Java Agent: {'Yes' if java_agent == '1' else 'No'}") print(f" - Agent Auto-init: {java_agent_auto_init}") - print( - f" - Mock DSN: http://502f25099c204a2fbf4cb16edc5975d1@localhost:8000/0") + print(f" - Mock DSN: http://502f25099c204a2fbf4cb16edc5975d1@localhost:8000/0") if "spring" in sample_module: print("\nSpring Boot app is running on: http://localhost:8080") @@ -595,26 +568,16 @@ def run_manual_test_mode(self, sample_module: str, java_agent: str, def get_available_modules(self) -> List[ModuleConfig]: """Get list of all available test modules.""" return [ - ModuleConfig("sentry-samples-spring-boot", - "false", "true", "false"), - ModuleConfig("sentry-samples-spring-boot-opentelemetry-noagent", - "false", "true", "false"), - ModuleConfig("sentry-samples-spring-boot-opentelemetry", - "true", "true", "false"), - ModuleConfig("sentry-samples-spring-boot-opentelemetry", - "true", "false", "false"), - ModuleConfig("sentry-samples-spring-boot-webflux-jakarta", - "false", "true", "false"), - ModuleConfig("sentry-samples-spring-boot-webflux", - "false", "true", "false"), - ModuleConfig("sentry-samples-spring-boot-jakarta", - "false", "true", "false"), - ModuleConfig( - "sentry-samples-spring-boot-jakarta-opentelemetry-noagent", "false", "true", "false"), - ModuleConfig( - "sentry-samples-spring-boot-jakarta-opentelemetry", "true", "true", "false"), - ModuleConfig("sentry-samples-spring-boot-jakarta-opentelemetry", - "true", "false", "false"), + ModuleConfig("sentry-samples-spring-boot", "false", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-opentelemetry-noagent", "false", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-opentelemetry", "true", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-opentelemetry", "true", "false", "false"), + ModuleConfig("sentry-samples-spring-boot-webflux-jakarta", "false", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-webflux", "false", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-jakarta", "false", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-jakarta-opentelemetry-noagent", "false", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-jakarta-opentelemetry", "true", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-jakarta-opentelemetry", "true", "false", "false"), ModuleConfig("sentry-samples-console", "false", "true", "false"), ] @@ -624,7 +587,7 @@ def _find_module_number(self, module_name: str, agent: str, auto_init: str) -> i for i, module_config in enumerate(modules, 1): if (module_config.sample_module == module_name and module_config.java_agent == agent and - module_config.java_agent_auto_init == auto_init): + module_config.java_agent_auto_init == auto_init): return i return 0 # Should not happen, but return 0 if not found @@ -660,8 +623,7 @@ def parse_selection(self, user_input: str, max_index: int) -> List[int]: # Validate indices for idx in indices: if idx < 0 or idx >= max_index: - raise ValueError( - f"Index {idx + 1} is out of range (1-{max_index})") + raise ValueError(f"Index {idx + 1} is out of range (1-{max_index})") return indices @@ -670,12 +632,11 @@ def interactive_module_selection(self) -> InteractiveSelection: modules = self.get_available_modules() print("\nAvailable test modules:") - print("=" * TERMINAL_COLUMNS) + print("=" * 80) for i, module_config in enumerate(modules, 1): agent_text = "with agent" if module_config.uses_agent() else "no agent" auto_init_text = f"auto-init: {module_config.java_agent_auto_init}" - print( - f"{i:2d}. {module_config.sample_module:<50} ({agent_text}, {auto_init_text})") + print(f"{i:2d}. {module_config.sample_module:<50} ({agent_text}, {auto_init_text})") print("\nSelection options:") print(" * = all modules") @@ -691,19 +652,16 @@ def interactive_module_selection(self) -> InteractiveSelection: print("Please enter a selection.") continue - selected_indices = self.parse_selection( - user_input, len(modules)) + selected_indices = self.parse_selection(user_input, len(modules)) selected_modules = [modules[i] for i in selected_indices] # Show confirmation print(f"\nSelected {len(selected_modules)} module(s):") for i, module_config in enumerate(selected_modules, 1): agent_text = "with agent" if module_config.uses_agent() else "no agent" - print( - f" {i}. {module_config.sample_module} ({agent_text}, auto-init: {module_config.java_agent_auto_init})") + print(f" {i}. {module_config.sample_module} ({agent_text}, auto-init: {module_config.java_agent_auto_init})") - confirm = input( - "\nProceed with these selections? [Y/n]: ").strip().lower() + confirm = input("\nProceed with these selections? [Y/n]: ").strip().lower() if confirm in ('', 'y', 'yes'): break else: @@ -720,8 +678,7 @@ def interactive_module_selection(self) -> InteractiveSelection: manual_test_mode = False while True: try: - mode_input = input( - "\nRun tests automatically (n = only set up infrastucture for testing in IDE)? [Y/n]: ").strip().lower() + mode_input = input("\nRun tests automatically (n = only set up infrastucture for testing in IDE)? [Y/n]: ").strip().lower() if not mode_input or mode_input in ('y', 'yes'): manual_test_mode = False break @@ -736,13 +693,11 @@ def interactive_module_selection(self) -> InteractiveSelection: # Ask about building agent if any modules use it build_agent = False - has_agent_modules = any(module_config.uses_agent() - for module_config in selected_modules) + has_agent_modules = any(module_config.uses_agent() for module_config in selected_modules) if has_agent_modules: while True: try: - agent_input = input( - "\nBuild OpenTelemetry agent JAR (recommended to ensure latest version)? [Y/n]: ").strip().lower() + agent_input = input("\nBuild OpenTelemetry agent JAR (recommended to ensure latest version)? [Y/n]: ").strip().lower() if not agent_input or agent_input in ('y', 'yes'): build_agent = True break @@ -787,9 +742,8 @@ def run_interactive_tests(self, agent: str, auto_init: str, build: str) -> int: auto_init = module_config.java_agent_auto_init # already in correct format build = str_to_bool(module_config.build_before_run) - print( - f"\nSetting up manual test environment for: {module_config.module}") - return self.run_manual_test_mode(module_config.module, agent, auto_init, build) + print(f"\nSetting up manual test environment for: {module_config.sample_module}") + return self.run_manual_test_mode(module_config.sample_module, agent, auto_init, build) # Handle automatic test running failed_tests = [] @@ -801,21 +755,16 @@ def run_interactive_tests(self, agent: str, auto_init: str, build: str) -> int: build = str_to_bool(module_config.build_before_run) print(f"\n{'='*TERMINAL_COLUMNS}") - print( - f"Running test {i}/{len(selection.modules)}: {module_config.module}") - print( - f"Agent: {module_config.java_agent}, Auto-init: {module_config.java_agent_auto_init}") + print(f"Running test {i}/{len(selection.modules)}: {module_config.sample_module}") + print(f"Agent: {module_config.java_agent}, Auto-init: {module_config.java_agent_auto_init}") print(f"{'='*TERMINAL_COLUMNS}") - result = self.run_single_test( - module_config.module, agent, auto_init, build) + result = self.run_single_test(module_config.sample_module, agent, auto_init, build) if result != 0: # Find the module number in the full list for interactive reference - module_number = self._find_module_number( - module_config.module, module_config.java_agent, module_config.java_agent_auto_init) - failed_tests.append((module_number, module_config.module, - module_config.java_agent, module_config.java_agent_auto_init)) + module_number = self._find_module_number(module_config.sample_module, module_config.java_agent, module_config.java_agent_auto_init) + failed_tests.append((module_number, module_config.sample_module, module_config.java_agent, module_config.java_agent_auto_init)) print(f"❌ Test failed: {module_config.sample_module}") else: print(f"✅ Test passed: {module_config.sample_module}") @@ -831,8 +780,7 @@ def run_interactive_tests(self, agent: str, auto_init: str, build: str) -> int: if failed_tests: print("\nFailed tests (for interactive mode, use these numbers):") for module_number, sample_module, test_agent, test_auto_init in failed_tests: - print( - f" {module_number}. {sample_module} (agent={test_agent}, auto_init={test_auto_init})") + print(f" {module_number}. {sample_module} (agent={test_agent}, auto_init={test_auto_init})") return 1 else: print("\n🎉 All tests passed!") @@ -845,73 +793,48 @@ def cleanup_on_exit(self, signum, frame): self.stop_sentry_mock_server() sys.exit(1) - def main(): - parser = argparse.ArgumentParser( - description="System Test Runner for Sentry Java") - subparsers = parser.add_subparsers( - dest="command", help="Available commands") + parser = argparse.ArgumentParser(description="System Test Runner for Sentry Java") + subparsers = parser.add_subparsers(dest="command", help="Available commands") # Test subcommand test_parser = subparsers.add_parser("test", help="Run system tests") test_group = test_parser.add_mutually_exclusive_group(required=True) - test_group.add_argument("--all", action="store_true", - help="Run all system tests") + test_group.add_argument("--all", action="store_true", help="Run all system tests") test_group.add_argument("--module", help="Sample module to test") - test_group.add_argument( - "--interactive", "-i", action="store_true", help="Interactive module selection") - test_parser.add_argument("--agent", default="false", - help="Use Java agent (true or false)") - test_parser.add_argument( - "--auto-init", default="true", help="Auto-init agent (true or false)") - test_parser.add_argument("--build", default="false", - help="Build before running (true or false)") - test_parser.add_argument("--manual-test", action="store_true", - help="Set up infrastructure but pause for manual testing from IDE") + test_group.add_argument("--interactive", "-i", action="store_true", help="Interactive module selection") + test_parser.add_argument("--agent", default="false", help="Use Java agent (true or false)") + test_parser.add_argument("--auto-init", default="true", help="Auto-init agent (true or false)") + test_parser.add_argument("--build", default="false", help="Build before running (true or false)") + test_parser.add_argument("--manual-test", action="store_true", help="Set up infrastructure but pause for manual testing from IDE") # Spring subcommand - spring_parser = subparsers.add_parser( - "spring", help="Manage Spring Boot applications") - spring_subparsers = spring_parser.add_subparsers( - dest="spring_action", help="Spring actions") + spring_parser = subparsers.add_parser("spring", help="Manage Spring Boot applications") + spring_subparsers = spring_parser.add_subparsers(dest="spring_action", help="Spring actions") - spring_start_parser = spring_subparsers.add_parser( - "start", help="Start Spring Boot application") + spring_start_parser = spring_subparsers.add_parser("start", help="Start Spring Boot application") spring_start_parser.add_argument("module", help="Sample module to start") - spring_start_parser.add_argument( - "--agent", default="false", help="Use Java agent (true or false)") - spring_start_parser.add_argument( - "--auto-init", default="true", help="Auto-init agent (true or false)") - spring_start_parser.add_argument( - "--build", default="false", help="Build before starting (true or false)") + spring_start_parser.add_argument("--agent", default="false", help="Use Java agent (true or false)") + spring_start_parser.add_argument("--auto-init", default="true", help="Auto-init agent (true or false)") + spring_start_parser.add_argument("--build", default="false", help="Build before starting (true or false)") - spring_stop_parser = spring_subparsers.add_parser( - "stop", help="Stop Spring Boot application") + spring_stop_parser = spring_subparsers.add_parser("stop", help="Stop Spring Boot application") - spring_wait_parser = spring_subparsers.add_parser( - "wait", help="Wait for Spring Boot application to be ready") - spring_wait_parser.add_argument( - "--timeout", type=int, default=20, help="Max attempts to wait (default: 20)") + spring_wait_parser = spring_subparsers.add_parser("wait", help="Wait for Spring Boot application to be ready") + spring_wait_parser.add_argument("--timeout", type=int, default=20, help="Max attempts to wait (default: 20)") - spring_status_parser = spring_subparsers.add_parser( - "status", help="Check Spring Boot application status") + spring_status_parser = spring_subparsers.add_parser("status", help="Check Spring Boot application status") # Sentry subcommand - sentry_parser = subparsers.add_parser( - "sentry", help="Manage Sentry mock server") - sentry_subparsers = sentry_parser.add_subparsers( - dest="sentry_action", help="Sentry actions") - - sentry_start_parser = sentry_subparsers.add_parser( - "start", help="Start Sentry mock server") - sentry_stop_parser = sentry_subparsers.add_parser( - "stop", help="Stop Sentry mock server") - sentry_status_parser = sentry_subparsers.add_parser( - "status", help="Check Sentry mock server status") + sentry_parser = subparsers.add_parser("sentry", help="Manage Sentry mock server") + sentry_subparsers = sentry_parser.add_subparsers(dest="sentry_action", help="Sentry actions") + + sentry_start_parser = sentry_subparsers.add_parser("start", help="Start Sentry mock server") + sentry_stop_parser = sentry_subparsers.add_parser("stop", help="Stop Sentry mock server") + sentry_status_parser = sentry_subparsers.add_parser("status", help="Check Sentry mock server status") # Status subcommand - status_parser = subparsers.add_parser( - "status", help="Show status of all services") + status_parser = subparsers.add_parser("status", help="Show status of all services") args = parser.parse_args() @@ -935,8 +858,7 @@ def main(): if args.manual_test and args.module: return runner.run_manual_test_mode(args.module, agent, auto_init, build) elif args.manual_test and args.all: - print( - "Error: --manual-test requires a specific --module, cannot be used with --all") + print("Error: --manual-test requires a specific --module, cannot be used with --all") return 1 elif args.manual_test and args.interactive: print("Error: --manual-test cannot be used with --interactive") @@ -985,8 +907,7 @@ def main(): status = runner.get_spring_status() print(f"Spring Boot Application Status:") print(f" PID: {status['pid'] or 'None'}") - print( - f" Process Running: {'✅' if status['process_running'] else '❌'}") + print(f" Process Running: {'✅' if status['process_running'] else '❌'}") print(f" HTTP Ready: {'✅' if status['http_ready'] else '❌'}") return 0 if (status['process_running'] and status['http_ready']) else 1 else: @@ -1006,8 +927,7 @@ def main(): status = runner.get_sentry_status() print(f"Sentry Mock Server Status:") print(f" PID: {status['pid'] or 'None'}") - print( - f" Process Running: {'✅' if status['process_running'] else '❌'}") + print(f" Process Running: {'✅' if status['process_running'] else '❌'}") print(f" HTTP Ready: {'✅' if status['http_ready'] else '❌'}") return 0 if (status['process_running'] and status['http_ready']) else 1 else: @@ -1033,6 +953,5 @@ def main(): runner.stop_spring_server() runner.stop_sentry_mock_server() - if __name__ == "__main__": sys.exit(main()) From ae12d639553d2d3bebaf39a50d118b6042037004 Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 14 Jul 2025 18:43:20 +0200 Subject: [PATCH 10/17] improve --- test/system-test-runner.py | 434 ++++++++++++++++++------------------- 1 file changed, 207 insertions(+), 227 deletions(-) diff --git a/test/system-test-runner.py b/test/system-test-runner.py index 7cd646752db..aed0d7126a2 100644 --- a/test/system-test-runner.py +++ b/test/system-test-runner.py @@ -39,11 +39,9 @@ import requests import threading from pathlib import Path -from typing import Optional, List +from typing import Optional, List, Tuple from dataclasses import dataclass -TERMINAL_COLUMNS: int = os.get_terminal_size()[0] or 60 - def str_to_bool(value: str) -> str: """Convert true/false string to 1/0 string for internal compatibility.""" if value.lower() in ('true', '1'): @@ -53,46 +51,30 @@ def str_to_bool(value: str) -> str: else: raise ValueError(f"Invalid boolean value: {value}. Use 'true' or 'false'") -@dataclass -class ModuleConfig: - """Configuration for running a test module.""" - sample_module: str - java_agent: str - java_agent_auto_init: str - build_before_run: str - - def uses_agent(self) -> bool: - """Check if this module configuration uses the Java agent.""" - return str_to_bool(self.java_agent) == "1" - - def needs_build(self) -> bool: - """Check if this module configuration needs building before run.""" - return str_to_bool(self.build_before_run) == "1" - @dataclass class InteractiveSelection: """Result of interactive module selection.""" - modules: List[ModuleConfig] + modules: List[Tuple[str, str, str, str]] manual_test_mode: bool build_agent: bool - + def is_empty(self) -> bool: """Check if no modules were selected.""" return len(self.modules) == 0 - + def is_single_module(self) -> bool: """Check if exactly one module was selected.""" return len(self.modules) == 1 - - def get_first_module(self) -> ModuleConfig: + + def get_first_module(self) -> Tuple[str, str, str, str]: """Get the first selected module (for manual test mode).""" if self.is_empty(): raise ValueError("No modules selected") return self.modules[0] - + def has_agent_modules(self) -> bool: """Check if any selected modules use the Java agent.""" - return any(module.uses_agent() for module in self.modules) + return any(str_to_bool(agent) == "1" for _, agent, _, _ in self.modules) class SystemTestRunner: def __init__(self): @@ -102,16 +84,16 @@ def __init__(self): self.spring_server_pid: Optional[int] = None self.mock_server_pid_file = "sentry-mock-server.pid" self.spring_server_pid_file = "spring-server.pid" - + # Load existing PIDs if available self.mock_server_pid = self.read_pid_file(self.mock_server_pid_file) self.spring_server_pid = self.read_pid_file(self.spring_server_pid_file) - + if self.mock_server_pid: print(f"Found existing mock server PID: {self.mock_server_pid}") if self.spring_server_pid: print(f"Found existing Spring server PID: {self.spring_server_pid}") - + def read_pid_file(self, pid_file: str) -> Optional[int]: """Read PID from file if it exists.""" try: @@ -121,7 +103,7 @@ def read_pid_file(self, pid_file: str) -> Optional[int]: except (ValueError, IOError) as e: print(f"Error reading PID file {pid_file}: {e}") return None - + def is_process_running(self, pid: int) -> bool: """Check if a process with given PID is still running.""" try: @@ -130,14 +112,14 @@ def is_process_running(self, pid: int) -> bool: return True except (OSError, ProcessLookupError): return False - + def kill_process(self, pid: int, name: str) -> None: """Kill a process by PID.""" try: print(f"Killing existing {name} process with PID {pid}") os.kill(pid, signal.SIGTERM) time.sleep(2) # Give it time to terminate gracefully - + # Check if it's still running and force kill if necessary if self.is_process_running(pid): print(f"Process {pid} didn't terminate gracefully, force killing...") @@ -145,9 +127,9 @@ def kill_process(self, pid: int, name: str) -> None: time.sleep(1) except (OSError, ProcessLookupError): print(f"Process {pid} was already dead") + - - + def start_sentry_mock_server(self) -> None: """Start the Sentry mock server.""" print("Starting Sentry mock server...") @@ -159,21 +141,21 @@ def start_sentry_mock_server(self) -> None: stdout=log_file, stderr=subprocess.STDOUT ) - + # Store PID in instance variable and write to file self.mock_server_pid = self.mock_server_process.pid with open(self.mock_server_pid_file, "w") as pid_file: pid_file.write(str(self.mock_server_pid)) - + print(f"Started mock server with PID {self.mock_server_pid}") - + # Wait a moment for the server to start time.sleep(2) - + except Exception as e: print(f"Failed to start mock server: {e}") raise - + def stop_sentry_mock_server(self) -> None: """Stop the Sentry mock server.""" try: @@ -183,7 +165,7 @@ def stop_sentry_mock_server(self) -> None: print("Sent stop signal to mock server") except: print("Could not send graceful stop signal") - + # Kill the process - try process object first, then PID from file if self.mock_server_process and self.mock_server_process.poll() is None: print(f"Killing mock server process object with PID {self.mock_server_process.pid}") @@ -192,7 +174,7 @@ def stop_sentry_mock_server(self) -> None: elif self.mock_server_pid and self.is_process_running(self.mock_server_pid): print(f"Killing mock server from PID file with PID {self.mock_server_pid}") self.kill_process(self.mock_server_pid, "mock server") - + except Exception as e: print(f"Error stopping mock server: {e}") finally: @@ -200,56 +182,56 @@ def stop_sentry_mock_server(self) -> None: if os.path.exists(self.mock_server_pid_file): os.remove(self.mock_server_pid_file) self.mock_server_pid = None - + def find_agent_jar(self) -> Optional[str]: """Find the OpenTelemetry agent JAR file.""" agent_dir = Path("sentry-opentelemetry/sentry-opentelemetry-agent/build/libs/") if not agent_dir.exists(): return None - + for jar_file in agent_dir.glob("*.jar"): name = jar_file.name - if ("agent" in name and - "javadoc" not in name and - "sources" not in name and + if ("agent" in name and + "javadoc" not in name and + "sources" not in name and "dontuse" not in name): return str(jar_file) return None - + def build_agent_jar(self) -> int: """Build the OpenTelemetry agent JAR file.""" print("Building OpenTelemetry agent JAR...") return self.run_gradle_task(":sentry-opentelemetry:sentry-opentelemetry-agent:assemble") - + def ensure_agent_jar(self, skip_build: bool = False) -> Optional[str]: """Ensure the OpenTelemetry agent JAR exists, building it if necessary.""" agent_jar = self.find_agent_jar() if agent_jar: return agent_jar - + if skip_build: print("OpenTelemetry agent JAR not found and build was skipped") return None - + # Agent JAR doesn't exist, try to build it print("OpenTelemetry agent JAR not found, building it...") build_result = self.build_agent_jar() if build_result != 0: print("Failed to build OpenTelemetry agent JAR") return None - + # Try to find it again after building agent_jar = self.find_agent_jar() if not agent_jar: print("OpenTelemetry agent JAR still not found after building") return None - + return agent_jar - + def start_spring_server(self, sample_module: str, java_agent: str, java_agent_auto_init: str) -> None: """Start a Spring Boot server for testing.""" print(f"Starting Spring server for {sample_module}...") - + # Build environment variables env = os.environ.copy() env.update({ @@ -257,15 +239,15 @@ def start_spring_server(self, sample_module: str, java_agent: str, java_agent_au "SENTRY_AUTO_INIT": java_agent_auto_init, "SENTRY_TRACES_SAMPLE_RATE": "1.0", "OTEL_TRACES_EXPORTER": "none", - "OTEL_METRICS_EXPORTER": "none", + "OTEL_METRICS_EXPORTER": "none", "OTEL_LOGS_EXPORTER": "none", "SENTRY_LOGS_ENABLED": "true" }) - + # Build command jar_path = f"sentry-samples/{sample_module}/build/libs/{sample_module}-0.0.1-SNAPSHOT.jar" cmd = ["java"] - + if java_agent == "1": agent_jar = self.ensure_agent_jar() if agent_jar: @@ -273,9 +255,9 @@ def start_spring_server(self, sample_module: str, java_agent: str, java_agent_au print(f"Using Java Agent: {agent_jar}") else: print("Warning: Java agent was requested but could not be found or built") - + cmd.extend(["-jar", jar_path]) - + try: # Start the Spring server with open("spring-server.txt", "w") as log_file: @@ -285,22 +267,22 @@ def start_spring_server(self, sample_module: str, java_agent: str, java_agent_au stdout=log_file, stderr=subprocess.STDOUT ) - + # Store PID in instance variable and write to file self.spring_server_pid = self.spring_server_process.pid with open(self.spring_server_pid_file, "w") as pid_file: pid_file.write(str(self.spring_server_pid)) - + print(f"Started Spring server with PID {self.spring_server_pid}") - + except Exception as e: print(f"Failed to start Spring server: {e}") raise - + def wait_for_spring(self, max_attempts: int = 20) -> bool: """Wait for Spring Boot application to be ready.""" print("Waiting for Spring application to be ready...") - + for attempt in range(1, max_attempts + 1): try: response = requests.head( @@ -313,13 +295,13 @@ def wait_for_spring(self, max_attempts: int = 20) -> bool: return True except: pass - + print(f"Waiting... (attempt {attempt}/{max_attempts})") time.sleep(1) - + print("Spring application failed to become ready") return False - + def get_spring_status(self) -> dict: """Get status of Spring Boot application.""" status = { @@ -327,10 +309,10 @@ def get_spring_status(self) -> dict: "pid": self.spring_server_pid, "http_ready": False } - + if self.spring_server_pid and self.is_process_running(self.spring_server_pid): status["process_running"] = True - + # Check HTTP endpoint try: response = requests.head( @@ -342,9 +324,9 @@ def get_spring_status(self) -> dict: status["http_ready"] = True except: pass - + return status - + def get_sentry_status(self) -> dict: """Get status of Sentry mock server.""" status = { @@ -352,10 +334,10 @@ def get_sentry_status(self) -> dict: "pid": self.mock_server_pid, "http_ready": False } - + if self.mock_server_pid and self.is_process_running(self.mock_server_pid): status["process_running"] = True - + # Check HTTP endpoint try: response = requests.get("http://127.0.0.1:8000/envelope-count", timeout=2) @@ -363,25 +345,25 @@ def get_sentry_status(self) -> dict: status["http_ready"] = True except: pass - + return status - + def print_status_summary(self) -> None: """Print status summary of all services.""" print("=== Service Status ===") - + sentry_status = self.get_sentry_status() print(f"Sentry Mock Server:") print(f" PID: {sentry_status['pid'] or 'None'}") print(f" Process Running: {'✅' if sentry_status['process_running'] else '❌'}") print(f" HTTP Ready: {'✅' if sentry_status['http_ready'] else '❌'}") - + spring_status = self.get_spring_status() print(f"Spring Boot App:") print(f" PID: {spring_status['pid'] or 'None'}") print(f" Process Running: {'✅' if spring_status['process_running'] else '❌'}") print(f" HTTP Ready: {'✅' if spring_status['http_ready'] else '❌'}") - + def stop_spring_server(self) -> None: """Stop the Spring Boot server.""" try: @@ -396,7 +378,7 @@ def stop_spring_server(self) -> None: elif self.spring_server_pid and self.is_process_running(self.spring_server_pid): print(f"Killing Spring server from PID file with PID {self.spring_server_pid}") self.kill_process(self.spring_server_pid, "Spring server") - + except Exception as e: print(f"Error stopping Spring server: {e}") finally: @@ -404,17 +386,17 @@ def stop_spring_server(self) -> None: if os.path.exists(self.spring_server_pid_file): os.remove(self.spring_server_pid_file) self.spring_server_pid = None - + def get_build_task(self, sample_module: str) -> str: """Get the appropriate build task for a module.""" return "bootJar" if "spring" in sample_module else "assemble" - + def build_module(self, sample_module: str) -> int: """Build a sample module using the appropriate task.""" build_task = self.get_build_task(sample_module) print(f"Building {sample_module} using {build_task} task") return self.run_gradle_task(f":sentry-samples:{sample_module}:{build_task}") - + def run_gradle_task(self, task: str) -> int: """Run a Gradle task and return the exit code.""" print(f"Running: ./gradlew {task}") @@ -424,8 +406,8 @@ def run_gradle_task(self, task: str) -> int: except Exception as e: print(f"Failed to run Gradle task: {e}") return 1 - - def setup_test_infrastructure(self, sample_module: str, java_agent: str, + + def setup_test_infrastructure(self, sample_module: str, java_agent: str, java_agent_auto_init: str, build_before_run: str) -> int: """Set up test infrastructure. Returns 0 on success, error code on failure.""" # Build if requested @@ -435,18 +417,18 @@ def setup_test_infrastructure(self, sample_module: str, java_agent: str, if build_result != 0: print("Build failed") return build_result - + # Ensure agent JAR is available if needed if java_agent == "1": agent_jar = self.ensure_agent_jar() if not agent_jar: print("Error: Java agent was requested but could not be found or built") return 1 - + # Start mock server print("Starting Sentry mock server...") self.start_sentry_mock_server() - + # Start Spring server if it's a Spring module if "spring" in sample_module: print(f"Starting Spring server for {sample_module}...") @@ -455,65 +437,65 @@ def setup_test_infrastructure(self, sample_module: str, java_agent: str, print("Spring application failed to start!") return 1 print("Spring application is ready!") - + return 0 - - def run_single_test(self, sample_module: str, java_agent: str, + + def run_single_test(self, sample_module: str, java_agent: str, java_agent_auto_init: str, build_before_run: str) -> int: """Run a single system test.""" print(f"Running system test for {sample_module}") - + try: # Set up infrastructure setup_result = self.setup_test_infrastructure(sample_module, java_agent, java_agent_auto_init, build_before_run) if setup_result != 0: return setup_result - + # Run the system test test_result = self.run_gradle_task(f":sentry-samples:{sample_module}:systemTest") - + return test_result - + finally: # Cleanup if "spring" in sample_module: self.stop_spring_server() self.stop_sentry_mock_server() - + def run_all_tests(self) -> int: """Run all system tests.""" test_configs = self.get_available_modules() - + failed_tests = [] - - for module_config in test_configs: + + for sample_module, java_agent, java_agent_auto_init, build_before_run in test_configs: # Convert true/false to internal 1/0 format - agent = str_to_bool(module_config.java_agent) - auto_init = module_config.java_agent_auto_init # already in correct format - build = str_to_bool(module_config.build_before_run) - - print(f"\n{'='*TERMINAL_COLUMNS}") - print(f"Running test: {module_config.sample_module} (agent={module_config.java_agent}, auto_init={module_config.java_agent_auto_init})") - print(f"{'='*TERMINAL_COLUMNS}") - - result = self.run_single_test(module_config.sample_module, agent, auto_init, build) - + agent = str_to_bool(java_agent) + auto_init = java_agent_auto_init # already in correct format + build = str_to_bool(build_before_run) + + print(f"\n{'='*60}") + print(f"Running test: {sample_module} (agent={java_agent}, auto_init={java_agent_auto_init})") + print(f"{'='*60}") + + result = self.run_single_test(sample_module, agent, auto_init, build) + if result != 0: # Find the module number in the full list for interactive reference - module_number = self._find_module_number(module_config.sample_module, module_config.java_agent, module_config.java_agent_auto_init) - failed_tests.append((module_number, module_config.sample_module, module_config.java_agent, module_config.java_agent_auto_init)) - print(f"❌ Test failed: {module_config.sample_module}") + module_number = self._find_module_number(sample_module, java_agent, java_agent_auto_init) + failed_tests.append((module_number, sample_module, java_agent, java_agent_auto_init)) + print(f"❌ Test failed: {sample_module}") else: - print(f"✅ Test passed: {module_config.sample_module}") - + print(f"✅ Test passed: {sample_module}") + # Summary - print(f"\n{'='*TERMINAL_COLUMNS}") + print(f"\n{'='*60}") print("TEST SUMMARY") - print(f"{'='*TERMINAL_COLUMNS}") + print(f"{'='*60}") print(f"Total tests: {len(test_configs)}") print(f"Passed: {len(test_configs) - len(failed_tests)}") print(f"Failed: {len(failed_tests)}") - + if failed_tests: print("\nFailed tests (for interactive mode, use these numbers):") for module_number, sample_module, java_agent, java_agent_auto_init in failed_tests: @@ -522,22 +504,22 @@ def run_all_tests(self) -> int: else: print("\n🎉 All tests passed!") return 0 - - def run_manual_test_mode(self, sample_module: str, java_agent: str, + + def run_manual_test_mode(self, sample_module: str, java_agent: str, java_agent_auto_init: str, build_before_run: str) -> int: """Set up infrastructure for manual testing from IDE.""" print(f"Setting up manual test environment for {sample_module}") - + try: # Set up infrastructure setup_result = self.setup_test_infrastructure(sample_module, java_agent, java_agent_auto_init, build_before_run) if setup_result != 0: return setup_result - + # Show status and wait for user - print("\n" + "="*TERMINAL_COLUMNS) + print("\n" + "="*60) print("🚀 Manual test environment ready 🚀") - print("="*TERMINAL_COLUMNS) + print("="*60) self.print_status_summary() print(f"\nInfrastructure is ready for manual testing of: {sample_module}") print("You can now run your system tests from your IDE.") @@ -546,59 +528,57 @@ def run_manual_test_mode(self, sample_module: str, java_agent: str, print(f" - Java Agent: {'Yes' if java_agent == '1' else 'No'}") print(f" - Agent Auto-init: {java_agent_auto_init}") print(f" - Mock DSN: http://502f25099c204a2fbf4cb16edc5975d1@localhost:8000/0") - + if "spring" in sample_module: print("\nSpring Boot app is running on: http://localhost:8080") - + print("\nPress Enter to stop the infrastructure and exit...") - + # Wait for user input try: input() except KeyboardInterrupt: print("\nReceived interrupt signal") - + print("\nStopping infrastructure...") return 0 - + finally: # Cleanup will happen in the finally block of main() pass - - def get_available_modules(self) -> List[ModuleConfig]: + + def get_available_modules(self) -> List[Tuple[str, str, str, str]]: """Get list of all available test modules.""" return [ - ModuleConfig("sentry-samples-spring-boot", "false", "true", "false"), - ModuleConfig("sentry-samples-spring-boot-opentelemetry-noagent", "false", "true", "false"), - ModuleConfig("sentry-samples-spring-boot-opentelemetry", "true", "true", "false"), - ModuleConfig("sentry-samples-spring-boot-opentelemetry", "true", "false", "false"), - ModuleConfig("sentry-samples-spring-boot-webflux-jakarta", "false", "true", "false"), - ModuleConfig("sentry-samples-spring-boot-webflux", "false", "true", "false"), - ModuleConfig("sentry-samples-spring-boot-jakarta", "false", "true", "false"), - ModuleConfig("sentry-samples-spring-boot-jakarta-opentelemetry-noagent", "false", "true", "false"), - ModuleConfig("sentry-samples-spring-boot-jakarta-opentelemetry", "true", "true", "false"), - ModuleConfig("sentry-samples-spring-boot-jakarta-opentelemetry", "true", "false", "false"), - ModuleConfig("sentry-samples-console", "false", "true", "false"), + ("sentry-samples-spring-boot", "false", "true", "false"), + ("sentry-samples-spring-boot-opentelemetry-noagent", "false", "true", "false"), + ("sentry-samples-spring-boot-opentelemetry", "true", "true", "false"), + ("sentry-samples-spring-boot-opentelemetry", "true", "false", "false"), + ("sentry-samples-spring-boot-webflux-jakarta", "false", "true", "false"), + ("sentry-samples-spring-boot-webflux", "false", "true", "false"), + ("sentry-samples-spring-boot-jakarta", "false", "true", "false"), + ("sentry-samples-spring-boot-jakarta-opentelemetry-noagent", "false", "true", "false"), + ("sentry-samples-spring-boot-jakarta-opentelemetry", "true", "true", "false"), + ("sentry-samples-spring-boot-jakarta-opentelemetry", "true", "false", "false"), + ("sentry-samples-console", "false", "true", "false"), ] - + def _find_module_number(self, module_name: str, agent: str, auto_init: str) -> int: """Find the module number in the interactive list (1-based).""" modules = self.get_available_modules() - for i, module_config in enumerate(modules, 1): - if (module_config.sample_module == module_name and - module_config.java_agent == agent and - module_config.java_agent_auto_init == auto_init): + for i, (mod_name, mod_agent, mod_auto_init, _) in enumerate(modules, 1): + if mod_name == module_name and mod_agent == agent and mod_auto_init == auto_init: return i return 0 # Should not happen, but return 0 if not found - + def parse_selection(self, user_input: str, max_index: int) -> List[int]: """Parse user selection string into list of indices.""" if user_input.strip() == "*": return list(range(max_index)) - + indices = [] parts = user_input.split(",") - + for part in parts: part = part.strip() if "-" in part: @@ -616,34 +596,34 @@ def parse_selection(self, user_input: str, max_index: int) -> List[int]: indices.append(int(part) - 1) except ValueError: raise ValueError(f"Invalid number: {part}") - + # Remove duplicates and sort indices = sorted(set(indices)) - + # Validate indices for idx in indices: if idx < 0 or idx >= max_index: raise ValueError(f"Index {idx + 1} is out of range (1-{max_index})") - + return indices - + def interactive_module_selection(self) -> InteractiveSelection: """Display modules and get user selection.""" modules = self.get_available_modules() - + print("\nAvailable test modules:") print("=" * 80) - for i, module_config in enumerate(modules, 1): - agent_text = "with agent" if module_config.uses_agent() else "no agent" - auto_init_text = f"auto-init: {module_config.java_agent_auto_init}" - print(f"{i:2d}. {module_config.sample_module:<50} ({agent_text}, {auto_init_text})") - + for i, (module, agent, auto_init, build) in enumerate(modules, 1): + agent_text = "with agent" if str_to_bool(agent) == "1" else "no agent" + auto_init_text = f"auto-init: {auto_init}" + print(f"{i:2d}. {module:<50} ({agent_text}, {auto_init_text})") + print("\nSelection options:") print(" * = all modules") print(" Single: 1, 5, 8") print(" Range: 1-4, 6-8") print(" Combined: 1,2,4-5,8") - + selected_modules = [] while True: try: @@ -651,29 +631,29 @@ def interactive_module_selection(self) -> InteractiveSelection: if not user_input: print("Please enter a selection.") continue - + selected_indices = self.parse_selection(user_input, len(modules)) selected_modules = [modules[i] for i in selected_indices] - + # Show confirmation print(f"\nSelected {len(selected_modules)} module(s):") - for i, module_config in enumerate(selected_modules, 1): - agent_text = "with agent" if module_config.uses_agent() else "no agent" - print(f" {i}. {module_config.sample_module} ({agent_text}, auto-init: {module_config.java_agent_auto_init})") - + for i, (module, agent, auto_init, build) in enumerate(selected_modules, 1): + agent_text = "with agent" if str_to_bool(agent) == "1" else "no agent" + print(f" {i}. {module} ({agent_text}, auto-init: {auto_init})") + confirm = input("\nProceed with these selections? [Y/n]: ").strip().lower() if confirm in ('', 'y', 'yes'): break else: print("Please make a new selection.") - + except ValueError as e: print(f"Error: {e}") print("Please try again.") except KeyboardInterrupt: print("\nOperation cancelled.") return InteractiveSelection(modules=[], manual_test_mode=False, build_agent=False) - + # Ask about test mode manual_test_mode = False while True: @@ -690,10 +670,10 @@ def interactive_module_selection(self) -> InteractiveSelection: except KeyboardInterrupt: print("\nOperation cancelled.") return InteractiveSelection(modules=[], manual_test_mode=False, build_agent=False) - + # Ask about building agent if any modules use it build_agent = False - has_agent_modules = any(module_config.uses_agent() for module_config in selected_modules) + has_agent_modules = any(str_to_bool(agent) == "1" for _, agent, _, _ in selected_modules) if has_agent_modules: while True: try: @@ -709,17 +689,17 @@ def interactive_module_selection(self) -> InteractiveSelection: except KeyboardInterrupt: print("\nOperation cancelled.") return InteractiveSelection(modules=[], manual_test_mode=False, build_agent=False) - + return InteractiveSelection(modules=selected_modules, manual_test_mode=manual_test_mode, build_agent=build_agent) - + def run_interactive_tests(self, agent: str, auto_init: str, build: str) -> int: """Run tests with interactive module selection.""" selection = self.interactive_module_selection() - + if selection.is_empty(): print("No modules selected. Exiting.") return 0 - + # Build agent JAR if requested and modules use agent if selection.build_agent and selection.has_agent_modules(): print("\nBuilding OpenTelemetry agent JAR...") @@ -728,55 +708,55 @@ def run_interactive_tests(self, agent: str, auto_init: str, build: str) -> int: print("Failed to build OpenTelemetry agent JAR") return build_result print("✅ OpenTelemetry agent JAR built successfully") - + # Handle manual test mode if selection.manual_test_mode: if not selection.is_single_module(): print("Error: Manual test mode can only be used with a single module.") print("Please select only one module for manual testing.") return 1 - - module_config = selection.get_first_module() + + sample_module, test_agent, test_auto_init, test_build = selection.get_first_module() # Convert true/false to internal 1/0 format - agent = str_to_bool(module_config.java_agent) - auto_init = module_config.java_agent_auto_init # already in correct format - build = str_to_bool(module_config.build_before_run) - - print(f"\nSetting up manual test environment for: {module_config.sample_module}") - return self.run_manual_test_mode(module_config.sample_module, agent, auto_init, build) - + agent = str_to_bool(test_agent) + auto_init = test_auto_init # already in correct format + build = str_to_bool(test_build) + + print(f"\nSetting up manual test environment for: {sample_module}") + return self.run_manual_test_mode(sample_module, agent, auto_init, build) + # Handle automatic test running failed_tests = [] - - for i, module_config in enumerate(selection.modules, 1): + + for i, (sample_module, test_agent, test_auto_init, test_build) in enumerate(selection.modules, 1): # Convert true/false to internal 1/0 format - agent = str_to_bool(module_config.java_agent) - auto_init = module_config.java_agent_auto_init # already in correct format - build = str_to_bool(module_config.build_before_run) - - print(f"\n{'='*TERMINAL_COLUMNS}") - print(f"Running test {i}/{len(selection.modules)}: {module_config.sample_module}") - print(f"Agent: {module_config.java_agent}, Auto-init: {module_config.java_agent_auto_init}") - print(f"{'='*TERMINAL_COLUMNS}") - - result = self.run_single_test(module_config.sample_module, agent, auto_init, build) - + agent = str_to_bool(test_agent) + auto_init = test_auto_init # already in correct format + build = str_to_bool(test_build) + + print(f"\n{'='*60}") + print(f"Running test {i}/{len(selection.modules)}: {sample_module}") + print(f"Agent: {test_agent}, Auto-init: {test_auto_init}") + print(f"{'='*60}") + + result = self.run_single_test(sample_module, agent, auto_init, build) + if result != 0: # Find the module number in the full list for interactive reference - module_number = self._find_module_number(module_config.sample_module, module_config.java_agent, module_config.java_agent_auto_init) - failed_tests.append((module_number, module_config.sample_module, module_config.java_agent, module_config.java_agent_auto_init)) - print(f"❌ Test failed: {module_config.sample_module}") + module_number = self._find_module_number(sample_module, test_agent, test_auto_init) + failed_tests.append((module_number, sample_module, test_agent, test_auto_init)) + print(f"❌ Test failed: {sample_module}") else: - print(f"✅ Test passed: {module_config.sample_module}") - + print(f"✅ Test passed: {sample_module}") + # Summary - print(f"\n{'='*TERMINAL_COLUMNS}") + print(f"\n{'='*60}") print("TEST SUMMARY") - print(f"{'='*TERMINAL_COLUMNS}") + print(f"{'='*60}") print(f"Total tests: {len(selection.modules)}") print(f"Passed: {len(selection.modules) - len(failed_tests)}") print(f"Failed: {len(failed_tests)}") - + if failed_tests: print("\nFailed tests (for interactive mode, use these numbers):") for module_number, sample_module, test_agent, test_auto_init in failed_tests: @@ -785,7 +765,7 @@ def run_interactive_tests(self, agent: str, auto_init: str, build: str) -> int: else: print("\n🎉 All tests passed!") return 0 - + def cleanup_on_exit(self, signum, frame): """Cleanup handler for signals.""" print(f"\nReceived signal {signum}, cleaning up...") @@ -796,7 +776,7 @@ def cleanup_on_exit(self, signum, frame): def main(): parser = argparse.ArgumentParser(description="System Test Runner for Sentry Java") subparsers = parser.add_subparsers(dest="command", help="Available commands") - + # Test subcommand test_parser = subparsers.add_parser("test", help="Run system tests") test_group = test_parser.add_mutually_exclusive_group(required=True) @@ -807,54 +787,54 @@ def main(): test_parser.add_argument("--auto-init", default="true", help="Auto-init agent (true or false)") test_parser.add_argument("--build", default="false", help="Build before running (true or false)") test_parser.add_argument("--manual-test", action="store_true", help="Set up infrastructure but pause for manual testing from IDE") - + # Spring subcommand spring_parser = subparsers.add_parser("spring", help="Manage Spring Boot applications") spring_subparsers = spring_parser.add_subparsers(dest="spring_action", help="Spring actions") - + spring_start_parser = spring_subparsers.add_parser("start", help="Start Spring Boot application") spring_start_parser.add_argument("module", help="Sample module to start") spring_start_parser.add_argument("--agent", default="false", help="Use Java agent (true or false)") spring_start_parser.add_argument("--auto-init", default="true", help="Auto-init agent (true or false)") spring_start_parser.add_argument("--build", default="false", help="Build before starting (true or false)") - + spring_stop_parser = spring_subparsers.add_parser("stop", help="Stop Spring Boot application") - + spring_wait_parser = spring_subparsers.add_parser("wait", help="Wait for Spring Boot application to be ready") spring_wait_parser.add_argument("--timeout", type=int, default=20, help="Max attempts to wait (default: 20)") - + spring_status_parser = spring_subparsers.add_parser("status", help="Check Spring Boot application status") - + # Sentry subcommand sentry_parser = subparsers.add_parser("sentry", help="Manage Sentry mock server") sentry_subparsers = sentry_parser.add_subparsers(dest="sentry_action", help="Sentry actions") - + sentry_start_parser = sentry_subparsers.add_parser("start", help="Start Sentry mock server") sentry_stop_parser = sentry_subparsers.add_parser("stop", help="Stop Sentry mock server") sentry_status_parser = sentry_subparsers.add_parser("status", help="Check Sentry mock server status") - + # Status subcommand status_parser = subparsers.add_parser("status", help="Show status of all services") - + args = parser.parse_args() - + if not args.command: parser.print_help() return 1 - + runner = SystemTestRunner() - + # Set up signal handlers for cleanup signal.signal(signal.SIGINT, runner.cleanup_on_exit) signal.signal(signal.SIGTERM, runner.cleanup_on_exit) - + try: if args.command == "test": # Convert true/false arguments to internal 1/0 format agent = str_to_bool(args.agent) auto_init = args.auto_init # already accepts true/false build = str_to_bool(args.build) - + if args.manual_test and args.module: return runner.run_manual_test_mode(args.module, agent, auto_init, build) elif args.manual_test and args.all: @@ -869,14 +849,14 @@ def main(): return runner.run_single_test(args.module, agent, auto_init, build) elif args.interactive: return runner.run_interactive_tests(agent, auto_init, build) - + elif args.command == "spring": if args.spring_action == "start": # Convert true/false arguments to internal format agent = str_to_bool(args.agent) auto_init = args.auto_init # already accepts true/false build = str_to_bool(args.build) - + # Build if requested if build == "1": print("Building before starting Spring application") @@ -884,7 +864,7 @@ def main(): if build_result != 0: print("Build failed") return build_result - + runner.start_spring_server(args.module, agent, auto_init) if runner.wait_for_spring(): print("Spring application started successfully!") @@ -913,7 +893,7 @@ def main(): else: spring_parser.print_help() return 1 - + elif args.command == "sentry": if args.sentry_action == "start": runner.start_sentry_mock_server() @@ -933,14 +913,14 @@ def main(): else: sentry_parser.print_help() return 1 - + elif args.command == "status": runner.print_status_summary() return 0 else: parser.print_help() return 1 - + except KeyboardInterrupt: print("\nInterrupted by user") return 1 @@ -954,4 +934,4 @@ def main(): runner.stop_sentry_mock_server() if __name__ == "__main__": - sys.exit(main()) + sys.exit(main()) \ No newline at end of file From 4c16dcc2271527562c3b6c95bbec352517781927 Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 14 Jul 2025 18:49:58 +0200 Subject: [PATCH 11/17] improve --- test/system-test-runner.py | 436 +++++++++++++++++++------------------ 1 file changed, 230 insertions(+), 206 deletions(-) diff --git a/test/system-test-runner.py b/test/system-test-runner.py index aed0d7126a2..9e906788345 100644 --- a/test/system-test-runner.py +++ b/test/system-test-runner.py @@ -42,6 +42,8 @@ from typing import Optional, List, Tuple from dataclasses import dataclass +TERMINAL_COLUMNS = os.get_terminal_size()[0] or 60 + def str_to_bool(value: str) -> str: """Convert true/false string to 1/0 string for internal compatibility.""" if value.lower() in ('true', '1'): @@ -51,30 +53,50 @@ def str_to_bool(value: str) -> str: else: raise ValueError(f"Invalid boolean value: {value}. Use 'true' or 'false'") +@dataclass +class ModuleConfig: + """Configuration for a test module.""" + name: str + java_agent: str + java_agent_auto_init: str + build_before_run: str + + def uses_agent(self) -> bool: + """Check if this module uses the Java agent.""" + return str_to_bool(self.java_agent) == "1" + + def needs_build(self) -> bool: + """Check if this module needs to be built before running.""" + return str_to_bool(self.build_before_run) == "1" + + def is_spring_module(self) -> bool: + """Check if this is a Spring Boot module.""" + return "spring" in self.name + @dataclass class InteractiveSelection: """Result of interactive module selection.""" - modules: List[Tuple[str, str, str, str]] + modules: List[ModuleConfig] manual_test_mode: bool build_agent: bool - + def is_empty(self) -> bool: """Check if no modules were selected.""" return len(self.modules) == 0 - + def is_single_module(self) -> bool: """Check if exactly one module was selected.""" return len(self.modules) == 1 - - def get_first_module(self) -> Tuple[str, str, str, str]: + + def get_first_module(self) -> ModuleConfig: """Get the first selected module (for manual test mode).""" if self.is_empty(): raise ValueError("No modules selected") return self.modules[0] - + def has_agent_modules(self) -> bool: """Check if any selected modules use the Java agent.""" - return any(str_to_bool(agent) == "1" for _, agent, _, _ in self.modules) + return any(module_config.uses_agent() for module_config in self.modules) class SystemTestRunner: def __init__(self): @@ -84,16 +106,16 @@ def __init__(self): self.spring_server_pid: Optional[int] = None self.mock_server_pid_file = "sentry-mock-server.pid" self.spring_server_pid_file = "spring-server.pid" - + # Load existing PIDs if available self.mock_server_pid = self.read_pid_file(self.mock_server_pid_file) self.spring_server_pid = self.read_pid_file(self.spring_server_pid_file) - + if self.mock_server_pid: print(f"Found existing mock server PID: {self.mock_server_pid}") if self.spring_server_pid: print(f"Found existing Spring server PID: {self.spring_server_pid}") - + def read_pid_file(self, pid_file: str) -> Optional[int]: """Read PID from file if it exists.""" try: @@ -103,7 +125,7 @@ def read_pid_file(self, pid_file: str) -> Optional[int]: except (ValueError, IOError) as e: print(f"Error reading PID file {pid_file}: {e}") return None - + def is_process_running(self, pid: int) -> bool: """Check if a process with given PID is still running.""" try: @@ -112,14 +134,14 @@ def is_process_running(self, pid: int) -> bool: return True except (OSError, ProcessLookupError): return False - + def kill_process(self, pid: int, name: str) -> None: """Kill a process by PID.""" try: print(f"Killing existing {name} process with PID {pid}") os.kill(pid, signal.SIGTERM) time.sleep(2) # Give it time to terminate gracefully - + # Check if it's still running and force kill if necessary if self.is_process_running(pid): print(f"Process {pid} didn't terminate gracefully, force killing...") @@ -127,9 +149,9 @@ def kill_process(self, pid: int, name: str) -> None: time.sleep(1) except (OSError, ProcessLookupError): print(f"Process {pid} was already dead") - - + + def start_sentry_mock_server(self) -> None: """Start the Sentry mock server.""" print("Starting Sentry mock server...") @@ -141,21 +163,21 @@ def start_sentry_mock_server(self) -> None: stdout=log_file, stderr=subprocess.STDOUT ) - + # Store PID in instance variable and write to file self.mock_server_pid = self.mock_server_process.pid with open(self.mock_server_pid_file, "w") as pid_file: pid_file.write(str(self.mock_server_pid)) - + print(f"Started mock server with PID {self.mock_server_pid}") - + # Wait a moment for the server to start time.sleep(2) - + except Exception as e: print(f"Failed to start mock server: {e}") raise - + def stop_sentry_mock_server(self) -> None: """Stop the Sentry mock server.""" try: @@ -165,7 +187,7 @@ def stop_sentry_mock_server(self) -> None: print("Sent stop signal to mock server") except: print("Could not send graceful stop signal") - + # Kill the process - try process object first, then PID from file if self.mock_server_process and self.mock_server_process.poll() is None: print(f"Killing mock server process object with PID {self.mock_server_process.pid}") @@ -174,7 +196,7 @@ def stop_sentry_mock_server(self) -> None: elif self.mock_server_pid and self.is_process_running(self.mock_server_pid): print(f"Killing mock server from PID file with PID {self.mock_server_pid}") self.kill_process(self.mock_server_pid, "mock server") - + except Exception as e: print(f"Error stopping mock server: {e}") finally: @@ -182,56 +204,56 @@ def stop_sentry_mock_server(self) -> None: if os.path.exists(self.mock_server_pid_file): os.remove(self.mock_server_pid_file) self.mock_server_pid = None - + def find_agent_jar(self) -> Optional[str]: """Find the OpenTelemetry agent JAR file.""" agent_dir = Path("sentry-opentelemetry/sentry-opentelemetry-agent/build/libs/") if not agent_dir.exists(): return None - + for jar_file in agent_dir.glob("*.jar"): name = jar_file.name - if ("agent" in name and - "javadoc" not in name and - "sources" not in name and + if ("agent" in name and + "javadoc" not in name and + "sources" not in name and "dontuse" not in name): return str(jar_file) return None - + def build_agent_jar(self) -> int: """Build the OpenTelemetry agent JAR file.""" print("Building OpenTelemetry agent JAR...") return self.run_gradle_task(":sentry-opentelemetry:sentry-opentelemetry-agent:assemble") - + def ensure_agent_jar(self, skip_build: bool = False) -> Optional[str]: """Ensure the OpenTelemetry agent JAR exists, building it if necessary.""" agent_jar = self.find_agent_jar() if agent_jar: return agent_jar - + if skip_build: print("OpenTelemetry agent JAR not found and build was skipped") return None - + # Agent JAR doesn't exist, try to build it print("OpenTelemetry agent JAR not found, building it...") build_result = self.build_agent_jar() if build_result != 0: print("Failed to build OpenTelemetry agent JAR") return None - + # Try to find it again after building agent_jar = self.find_agent_jar() if not agent_jar: print("OpenTelemetry agent JAR still not found after building") return None - + return agent_jar - + def start_spring_server(self, sample_module: str, java_agent: str, java_agent_auto_init: str) -> None: """Start a Spring Boot server for testing.""" print(f"Starting Spring server for {sample_module}...") - + # Build environment variables env = os.environ.copy() env.update({ @@ -239,15 +261,15 @@ def start_spring_server(self, sample_module: str, java_agent: str, java_agent_au "SENTRY_AUTO_INIT": java_agent_auto_init, "SENTRY_TRACES_SAMPLE_RATE": "1.0", "OTEL_TRACES_EXPORTER": "none", - "OTEL_METRICS_EXPORTER": "none", + "OTEL_METRICS_EXPORTER": "none", "OTEL_LOGS_EXPORTER": "none", "SENTRY_LOGS_ENABLED": "true" }) - + # Build command jar_path = f"sentry-samples/{sample_module}/build/libs/{sample_module}-0.0.1-SNAPSHOT.jar" cmd = ["java"] - + if java_agent == "1": agent_jar = self.ensure_agent_jar() if agent_jar: @@ -255,9 +277,9 @@ def start_spring_server(self, sample_module: str, java_agent: str, java_agent_au print(f"Using Java Agent: {agent_jar}") else: print("Warning: Java agent was requested but could not be found or built") - + cmd.extend(["-jar", jar_path]) - + try: # Start the Spring server with open("spring-server.txt", "w") as log_file: @@ -267,22 +289,22 @@ def start_spring_server(self, sample_module: str, java_agent: str, java_agent_au stdout=log_file, stderr=subprocess.STDOUT ) - + # Store PID in instance variable and write to file self.spring_server_pid = self.spring_server_process.pid with open(self.spring_server_pid_file, "w") as pid_file: pid_file.write(str(self.spring_server_pid)) - + print(f"Started Spring server with PID {self.spring_server_pid}") - + except Exception as e: print(f"Failed to start Spring server: {e}") raise - + def wait_for_spring(self, max_attempts: int = 20) -> bool: """Wait for Spring Boot application to be ready.""" print("Waiting for Spring application to be ready...") - + for attempt in range(1, max_attempts + 1): try: response = requests.head( @@ -295,13 +317,13 @@ def wait_for_spring(self, max_attempts: int = 20) -> bool: return True except: pass - + print(f"Waiting... (attempt {attempt}/{max_attempts})") time.sleep(1) - + print("Spring application failed to become ready") return False - + def get_spring_status(self) -> dict: """Get status of Spring Boot application.""" status = { @@ -309,10 +331,10 @@ def get_spring_status(self) -> dict: "pid": self.spring_server_pid, "http_ready": False } - + if self.spring_server_pid and self.is_process_running(self.spring_server_pid): status["process_running"] = True - + # Check HTTP endpoint try: response = requests.head( @@ -324,9 +346,9 @@ def get_spring_status(self) -> dict: status["http_ready"] = True except: pass - + return status - + def get_sentry_status(self) -> dict: """Get status of Sentry mock server.""" status = { @@ -334,10 +356,10 @@ def get_sentry_status(self) -> dict: "pid": self.mock_server_pid, "http_ready": False } - + if self.mock_server_pid and self.is_process_running(self.mock_server_pid): status["process_running"] = True - + # Check HTTP endpoint try: response = requests.get("http://127.0.0.1:8000/envelope-count", timeout=2) @@ -345,25 +367,25 @@ def get_sentry_status(self) -> dict: status["http_ready"] = True except: pass - + return status - + def print_status_summary(self) -> None: """Print status summary of all services.""" print("=== Service Status ===") - + sentry_status = self.get_sentry_status() print(f"Sentry Mock Server:") print(f" PID: {sentry_status['pid'] or 'None'}") print(f" Process Running: {'✅' if sentry_status['process_running'] else '❌'}") print(f" HTTP Ready: {'✅' if sentry_status['http_ready'] else '❌'}") - + spring_status = self.get_spring_status() print(f"Spring Boot App:") print(f" PID: {spring_status['pid'] or 'None'}") print(f" Process Running: {'✅' if spring_status['process_running'] else '❌'}") print(f" HTTP Ready: {'✅' if spring_status['http_ready'] else '❌'}") - + def stop_spring_server(self) -> None: """Stop the Spring Boot server.""" try: @@ -378,7 +400,7 @@ def stop_spring_server(self) -> None: elif self.spring_server_pid and self.is_process_running(self.spring_server_pid): print(f"Killing Spring server from PID file with PID {self.spring_server_pid}") self.kill_process(self.spring_server_pid, "Spring server") - + except Exception as e: print(f"Error stopping Spring server: {e}") finally: @@ -386,17 +408,17 @@ def stop_spring_server(self) -> None: if os.path.exists(self.spring_server_pid_file): os.remove(self.spring_server_pid_file) self.spring_server_pid = None - + def get_build_task(self, sample_module: str) -> str: """Get the appropriate build task for a module.""" return "bootJar" if "spring" in sample_module else "assemble" - + def build_module(self, sample_module: str) -> int: """Build a sample module using the appropriate task.""" build_task = self.get_build_task(sample_module) print(f"Building {sample_module} using {build_task} task") return self.run_gradle_task(f":sentry-samples:{sample_module}:{build_task}") - + def run_gradle_task(self, task: str) -> int: """Run a Gradle task and return the exit code.""" print(f"Running: ./gradlew {task}") @@ -406,8 +428,8 @@ def run_gradle_task(self, task: str) -> int: except Exception as e: print(f"Failed to run Gradle task: {e}") return 1 - - def setup_test_infrastructure(self, sample_module: str, java_agent: str, + + def setup_test_infrastructure(self, sample_module: str, java_agent: str, java_agent_auto_init: str, build_before_run: str) -> int: """Set up test infrastructure. Returns 0 on success, error code on failure.""" # Build if requested @@ -417,18 +439,18 @@ def setup_test_infrastructure(self, sample_module: str, java_agent: str, if build_result != 0: print("Build failed") return build_result - + # Ensure agent JAR is available if needed if java_agent == "1": agent_jar = self.ensure_agent_jar() if not agent_jar: print("Error: Java agent was requested but could not be found or built") return 1 - + # Start mock server print("Starting Sentry mock server...") self.start_sentry_mock_server() - + # Start Spring server if it's a Spring module if "spring" in sample_module: print(f"Starting Spring server for {sample_module}...") @@ -437,65 +459,65 @@ def setup_test_infrastructure(self, sample_module: str, java_agent: str, print("Spring application failed to start!") return 1 print("Spring application is ready!") - + return 0 - - def run_single_test(self, sample_module: str, java_agent: str, + + def run_single_test(self, sample_module: str, java_agent: str, java_agent_auto_init: str, build_before_run: str) -> int: """Run a single system test.""" print(f"Running system test for {sample_module}") - + try: # Set up infrastructure setup_result = self.setup_test_infrastructure(sample_module, java_agent, java_agent_auto_init, build_before_run) if setup_result != 0: return setup_result - + # Run the system test test_result = self.run_gradle_task(f":sentry-samples:{sample_module}:systemTest") - + return test_result - + finally: # Cleanup if "spring" in sample_module: self.stop_spring_server() self.stop_sentry_mock_server() - + def run_all_tests(self) -> int: """Run all system tests.""" test_configs = self.get_available_modules() - + failed_tests = [] - - for sample_module, java_agent, java_agent_auto_init, build_before_run in test_configs: + + for module_config in test_configs: # Convert true/false to internal 1/0 format - agent = str_to_bool(java_agent) - auto_init = java_agent_auto_init # already in correct format - build = str_to_bool(build_before_run) - - print(f"\n{'='*60}") - print(f"Running test: {sample_module} (agent={java_agent}, auto_init={java_agent_auto_init})") - print(f"{'='*60}") - - result = self.run_single_test(sample_module, agent, auto_init, build) - + agent = str_to_bool(module_config.java_agent) + auto_init = module_config.java_agent_auto_init # already in correct format + build = str_to_bool(module_config.build_before_run) + + print(f"\n{'='*TERMINAL_COLUMNS}") + print(f"Running test: {module_config.name} (agent={module_config.java_agent}, auto_init={module_config.java_agent_auto_init})") + print(f"{'='*TERMINAL_COLUMNS}") + + result = self.run_single_test(module_config.name, agent, auto_init, build) + if result != 0: # Find the module number in the full list for interactive reference - module_number = self._find_module_number(sample_module, java_agent, java_agent_auto_init) - failed_tests.append((module_number, sample_module, java_agent, java_agent_auto_init)) - print(f"❌ Test failed: {sample_module}") + module_number = self._find_module_number(module_config.name, module_config.java_agent, module_config.java_agent_auto_init) + failed_tests.append((module_number, module_config.name, module_config.java_agent, module_config.java_agent_auto_init)) + print(f"❌ Test failed: {module_config.name}") else: - print(f"✅ Test passed: {sample_module}") - + print(f"✅ Test passed: {module_config.name}") + # Summary - print(f"\n{'='*60}") + print(f"\n{'='*TERMINAL_COLUMNS}") print("TEST SUMMARY") - print(f"{'='*60}") + print(f"{'='*TERMINAL_COLUMNS}") print(f"Total tests: {len(test_configs)}") print(f"Passed: {len(test_configs) - len(failed_tests)}") print(f"Failed: {len(failed_tests)}") - + if failed_tests: print("\nFailed tests (for interactive mode, use these numbers):") for module_number, sample_module, java_agent, java_agent_auto_init in failed_tests: @@ -504,22 +526,22 @@ def run_all_tests(self) -> int: else: print("\n🎉 All tests passed!") return 0 - - def run_manual_test_mode(self, sample_module: str, java_agent: str, + + def run_manual_test_mode(self, sample_module: str, java_agent: str, java_agent_auto_init: str, build_before_run: str) -> int: """Set up infrastructure for manual testing from IDE.""" print(f"Setting up manual test environment for {sample_module}") - + try: # Set up infrastructure setup_result = self.setup_test_infrastructure(sample_module, java_agent, java_agent_auto_init, build_before_run) if setup_result != 0: return setup_result - + # Show status and wait for user - print("\n" + "="*60) + print("\n" + "="*TERMINAL_COLUMNS) print("🚀 Manual test environment ready 🚀") - print("="*60) + print("="*TERMINAL_COLUMNS) self.print_status_summary() print(f"\nInfrastructure is ready for manual testing of: {sample_module}") print("You can now run your system tests from your IDE.") @@ -528,57 +550,59 @@ def run_manual_test_mode(self, sample_module: str, java_agent: str, print(f" - Java Agent: {'Yes' if java_agent == '1' else 'No'}") print(f" - Agent Auto-init: {java_agent_auto_init}") print(f" - Mock DSN: http://502f25099c204a2fbf4cb16edc5975d1@localhost:8000/0") - + if "spring" in sample_module: print("\nSpring Boot app is running on: http://localhost:8080") - + print("\nPress Enter to stop the infrastructure and exit...") - + # Wait for user input try: input() except KeyboardInterrupt: print("\nReceived interrupt signal") - + print("\nStopping infrastructure...") return 0 - + finally: # Cleanup will happen in the finally block of main() pass - - def get_available_modules(self) -> List[Tuple[str, str, str, str]]: + + def get_available_modules(self) -> List[ModuleConfig]: """Get list of all available test modules.""" return [ - ("sentry-samples-spring-boot", "false", "true", "false"), - ("sentry-samples-spring-boot-opentelemetry-noagent", "false", "true", "false"), - ("sentry-samples-spring-boot-opentelemetry", "true", "true", "false"), - ("sentry-samples-spring-boot-opentelemetry", "true", "false", "false"), - ("sentry-samples-spring-boot-webflux-jakarta", "false", "true", "false"), - ("sentry-samples-spring-boot-webflux", "false", "true", "false"), - ("sentry-samples-spring-boot-jakarta", "false", "true", "false"), - ("sentry-samples-spring-boot-jakarta-opentelemetry-noagent", "false", "true", "false"), - ("sentry-samples-spring-boot-jakarta-opentelemetry", "true", "true", "false"), - ("sentry-samples-spring-boot-jakarta-opentelemetry", "true", "false", "false"), - ("sentry-samples-console", "false", "true", "false"), + ModuleConfig("sentry-samples-spring-boot", "false", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-opentelemetry-noagent", "false", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-opentelemetry", "true", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-opentelemetry", "true", "false", "false"), + ModuleConfig("sentry-samples-spring-boot-webflux-jakarta", "false", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-webflux", "false", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-jakarta", "false", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-jakarta-opentelemetry-noagent", "false", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-jakarta-opentelemetry", "true", "true", "false"), + ModuleConfig("sentry-samples-spring-boot-jakarta-opentelemetry", "true", "false", "false"), + ModuleConfig("sentry-samples-console", "false", "true", "false"), ] - + def _find_module_number(self, module_name: str, agent: str, auto_init: str) -> int: """Find the module number in the interactive list (1-based).""" modules = self.get_available_modules() - for i, (mod_name, mod_agent, mod_auto_init, _) in enumerate(modules, 1): - if mod_name == module_name and mod_agent == agent and mod_auto_init == auto_init: + for i, module_config in enumerate(modules, 1): + if (module_config.name == module_name and + module_config.java_agent == agent and + module_config.java_agent_auto_init == auto_init): return i return 0 # Should not happen, but return 0 if not found - + def parse_selection(self, user_input: str, max_index: int) -> List[int]: """Parse user selection string into list of indices.""" if user_input.strip() == "*": return list(range(max_index)) - + indices = [] parts = user_input.split(",") - + for part in parts: part = part.strip() if "-" in part: @@ -596,34 +620,34 @@ def parse_selection(self, user_input: str, max_index: int) -> List[int]: indices.append(int(part) - 1) except ValueError: raise ValueError(f"Invalid number: {part}") - + # Remove duplicates and sort indices = sorted(set(indices)) - + # Validate indices for idx in indices: if idx < 0 or idx >= max_index: raise ValueError(f"Index {idx + 1} is out of range (1-{max_index})") - + return indices - + def interactive_module_selection(self) -> InteractiveSelection: """Display modules and get user selection.""" modules = self.get_available_modules() - + print("\nAvailable test modules:") print("=" * 80) - for i, (module, agent, auto_init, build) in enumerate(modules, 1): - agent_text = "with agent" if str_to_bool(agent) == "1" else "no agent" - auto_init_text = f"auto-init: {auto_init}" - print(f"{i:2d}. {module:<50} ({agent_text}, {auto_init_text})") - + for i, module_config in enumerate(modules, 1): + agent_text = "with agent" if module_config.uses_agent() else "no agent" + auto_init_text = f"auto-init: {module_config.java_agent_auto_init}" + print(f"{i:2d}. {module_config.name:<50} ({agent_text}, {auto_init_text})") + print("\nSelection options:") print(" * = all modules") print(" Single: 1, 5, 8") print(" Range: 1-4, 6-8") print(" Combined: 1,2,4-5,8") - + selected_modules = [] while True: try: @@ -631,29 +655,29 @@ def interactive_module_selection(self) -> InteractiveSelection: if not user_input: print("Please enter a selection.") continue - + selected_indices = self.parse_selection(user_input, len(modules)) selected_modules = [modules[i] for i in selected_indices] - + # Show confirmation print(f"\nSelected {len(selected_modules)} module(s):") - for i, (module, agent, auto_init, build) in enumerate(selected_modules, 1): - agent_text = "with agent" if str_to_bool(agent) == "1" else "no agent" - print(f" {i}. {module} ({agent_text}, auto-init: {auto_init})") - + for i, module_config in enumerate(selected_modules, 1): + agent_text = "with agent" if module_config.uses_agent() else "no agent" + print(f" {i}. {module_config.name} ({agent_text}, auto-init: {module_config.java_agent_auto_init})") + confirm = input("\nProceed with these selections? [Y/n]: ").strip().lower() if confirm in ('', 'y', 'yes'): break else: print("Please make a new selection.") - + except ValueError as e: print(f"Error: {e}") print("Please try again.") except KeyboardInterrupt: print("\nOperation cancelled.") return InteractiveSelection(modules=[], manual_test_mode=False, build_agent=False) - + # Ask about test mode manual_test_mode = False while True: @@ -670,10 +694,10 @@ def interactive_module_selection(self) -> InteractiveSelection: except KeyboardInterrupt: print("\nOperation cancelled.") return InteractiveSelection(modules=[], manual_test_mode=False, build_agent=False) - + # Ask about building agent if any modules use it build_agent = False - has_agent_modules = any(str_to_bool(agent) == "1" for _, agent, _, _ in selected_modules) + has_agent_modules = any(module_config.uses_agent() for module_config in selected_modules) if has_agent_modules: while True: try: @@ -689,17 +713,17 @@ def interactive_module_selection(self) -> InteractiveSelection: except KeyboardInterrupt: print("\nOperation cancelled.") return InteractiveSelection(modules=[], manual_test_mode=False, build_agent=False) - + return InteractiveSelection(modules=selected_modules, manual_test_mode=manual_test_mode, build_agent=build_agent) - + def run_interactive_tests(self, agent: str, auto_init: str, build: str) -> int: """Run tests with interactive module selection.""" selection = self.interactive_module_selection() - + if selection.is_empty(): print("No modules selected. Exiting.") return 0 - + # Build agent JAR if requested and modules use agent if selection.build_agent and selection.has_agent_modules(): print("\nBuilding OpenTelemetry agent JAR...") @@ -708,55 +732,55 @@ def run_interactive_tests(self, agent: str, auto_init: str, build: str) -> int: print("Failed to build OpenTelemetry agent JAR") return build_result print("✅ OpenTelemetry agent JAR built successfully") - + # Handle manual test mode if selection.manual_test_mode: if not selection.is_single_module(): print("Error: Manual test mode can only be used with a single module.") print("Please select only one module for manual testing.") return 1 - - sample_module, test_agent, test_auto_init, test_build = selection.get_first_module() + + module_config = selection.get_first_module() # Convert true/false to internal 1/0 format - agent = str_to_bool(test_agent) - auto_init = test_auto_init # already in correct format - build = str_to_bool(test_build) - - print(f"\nSetting up manual test environment for: {sample_module}") - return self.run_manual_test_mode(sample_module, agent, auto_init, build) - + agent = str_to_bool(module_config.java_agent) + auto_init = module_config.java_agent_auto_init # already in correct format + build = str_to_bool(module_config.build_before_run) + + print(f"\nSetting up manual test environment for: {module_config.name}") + return self.run_manual_test_mode(module_config.name, agent, auto_init, build) + # Handle automatic test running failed_tests = [] - - for i, (sample_module, test_agent, test_auto_init, test_build) in enumerate(selection.modules, 1): + + for i, module_config in enumerate(selection.modules, 1): # Convert true/false to internal 1/0 format - agent = str_to_bool(test_agent) - auto_init = test_auto_init # already in correct format - build = str_to_bool(test_build) - - print(f"\n{'='*60}") - print(f"Running test {i}/{len(selection.modules)}: {sample_module}") - print(f"Agent: {test_agent}, Auto-init: {test_auto_init}") - print(f"{'='*60}") - - result = self.run_single_test(sample_module, agent, auto_init, build) - + agent = str_to_bool(module_config.java_agent) + auto_init = module_config.java_agent_auto_init # already in correct format + build = str_to_bool(module_config.build_before_run) + + print(f"\n{'='*TERMINAL_COLUMNS}") + print(f"Running test {i}/{len(selection.modules)}: {module_config.name}") + print(f"Agent: {module_config.java_agent}, Auto-init: {module_config.java_agent_auto_init}") + print(f"{'='*TERMINAL_COLUMNS}") + + result = self.run_single_test(module_config.name, agent, auto_init, build) + if result != 0: # Find the module number in the full list for interactive reference - module_number = self._find_module_number(sample_module, test_agent, test_auto_init) - failed_tests.append((module_number, sample_module, test_agent, test_auto_init)) - print(f"❌ Test failed: {sample_module}") + module_number = self._find_module_number(module_config.name, module_config.java_agent, module_config.java_agent_auto_init) + failed_tests.append((module_number, module_config.name, module_config.java_agent, module_config.java_agent_auto_init)) + print(f"❌ Test failed: {module_config.name}") else: - print(f"✅ Test passed: {sample_module}") - + print(f"✅ Test passed: {module_config.name}") + # Summary - print(f"\n{'='*60}") + print(f"\n{'='*TERMINAL_COLUMNS}") print("TEST SUMMARY") - print(f"{'='*60}") + print(f"{'='*TERMINAL_COLUMNS}") print(f"Total tests: {len(selection.modules)}") print(f"Passed: {len(selection.modules) - len(failed_tests)}") print(f"Failed: {len(failed_tests)}") - + if failed_tests: print("\nFailed tests (for interactive mode, use these numbers):") for module_number, sample_module, test_agent, test_auto_init in failed_tests: @@ -765,7 +789,7 @@ def run_interactive_tests(self, agent: str, auto_init: str, build: str) -> int: else: print("\n🎉 All tests passed!") return 0 - + def cleanup_on_exit(self, signum, frame): """Cleanup handler for signals.""" print(f"\nReceived signal {signum}, cleaning up...") @@ -776,7 +800,7 @@ def cleanup_on_exit(self, signum, frame): def main(): parser = argparse.ArgumentParser(description="System Test Runner for Sentry Java") subparsers = parser.add_subparsers(dest="command", help="Available commands") - + # Test subcommand test_parser = subparsers.add_parser("test", help="Run system tests") test_group = test_parser.add_mutually_exclusive_group(required=True) @@ -787,54 +811,54 @@ def main(): test_parser.add_argument("--auto-init", default="true", help="Auto-init agent (true or false)") test_parser.add_argument("--build", default="false", help="Build before running (true or false)") test_parser.add_argument("--manual-test", action="store_true", help="Set up infrastructure but pause for manual testing from IDE") - + # Spring subcommand spring_parser = subparsers.add_parser("spring", help="Manage Spring Boot applications") spring_subparsers = spring_parser.add_subparsers(dest="spring_action", help="Spring actions") - + spring_start_parser = spring_subparsers.add_parser("start", help="Start Spring Boot application") spring_start_parser.add_argument("module", help="Sample module to start") spring_start_parser.add_argument("--agent", default="false", help="Use Java agent (true or false)") spring_start_parser.add_argument("--auto-init", default="true", help="Auto-init agent (true or false)") spring_start_parser.add_argument("--build", default="false", help="Build before starting (true or false)") - + spring_stop_parser = spring_subparsers.add_parser("stop", help="Stop Spring Boot application") - + spring_wait_parser = spring_subparsers.add_parser("wait", help="Wait for Spring Boot application to be ready") spring_wait_parser.add_argument("--timeout", type=int, default=20, help="Max attempts to wait (default: 20)") - + spring_status_parser = spring_subparsers.add_parser("status", help="Check Spring Boot application status") - + # Sentry subcommand sentry_parser = subparsers.add_parser("sentry", help="Manage Sentry mock server") sentry_subparsers = sentry_parser.add_subparsers(dest="sentry_action", help="Sentry actions") - + sentry_start_parser = sentry_subparsers.add_parser("start", help="Start Sentry mock server") sentry_stop_parser = sentry_subparsers.add_parser("stop", help="Stop Sentry mock server") sentry_status_parser = sentry_subparsers.add_parser("status", help="Check Sentry mock server status") - + # Status subcommand status_parser = subparsers.add_parser("status", help="Show status of all services") - + args = parser.parse_args() - + if not args.command: parser.print_help() return 1 - + runner = SystemTestRunner() - + # Set up signal handlers for cleanup signal.signal(signal.SIGINT, runner.cleanup_on_exit) signal.signal(signal.SIGTERM, runner.cleanup_on_exit) - + try: if args.command == "test": # Convert true/false arguments to internal 1/0 format agent = str_to_bool(args.agent) auto_init = args.auto_init # already accepts true/false build = str_to_bool(args.build) - + if args.manual_test and args.module: return runner.run_manual_test_mode(args.module, agent, auto_init, build) elif args.manual_test and args.all: @@ -849,14 +873,14 @@ def main(): return runner.run_single_test(args.module, agent, auto_init, build) elif args.interactive: return runner.run_interactive_tests(agent, auto_init, build) - + elif args.command == "spring": if args.spring_action == "start": # Convert true/false arguments to internal format agent = str_to_bool(args.agent) auto_init = args.auto_init # already accepts true/false build = str_to_bool(args.build) - + # Build if requested if build == "1": print("Building before starting Spring application") @@ -864,7 +888,7 @@ def main(): if build_result != 0: print("Build failed") return build_result - + runner.start_spring_server(args.module, agent, auto_init) if runner.wait_for_spring(): print("Spring application started successfully!") @@ -893,7 +917,7 @@ def main(): else: spring_parser.print_help() return 1 - + elif args.command == "sentry": if args.sentry_action == "start": runner.start_sentry_mock_server() @@ -913,14 +937,14 @@ def main(): else: sentry_parser.print_help() return 1 - + elif args.command == "status": runner.print_status_summary() return 0 else: parser.print_help() return 1 - + except KeyboardInterrupt: print("\nInterrupted by user") return 1 @@ -934,4 +958,4 @@ def main(): runner.stop_sentry_mock_server() if __name__ == "__main__": - sys.exit(main()) \ No newline at end of file + sys.exit(main()) From b5260f4b9457499106c7e030b074365a01cf27c2 Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 14 Jul 2025 18:54:17 +0200 Subject: [PATCH 12/17] improve --- test/system-test-runner.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/system-test-runner.py b/test/system-test-runner.py index 9e906788345..cbb2bddc6e3 100644 --- a/test/system-test-runner.py +++ b/test/system-test-runner.py @@ -42,7 +42,7 @@ from typing import Optional, List, Tuple from dataclasses import dataclass -TERMINAL_COLUMNS = os.get_terminal_size()[0] or 60 +TERMINAL_COLUMNS: int = os.get_terminal_size().columns() def str_to_bool(value: str) -> str: """Convert true/false string to 1/0 string for internal compatibility.""" @@ -589,8 +589,8 @@ def _find_module_number(self, module_name: str, agent: str, auto_init: str) -> i """Find the module number in the interactive list (1-based).""" modules = self.get_available_modules() for i, module_config in enumerate(modules, 1): - if (module_config.name == module_name and - module_config.java_agent == agent and + if (module_config.name == module_name and + module_config.java_agent == agent and module_config.java_agent_auto_init == auto_init): return i return 0 # Should not happen, but return 0 if not found From 26a35030f76f3d6e1c95093339e9e47ff1f477d7 Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 14 Jul 2025 18:57:55 +0200 Subject: [PATCH 13/17] improve --- test/system-test-runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/system-test-runner.py b/test/system-test-runner.py index cbb2bddc6e3..801dbfa9b10 100644 --- a/test/system-test-runner.py +++ b/test/system-test-runner.py @@ -42,7 +42,7 @@ from typing import Optional, List, Tuple from dataclasses import dataclass -TERMINAL_COLUMNS: int = os.get_terminal_size().columns() +TERMINAL_COLUMNS: int = os.get_terminal_size().columns def str_to_bool(value: str) -> str: """Convert true/false string to 1/0 string for internal compatibility.""" From 8db5618a17bd811f2cb2f65377d37421f60a3504 Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 14 Jul 2025 19:01:22 +0200 Subject: [PATCH 14/17] improve --- .../src/test/kotlin/io/sentry/DummyTest.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 sentry-samples/sentry-samples-console/src/test/kotlin/io/sentry/DummyTest.kt diff --git a/sentry-samples/sentry-samples-console/src/test/kotlin/io/sentry/DummyTest.kt b/sentry-samples/sentry-samples-console/src/test/kotlin/io/sentry/DummyTest.kt new file mode 100644 index 00000000000..6f762b7e453 --- /dev/null +++ b/sentry-samples/sentry-samples-console/src/test/kotlin/io/sentry/DummyTest.kt @@ -0,0 +1,12 @@ +package io.sentry + +import kotlin.test.Test +import kotlin.test.assertTrue + +class DummyTest { + @Test + fun `the only test`() { + // only needed to have more than 0 tests and not fail the build + assertTrue(true) + } +} From 46e57a8450fe344d672830193bedf5b4aec41fc1 Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 14 Jul 2025 19:28:02 +0200 Subject: [PATCH 15/17] improve --- test/system-test-runner.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/system-test-runner.py b/test/system-test-runner.py index 801dbfa9b10..a9903b49976 100644 --- a/test/system-test-runner.py +++ b/test/system-test-runner.py @@ -42,7 +42,11 @@ from typing import Optional, List, Tuple from dataclasses import dataclass -TERMINAL_COLUMNS: int = os.get_terminal_size().columns +TERMINAL_COLUMNS: int = 60 +try: + TERMINAL_COLUMNS: int = os.get_terminal_size().columns +except: + pass def str_to_bool(value: str) -> str: """Convert true/false string to 1/0 string for internal compatibility.""" From d209e89086d9a3d8b1b325171de84e112ba11faf Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 14 Jul 2025 19:30:47 +0200 Subject: [PATCH 16/17] improve --- .../java/io/sentry/samples/console/Main.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/sentry-samples/sentry-samples-console/src/main/java/io/sentry/samples/console/Main.java b/sentry-samples/sentry-samples-console/src/main/java/io/sentry/samples/console/Main.java index 86ab6a54f39..16f5a09d1b4 100644 --- a/sentry-samples/sentry-samples-console/src/main/java/io/sentry/samples/console/Main.java +++ b/sentry-samples/sentry-samples-console/src/main/java/io/sentry/samples/console/Main.java @@ -82,16 +82,15 @@ public static void main(String[] args) throws InterruptedException { options.setTracesSampleRate(1.0); // set 0.5 to send 50% of traces // Determine traces sample rate based on the sampling context - // options.setTracesSampler( - // context -> { - // // only 10% of transactions with "/product" prefix will be collected - // if (!context.getTransactionContext().getName().startsWith("/products")) - // { - // return 0.1; - // } else { - // return 0.5; - // } - // }); + // options.setTracesSampler( + // context -> { + // // only 10% of transactions with "/product" prefix will be collected + // if (!context.getTransactionContext().getName().startsWith("/products")) { + // return 0.1; + // } else { + // return 0.5; + // } + // }); }); Sentry.addBreadcrumb( From 6cf610a2b1a617e89cec66a305c9569542b7cb06 Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 14 Jul 2025 19:33:56 +0200 Subject: [PATCH 17/17] improve --- test/system-test-runner.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/system-test-runner.py b/test/system-test-runner.py index a9903b49976..619e6bda104 100644 --- a/test/system-test-runner.py +++ b/test/system-test-runner.py @@ -494,14 +494,14 @@ def run_all_tests(self) -> int: failed_tests = [] - for module_config in test_configs: + for i, module_config in enumerate(test_configs): # Convert true/false to internal 1/0 format agent = str_to_bool(module_config.java_agent) auto_init = module_config.java_agent_auto_init # already in correct format build = str_to_bool(module_config.build_before_run) print(f"\n{'='*TERMINAL_COLUMNS}") - print(f"Running test: {module_config.name} (agent={module_config.java_agent}, auto_init={module_config.java_agent_auto_init})") + print(f"Running test {i + 1}/{len(test_configs)}: {module_config.name} (agent={module_config.java_agent}, auto_init={module_config.java_agent_auto_init})") print(f"{'='*TERMINAL_COLUMNS}") result = self.run_single_test(module_config.name, agent, auto_init, build)