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

1""" 

2Integration testing execution engine. 

3 

4This module contains the core test execution logic for running 

5integration tests with timeout handling and concurrent execution. 

6""" 

7 

8import asyncio 

9import time 

10from concurrent.futures import ThreadPoolExecutor, as_completed 

11from typing import Callable, List, Optional 

12 

13from .models import IntegrationTestResult, TestStatus 

14 

15 

16class TestEngine: 

17 """ 

18 Core test execution engine for integration testing. 

19 

20 Handles test execution, timeout management, and concurrent execution. 

21 """ 

22 

23 def __init__(self, test_timeout: float = 30.0, max_workers: int = 4): 

24 """ 

25 Initialize the test engine. 

26 

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") 

35 

36 self.test_timeout = test_timeout 

37 self.max_workers = max_workers 

38 self._test_counter = 0 

39 

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}" 

44 

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. 

50 

51 Args: 

52 test_func: Test function to execute 

53 test_name: Optional test name 

54 components: List of components being tested 

55 

56 Returns: 

57 IntegrationTestResult: Test execution result 

58 """ 

59 if test_name is None: 

60 test_name = self._generate_test_id() 

61 

62 if components is None: 

63 components = [] 

64 

65 start_time = time.time() 

66 result = IntegrationTestResult( 

67 test_name=test_name, passed=False, components_tested=components 

68 ) 

69 

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 ) 

80 

81 except TimeoutError: 

82 result.error_message = f"Test timed out after {self.test_timeout}s" 

83 result.status = TestStatus.FAILED 

84 

85 except Exception as e: 

86 result.error_message = str(e) 

87 result.status = TestStatus.FAILED 

88 

89 finally: 

90 result.execution_time = time.time() - start_time 

91 

92 return result 

93 

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. 

99 

100 Args: 

101 test_func: Test function to execute 

102 test_name: Optional test name 

103 components: List of components being tested 

104 

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 ) 

112 

113 def run_concurrent_tests( 

114 self, tests: List[tuple], timeout: Optional[float] = None 

115 ) -> List[IntegrationTestResult]: 

116 """ 

117 Run multiple tests concurrently. 

118 

119 Args: 

120 tests: List of (test_func, test_name, components) tuples 

121 timeout: Optional timeout for entire batch 

122 

123 Returns: 

124 List of test results 

125 """ 

126 if timeout is None: 

127 timeout = self.test_timeout * 2 

128 

129 results = [] 

130 

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 } 

136 

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) 

150 

151 return results 

152 

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. 

158 

159 Args: 

160 tests: List of (test_func, test_name, components) tuples 

161 timeout: Optional timeout for entire batch 

162 

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 )