Coverage for src / moai_adk / core / integration / engine.py: 20.00%
60 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-20 20:52 +0900
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-20 20:52 +0900
1"""
2Integration testing execution engine.
4This module contains the core test execution logic for running
5integration tests with timeout handling and concurrent execution.
6"""
8import asyncio
9import time
10from concurrent.futures import ThreadPoolExecutor, as_completed
11from typing import Callable, List, Optional
13from .models import IntegrationTestResult, TestStatus
16class TestEngine:
17 """
18 Core test execution engine for integration testing.
20 Handles test execution, timeout management, and concurrent execution.
21 """
23 def __init__(self, test_timeout: float = 30.0, max_workers: int = 4):
24 """
25 Initialize the test engine.
27 Args:
28 test_timeout: Maximum time (in seconds) for each test
29 max_workers: Maximum number of concurrent workers
30 """
31 if test_timeout <= 0:
32 raise ValueError("Test timeout must be positive")
33 if max_workers <= 0:
34 raise ValueError("Max workers must be positive")
36 self.test_timeout = test_timeout
37 self.max_workers = max_workers
38 self._test_counter = 0
40 def _generate_test_id(self) -> str:
41 """Generate a unique test ID."""
42 self._test_counter += 1
43 return f"test_{self._test_counter:04d}"
45 def execute_test(
46 self, test_func: Callable, test_name: str = None, components: List[str] = None
47 ) -> IntegrationTestResult:
48 """
49 Execute a single integration test.
51 Args:
52 test_func: Test function to execute
53 test_name: Optional test name
54 components: List of components being tested
56 Returns:
57 IntegrationTestResult: Test execution result
58 """
59 if test_name is None:
60 test_name = self._generate_test_id()
62 if components is None:
63 components = []
65 start_time = time.time()
66 result = IntegrationTestResult(
67 test_name=test_name, passed=False, components_tested=components
68 )
70 try:
71 # Execute test with timeout
72 with ThreadPoolExecutor(max_workers=1) as executor:
73 future = executor.submit(test_func)
74 try:
75 test_result = future.result(timeout=self.test_timeout)
76 result.passed = bool(test_result)
77 result.status = (
78 TestStatus.PASSED if result.passed else TestStatus.FAILED
79 )
81 except TimeoutError:
82 result.error_message = f"Test timed out after {self.test_timeout}s"
83 result.status = TestStatus.FAILED
85 except Exception as e:
86 result.error_message = str(e)
87 result.status = TestStatus.FAILED
89 finally:
90 result.execution_time = time.time() - start_time
92 return result
94 async def execute_test_async(
95 self, test_func: Callable, test_name: str = None, components: List[str] = None
96 ) -> IntegrationTestResult:
97 """
98 Execute a single integration test asynchronously.
100 Args:
101 test_func: Test function to execute
102 test_name: Optional test name
103 components: List of components being tested
105 Returns:
106 IntegrationTestResult: Test execution result
107 """
108 loop = asyncio.get_event_loop()
109 return await loop.run_in_executor(
110 None, self.execute_test, test_func, test_name, components
111 )
113 def run_concurrent_tests(
114 self, tests: List[tuple], timeout: Optional[float] = None
115 ) -> List[IntegrationTestResult]:
116 """
117 Run multiple tests concurrently.
119 Args:
120 tests: List of (test_func, test_name, components) tuples
121 timeout: Optional timeout for entire batch
123 Returns:
124 List of test results
125 """
126 if timeout is None:
127 timeout = self.test_timeout * 2
129 results = []
131 with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
132 # Submit all tests
133 future_to_test = {
134 executor.submit(self.execute_test, *test): test for test in tests
135 }
137 # Collect results as they complete
138 for future in as_completed(future_to_test, timeout=timeout):
139 try:
140 result = future.result()
141 results.append(result)
142 except Exception as e:
143 test_info = future_to_test[future]
144 error_result = IntegrationTestResult(
145 test_name=test_info[1] if len(test_info) > 1 else "unknown",
146 passed=False,
147 error_message=f"Execution error: {str(e)}",
148 )
149 results.append(error_result)
151 return results
153 async def run_concurrent_tests_async(
154 self, tests: List[tuple], timeout: Optional[float] = None
155 ) -> List[IntegrationTestResult]:
156 """
157 Run multiple tests concurrently asynchronously.
159 Args:
160 tests: List of (test_func, test_name, components) tuples
161 timeout: Optional timeout for entire batch
163 Returns:
164 List of test results
165 """
166 loop = asyncio.get_event_loop()
167 return await loop.run_in_executor(
168 None, self.run_concurrent_tests, tests, timeout
169 )