""" Pytest配置文件 本模块提供全局的pytest fixtures、hooks和配置,用于支持测试的执行和管理。 包括测试环境设置、模拟配置、测试数据管理等。 """ import os import sys import pytest import tempfile import shutil from pathlib import Path from unittest.mock import Mock, AsyncMock from typing import Dict, List, Any, Optional, Generator import json import time # 添加项目根目录到Python路径 project_root = Path(__file__).parent.parent sys.path.insert(0, str(project_root)) # 导入项目模块 from src.utils.config import get_config, ConfigManager from src.verify.verification_types import ( VerificationStatus, VerificationType, CBMCConfiguration, VerificationResult, VerificationIssue ) from src.parser.metadata import FunctionInfo, ParameterInfo, SourceLocation from src.mutate.mutation_types import MutationType, MutationResult # ============================================================================= # 全局配置和设置 # ============================================================================= def pytest_configure(config): """pytest配置hook""" # 注册自定义标记 config.addinivalue_line( "markers", "performance: Tests that measure performance metrics" ) config.addinivalue_line( "markers", "slow: Tests that take a long time to run" ) config.addinivalue_line( "markers", "requires_cbmc: Tests that require CBMC installation" ) config.addinivalue_line( "markers", "requires_freertos: Tests that require FreeRTOS availability" ) config.addinivalue_line( "markers", "external: Tests that require external services" ) def pytest_collection_modifyitems(config, items): """修改测试收集的hook""" # 自动为慢速测试添加slow标记 for item in items: # 根据测试名称或模块自动添加标记 if "performance" in item.nodeid: item.add_marker(pytest.mark.performance) item.add_marker(pytest.mark.slow) if "regression" in item.nodeid: item.add_marker(pytest.mark.regression) if "freertos" in item.nodeid: item.add_marker(pytest.mark.freertos) item.add_marker(pytest.mark.requires_freertos) if "integration" in item.nodeid: item.add_marker(pytest.mark.integration) if "unit" in item.nodeid: item.add_marker(pytest.mark.unit) # ============================================================================= # 跳过条件hook # ============================================================================= def pytest_runtest_setup(item): """测试设置前hook""" # 检查CBMC可用性 if item.get_closest_marker("requires_cbmc"): if not shutil.which("cbmc"): pytest.skip("CBMC not installed") # 检查FreeRTOS可用性 if item.get_closest_marker("requires_freertos"): if not os.environ.get("FREERTOS_PATH"): pytest.skip("FreeRTOS headers not available") # 检查网络连接 if item.get_closest_marker("network"): try: import socket socket.create_connection(("www.google.com", 80), timeout=5) except OSError: pytest.skip("Network connection not available") # ============================================================================= # 基础配置fixtures # ============================================================================= @pytest.fixture(scope="session") def project_root() -> Path: """项目根目录路径""" return Path(__file__).parent.parent @pytest.fixture(scope="session") def test_config(): """测试配置""" return get_config() @pytest.fixture(scope="session") def temp_dir() -> Generator[Path, None, None]: """临时目录fixture""" temp_path = Path(tempfile.mkdtemp()) try: yield temp_path finally: shutil.rmtree(temp_path, ignore_errors=True) @pytest.fixture(scope="session") def config_manager(): """配置管理器fixture""" return ConfigManager() # ============================================================================= # CBMC相关fixtures # ============================================================================= @pytest.fixture(scope="session") def cbmc_config() -> CBMCConfiguration: """CBMC配置fixture""" return CBMCConfiguration( path="cbmc", timeout=60, unwind_depth=20, memory_model="simple", output_format="text", check_assertions=True, check_overflow=True, check_bounds=True, max_concurrent=2 ) @pytest.fixture(scope="session") def cbmc_available() -> bool: """检查CBMC是否可用""" return shutil.which("cbmc") is not None @pytest.fixture(scope="session") def cbmc_version(cbmc_available) -> Optional[str]: """获取CBMC版本""" if not cbmc_available: return None try: import subprocess result = subprocess.run( ["cbmc", "--version"], capture_output=True, text=True, timeout=10 ) if result.returncode == 0: return result.stdout.strip().split()[1] if " " in result.stdout.strip() else "unknown" except (subprocess.TimeoutExpired, subprocess.CalledProcessError): pass return "unknown" # ============================================================================= # FreeRTOS相关fixtures # ============================================================================= @pytest.fixture(scope="session") def freertos_available() -> bool: """检查FreeRTOS是否可用""" freertos_path = os.environ.get("FREERTOS_PATH") if not freertos_path: return False freertos_h = Path(freertos_path) / "FreeRTOS" / "Source" / "include" / "FreeRTOS.h" return freertos_h.exists() @pytest.fixture(scope="session") def freertos_config() -> Dict[str, Any]: """FreeRTOS配置fixture""" return { "freertos_mode": True, "freertos_include_path": "/opt/freertos/FreeRTOS/Source/include", "define_macros": [ "FREERTOS", "CBMC", "configUSE_PREEMPTION=1", "configMAX_PRIORITIES=5", "configMINIMAL_STACK_SIZE=128" ], "include_paths": [ "/opt/freertos/FreeRTOS/Source/include", "/opt/freertos/FreeRTOS/Source/portable/GCC/Linux" ] } # ============================================================================= # 函数元数据fixtures # ============================================================================= @pytest.fixture def simple_function() -> FunctionInfo: """简单函数元数据fixture""" return FunctionInfo( name="simple_function", return_type="int", parameters=[ ParameterInfo(name="a", type_name="int", is_pointer=False), ParameterInfo(name="b", type_name="int", is_pointer=False) ], source_location=SourceLocation(file="simple.c", line=1, column=1), complexity_score=0.3 ) @pytest.fixture def pointer_function() -> FunctionInfo: """指针函数元数据fixture""" return FunctionInfo( name="pointer_function", return_type="void", parameters=[ ParameterInfo(name="buffer", type_name="char*", is_pointer=True), ParameterInfo(name="size", type_name="size_t", is_pointer=False) ], source_location=SourceLocation(file="pointer.c", line=5, column=1), complexity_score=0.6 ) @pytest.fixture def complex_function() -> FunctionInfo: """复杂函数元数据fixture""" return FunctionInfo( name="complex_function", return_type="int*", parameters=[ ParameterInfo(name="array", type_name="int*", is_pointer=True), ParameterInfo(name="length", type_name="int", is_pointer=False), ParameterInfo(name="callback", type_name="int(*)(int)", is_pointer=False) ], source_location=SourceLocation(file="complex.c", line=10, column=1), complexity_score=0.8 ) @pytest.fixture def function_fixtures() -> Dict[str, FunctionInfo]: """函数元数据fixture集合""" return { "simple": FunctionInfo( name="add", return_type="int", parameters=[ ParameterInfo(name="a", type_name="int", is_pointer=False), ParameterInfo(name="b", type_name="int", is_pointer=False) ], complexity_score=0.3 ), "pointer": FunctionInfo( name="copy_string", return_type="void", parameters=[ ParameterInfo(name="dest", type_name="char*", is_pointer=True), ParameterInfo(name="src", type_name="const char*", is_pointer=True), ParameterInfo(name="size", type_name="size_t", is_pointer=False) ], complexity_score=0.6 ), "complex": FunctionInfo( name="process_data", return_type="int*", parameters=[ ParameterInfo(name="data", type_name="int*", is_pointer=True), ParameterInfo(name="count", type_name="int", is_pointer=False), ParameterInfo(name="processor", type_name="int(*)(int)", is_pointer=False) ], complexity_score=0.9 ) } # ============================================================================= # 验证结果fixtures # ============================================================================= @pytest.fixture def verification_success_result() -> VerificationResult: """成功验证结果fixture""" return VerificationResult( status=VerificationStatus.SUCCESSFUL, function_name="test_function", harness_file="test_harness.c", start_time=time.time() - 1.0, end_time=time.time(), duration=1.0, issues=[], cbmc_output="VERIFICATION SUCCESSFUL", cbmc_command="cbmc test_harness.c" ) @pytest.fixture def verification_failure_result() -> VerificationResult: """失败验证结果fixture""" return VerificationResult( status=VerificationStatus.FAILED, function_name="test_function", harness_file="test_harness.c", start_time=time.time() - 1.0, end_time=time.time(), duration=1.0, issues=[ VerificationIssue( issue_type="assertion_failure", description="Assertion failed: buffer size check", severity="error", source_location=SourceLocation(file="test.c", line=15, column=5) ) ], cbmc_output="VERIFICATION FAILED", cbmc_command="cbmc test_harness.c" ) @pytest.fixture def verification_timeout_result() -> VerificationResult: """超时验证结果fixture""" return VerificationResult( status=VerificationStatus.TIMEOUT, function_name="test_function", harness_file="test_harness.c", start_time=time.time() - 60.0, end_time=time.time(), duration=60.0, issues=[ VerificationIssue( issue_type="timeout", description="CBMC execution timed out", severity="error" ) ], cbmc_output="TIMEOUT", cbmc_command="cbmc test_harness.c" ) # ============================================================================= # 突变结果fixtures # ============================================================================= @pytest.fixture def mutation_success_result() -> MutationResult: """成功突变结果fixture""" return MutationResult( specification="void test_harness() { __CPROVER_assert(1 == 1); }", mutation_type=MutationType.PREDICATE_MUTATION, confidence=0.85, description="Basic assertion mutation" ) @pytest.fixture def mutation_results_fixtures() -> List[MutationResult]: """突变结果fixture集合""" return [ MutationResult( specification="void test_harness() { __CPROVER_assert(x > 0); }", mutation_type=MutationType.PREDICATE_MUTATION, confidence=0.8, description="Positive value check" ), MutationResult( specification="void test_harness() { __CPROVER_assert(y != NULL); }", mutation_type=MutationType.NULL_POINTER_MUTATION, confidence=0.9, description="Null pointer check" ), MutationResult( specification="void test_harness() { __CPROVER_assert(size > 0 && size < 1000); }", mutation_type=MutationType.BOUNDS_MUTATION, confidence=0.75, description="Bounds check" ) ] # ============================================================================= # 性能测试fixtures # ============================================================================= @pytest.fixture def performance_config(): """性能测试配置fixture""" return { "iterations": 100, "timeout": 300, "warmup_iterations": 10, "memory_threshold_mb": 100, "time_threshold_seconds": 30 } @pytest.fixture def benchmark_functions(): """基准测试函数fixture""" return [ {"name": "simple_add", "code": "int add(int a, int b) { return a + b; }"}, {"name": "loop_sum", "code": "int sum(int n) { int s = 0; for(int i=0; i