"""
Patcher框架用法测试模块
本模块专注于测试patcher的各种实际使用场景,包括:
- diff生成的ground truth对比
- 各种补丁模式的实际效果验证
- mock target和mock replacement的完整测试
- 边界情况和错误处理
测试设计原则:
- 每个测试都有明确的期望值(ground truth)进行对比
- 使用mock模块模拟真实环境
- 覆盖常见用法和边界情况
"""
import importlib.util
import os
import sys
import types
import unittest
from typing import Dict, List
_project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
_patcher_dir = os.path.join(_project_root, "mx_driving", "patcher")
def _load_module_from_file(module_name: str, file_path: str):
"""Load a module directly from file without triggering parent package __init__.py."""
spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
return module
_patcher_logger_module = _load_module_from_file(
"mx_driving.patcher.patcher_logger",
os.path.join(_patcher_dir, "patcher_logger.py")
)
_reporting = _load_module_from_file(
"mx_driving.patcher.reporting",
os.path.join(_patcher_dir, "reporting.py")
)
_version_module = _load_module_from_file(
"mx_driving.patcher.version",
os.path.join(_patcher_dir, "version.py")
)
_patch_module = _load_module_from_file(
"mx_driving.patcher.patch",
os.path.join(_patcher_dir, "patch.py")
)
_patcher_module = _load_module_from_file(
"mx_driving.patcher.patcher",
os.path.join(_patcher_dir, "patcher.py")
)
AtomicPatch = _patch_module.AtomicPatch
BasePatch = _patch_module.BasePatch
LegacyPatch = _patch_module.LegacyPatch
Patch = _patch_module.Patch
RegistryPatch = _patch_module.RegistryPatch
Patcher = _patcher_module.Patcher
PatchResult = _reporting.PatchResult
PatchStatus = _reporting.PatchStatus
_get_source_diff = _patch_module._get_source_diff
_get_callable_name = _patch_module._get_callable_name
def mock_original_add(a, b):
"""原始加法函数"""
return a + b
def mock_original_multiply(a, b):
"""原始乘法函数"""
return a * b
def mock_original_process(data):
"""原始数据处理函数"""
return [x * 2 for x in data]
class MockOriginalClass:
"""原始类,用于测试类方法补丁"""
def __init__(self, value):
self.value = value
def compute(self, x):
"""原始计算方法"""
return self.value + x
@staticmethod
def static_compute(x):
"""原始静态方法"""
return x * 2
@classmethod
def class_compute(cls, x):
"""原始类方法"""
return x * 3
def mock_replacement_add(a, b):
"""替换加法函数 - 返回a + b + 100"""
return a + b + 100
def mock_replacement_multiply(a, b):
"""替换乘法函数 - 返回a * b * 10"""
return a * b * 10
def mock_replacement_process(data):
"""替换数据处理函数 - 返回每个元素的平方"""
return [x ** 2 for x in data]
class MockReplacementClass:
"""替换类"""
def __init__(self, value):
self.value = value * 10
def compute(self, x):
"""替换计算方法"""
return self.value * x
@staticmethod
def static_compute(x):
"""替换静态方法"""
return x ** 2
@classmethod
def class_compute(cls, x):
"""替换类方法"""
return x ** 3
class TestDiffGenerationGroundTruth(unittest.TestCase):
"""
Diff生成测试 - 使用ground truth进行对比
测试目的:验证_get_source_diff生成的diff内容正确,
包含预期的差异标记和代码变化。
"""
def test_diff_contains_function_names(self):
"""
测试diff包含函数名称
Ground Truth:
- diff应包含"original:"和"replacement:"标记
- diff应包含原始函数名和替换函数名
"""
diff = _get_source_diff(mock_original_add, mock_replacement_add)
self.assertIn("original:", diff)
self.assertIn("replacement:", diff)
self.assertIn("mock_original_add", diff)
self.assertIn("mock_replacement_add", diff)
def test_diff_contains_code_changes(self):
"""
测试diff包含代码变化
Ground Truth:
- diff应包含"-"标记表示删除的行
- diff应包含"+"标记表示添加的行
- diff应包含实际的代码差异
"""
diff = _get_source_diff(mock_original_add, mock_replacement_add)
self.assertIn("-", diff)
self.assertIn("+", diff)
self.assertIn("return", diff)
def test_diff_unified_format(self):
"""
测试diff使用unified格式
Ground Truth:
- diff应以"---"开头表示原始文件
- diff应包含"+++"表示新文件
- diff应包含"@@"表示变化位置
"""
diff = _get_source_diff(mock_original_add, mock_replacement_add)
self.assertIn("---", diff)
self.assertIn("+++", diff)
self.assertIn("@@", diff)
def test_diff_empty_for_identical_functions(self):
"""
测试相同函数的diff为空或只有头部
Ground Truth:
- 当两个函数完全相同时,diff应该没有实际的代码变化
"""
diff = _get_source_diff(mock_original_add, mock_original_add)
lines = diff.split('\n')
change_lines = [l for l in lines if l.startswith('+') or l.startswith('-')]
actual_changes = [l for l in change_lines if not l.startswith('+++') and not l.startswith('---')]
self.assertEqual(len(actual_changes), 0)
def test_diff_for_class_methods(self):
"""
测试类方法的diff生成
Ground Truth:
- diff应正确显示类方法的变化
- 应包含方法签名和实现的差异
"""
diff = _get_source_diff(
MockOriginalClass.compute,
MockReplacementClass.compute
)
self.assertIn("compute", diff)
self.assertIn("self", diff)
def test_diff_builtin_returns_empty(self):
"""
测试内置函数的diff返回空字符串
Ground Truth:
- 内置函数没有Python源码
- _get_source_diff应返回空字符串
"""
diff = _get_source_diff(len, str)
self.assertEqual(diff, "")
class TestBasicReplacementGroundTruth(unittest.TestCase):
"""
基本替换功能测试 - 使用ground truth验证替换效果
"""
def setUp(self):
"""创建测试模块"""
self.mock_module = types.ModuleType('usage_test_module')
self.mock_module.add = mock_original_add
self.mock_module.multiply = mock_original_multiply
self.mock_module.process = mock_original_process
sys.modules['usage_test_module'] = self.mock_module
def tearDown(self):
"""清理测试模块"""
if 'usage_test_module' in sys.modules:
del sys.modules['usage_test_module']
def test_function_replacement_ground_truth(self):
"""
测试函数替换的ground truth
Ground Truth:
- 原始: add(3, 5) = 8
- 替换后: add(3, 5) = 108 (3 + 5 + 100)
"""
self.assertEqual(self.mock_module.add(3, 5), 8)
patch = AtomicPatch("usage_test_module.add", mock_replacement_add)
patch.apply()
self.assertEqual(self.mock_module.add(3, 5), 108)
def test_multiple_replacements_ground_truth(self):
"""
测试多个函数替换的ground truth
Ground Truth:
- add(2, 3) = 5 -> 105
- multiply(2, 3) = 6 -> 60
- process([1,2,3]) = [2,4,6] -> [1,4,9]
"""
self.assertEqual(self.mock_module.add(2, 3), 5)
self.assertEqual(self.mock_module.multiply(2, 3), 6)
self.assertEqual(self.mock_module.process([1, 2, 3]), [2, 4, 6])
patcher = Patcher()
patcher.add(
AtomicPatch("usage_test_module.add", mock_replacement_add),
AtomicPatch("usage_test_module.multiply", mock_replacement_multiply),
AtomicPatch("usage_test_module.process", mock_replacement_process),
)
patcher.apply()
self.assertEqual(self.mock_module.add(2, 3), 105)
self.assertEqual(self.mock_module.multiply(2, 3), 60)
self.assertEqual(self.mock_module.process([1, 2, 3]), [1, 4, 9])
def test_string_path_replacement_ground_truth(self):
"""
测试字符串路径替换的ground truth
Ground Truth:
- 使用字符串路径指定replacement
- 替换效果与直接传入函数相同
"""
self.mock_module.replacement_add = mock_replacement_add
self.assertEqual(self.mock_module.add(10, 20), 30)
patch = AtomicPatch(
"usage_test_module.add",
"usage_test_module.replacement_add"
)
patch.apply()
self.assertEqual(self.mock_module.add(10, 20), 130)
class TestWrapperModesGroundTruth(unittest.TestCase):
"""
Wrapper模式测试 - 验证target_wrapper和replacement_wrapper的效果
"""
def setUp(self):
"""创建测试模块"""
self.mock_module = types.ModuleType('wrapper_test_module')
self.mock_module.add = mock_original_add
self.mock_module.multiply = mock_original_multiply
sys.modules['wrapper_test_module'] = self.mock_module
def tearDown(self):
"""清理测试模块"""
if 'wrapper_test_module' in sys.modules:
del sys.modules['wrapper_test_module']
def test_target_wrapper_ground_truth(self):
"""
测试target_wrapper的ground truth
Ground Truth:
- 原始: add(3, 5) = 8
- wrapper: 在原始结果上乘以10
- 包装后: add(3, 5) = 80
"""
def multiply_result_wrapper(original_func):
def wrapped(a, b):
result = original_func(a, b)
return result * 10
return wrapped
self.assertEqual(self.mock_module.add(3, 5), 8)
patch = AtomicPatch(
"wrapper_test_module.add",
target_wrapper=multiply_result_wrapper
)
patch.apply()
self.assertEqual(self.mock_module.add(3, 5), 80)
def test_replacement_wrapper_ground_truth(self):
"""
测试replacement_wrapper的ground truth
Ground Truth:
- replacement: add(a, b) = a + b + 100
- wrapper: 在replacement结果上加1000
- 最终: add(3, 5) = 108 + 1000 = 1108
"""
def add_thousand_wrapper(replacement_func):
def wrapped(a, b):
result = replacement_func(a, b)
return result + 1000
return wrapped
self.assertEqual(self.mock_module.add(3, 5), 8)
patch = AtomicPatch(
"wrapper_test_module.add",
mock_replacement_add,
replacement_wrapper=add_thousand_wrapper
)
patch.apply()
self.assertEqual(self.mock_module.add(3, 5), 1108)
def test_wrapper_with_logging_ground_truth(self):
"""
测试带日志功能的wrapper
Ground Truth:
- wrapper记录调用参数和结果
- 原始功能不变
"""
call_log = []
def logging_wrapper(original_func):
def wrapped(a, b):
result = original_func(a, b)
call_log.append({'args': (a, b), 'result': result})
return result
return wrapped
patch = AtomicPatch(
"wrapper_test_module.add",
target_wrapper=logging_wrapper
)
patch.apply()
result1 = self.mock_module.add(1, 2)
result2 = self.mock_module.add(10, 20)
self.assertEqual(result1, 3)
self.assertEqual(result2, 30)
self.assertEqual(len(call_log), 2)
self.assertEqual(call_log[0], {'args': (1, 2), 'result': 3})
self.assertEqual(call_log[1], {'args': (10, 20), 'result': 30})
class TestRuntimeCheckGroundTruth(unittest.TestCase):
"""
Runtime Check测试 - 验证运行时条件分发
"""
def setUp(self):
"""创建测试模块"""
self.mock_module = types.ModuleType('runtime_test_module')
self.mock_module.compute = lambda x: x * 2
sys.modules['runtime_test_module'] = self.mock_module
def tearDown(self):
"""清理测试模块"""
if 'runtime_test_module' in sys.modules:
del sys.modules['runtime_test_module']
def test_runtime_check_condition_met_ground_truth(self):
"""
测试runtime_check条件满足时使用replacement
Ground Truth:
- 条件: x > 10
- 原始: compute(x) = x * 2
- 替换: compute(x) = x * 100
- x=20 (>10): 使用替换, 结果=2000
- x=5 (<=10): 使用原始, 结果=10
"""
def optimized_compute(x):
return x * 100
patch = AtomicPatch(
"runtime_test_module.compute",
optimized_compute,
runtime_check=lambda x: x > 10
)
patch.apply()
self.assertEqual(self.mock_module.compute(20), 2000)
self.assertEqual(self.mock_module.compute(15), 1500)
self.assertEqual(self.mock_module.compute(5), 10)
self.assertEqual(self.mock_module.compute(10), 20)
def test_runtime_check_type_based_ground_truth(self):
"""
测试基于类型的runtime_check
Ground Truth:
- 条件: isinstance(x, int)
- int类型使用优化版本
- 其他类型使用原始版本
"""
self.mock_module.process = lambda x: str(x)
def optimized_process(x):
return f"INT:{x}"
patch = AtomicPatch(
"runtime_test_module.process",
optimized_process,
runtime_check=lambda x: isinstance(x, int)
)
patch.apply()
self.assertEqual(self.mock_module.process(42), "INT:42")
self.assertEqual(self.mock_module.process("hello"), "hello")
self.assertEqual(self.mock_module.process(3.14), "3.14")
def test_runtime_check_exception_fallback_ground_truth(self):
"""
测试runtime_check异常时回退到原始函数
Ground Truth:
- runtime_check抛出异常时,使用原始函数
- 不会导致程序崩溃
"""
def bad_check(x):
if x == 0:
raise ValueError("Cannot check zero")
return x > 5
def replacement(x):
return x * 100
patch = AtomicPatch(
"runtime_test_module.compute",
replacement,
runtime_check=bad_check
)
patch.apply()
self.assertEqual(self.mock_module.compute(10), 1000)
self.assertEqual(self.mock_module.compute(0), 0)
class TestClassMethodPatchingGroundTruth(unittest.TestCase):
"""
类和方法补丁测试 - 验证对类方法的补丁效果
"""
def setUp(self):
"""创建测试模块"""
self.mock_module = types.ModuleType('class_test_module')
self.mock_module.MyClass = MockOriginalClass
sys.modules['class_test_module'] = self.mock_module
def tearDown(self):
"""清理测试模块"""
if 'class_test_module' in sys.modules:
del sys.modules['class_test_module']
def test_instance_method_patch_ground_truth(self):
"""
测试实例方法补丁的ground truth
Ground Truth:
- 原始: obj.compute(5) = value + 5
- 替换: obj.compute(5) = value * 5
- obj.value=10时: 原始=15, 替换=50
"""
def new_compute(self, x):
return self.value * x
obj = self.mock_module.MyClass(10)
self.assertEqual(obj.compute(5), 15)
patch = AtomicPatch(
"class_test_module.MyClass.compute",
new_compute
)
patch.apply()
obj2 = self.mock_module.MyClass(10)
self.assertEqual(obj2.compute(5), 50)
def test_static_method_patch_ground_truth(self):
"""
测试静态方法补丁的ground truth
Ground Truth:
- 原始: static_compute(5) = 5 * 2 = 10
- 替换: static_compute(5) = 5 ** 2 = 25
"""
self.assertEqual(self.mock_module.MyClass.static_compute(5), 10)
patch = AtomicPatch(
"class_test_module.MyClass.static_compute",
staticmethod(lambda x: x ** 2)
)
patch.apply()
self.assertEqual(self.mock_module.MyClass.static_compute(5), 25)
def test_class_method_patch_ground_truth(self):
"""
测试类方法补丁的ground truth
Ground Truth:
- 原始: class_compute(5) = 5 * 3 = 15
- 替换: class_compute(5) = 5 ** 3 = 125
"""
self.assertEqual(self.mock_module.MyClass.class_compute(5), 15)
@classmethod
def new_class_compute(cls, x):
return x ** 3
patch = AtomicPatch(
"class_test_module.MyClass.class_compute",
new_class_compute
)
patch.apply()
self.assertEqual(self.mock_module.MyClass.class_compute(5), 125)
class TestPrecheckGroundTruth(unittest.TestCase):
"""
Precheck测试 - 验证预检查功能
"""
def setUp(self):
"""创建测试模块"""
self.mock_module = types.ModuleType('precheck_test_module')
self.mock_module.func = lambda x: x
sys.modules['precheck_test_module'] = self.mock_module
def tearDown(self):
"""清理测试模块"""
if 'precheck_test_module' in sys.modules:
del sys.modules['precheck_test_module']
def test_precheck_pass_ground_truth(self):
"""
测试precheck通过时补丁被应用
Ground Truth:
- precheck返回True时,补丁应用
- func(5) = 5 -> 50
"""
patch = AtomicPatch(
"precheck_test_module.func",
lambda x: x * 10,
precheck=lambda: True
)
result = patch.apply()
self.assertEqual(result.status, PatchStatus.APPLIED)
self.assertEqual(self.mock_module.func(5), 50)
def test_precheck_fail_ground_truth(self):
"""
测试precheck失败时补丁被跳过
Ground Truth:
- precheck返回False时,补丁跳过
- func(5) = 5 (保持原样)
"""
patch = AtomicPatch(
"precheck_test_module.func",
lambda x: x * 10,
precheck=lambda: False
)
result = patch.apply()
self.assertEqual(result.status, PatchStatus.SKIPPED)
self.assertEqual(self.mock_module.func(5), 5)
def test_precheck_with_target_param_ground_truth(self):
"""
测试precheck接收target参数
Ground Truth:
- precheck可以根据target路径决定是否应用
"""
received_target = []
def precheck_with_target(target):
received_target.append(target)
return target.endswith('.func')
patch = AtomicPatch(
"precheck_test_module.func",
lambda x: x * 10,
precheck=precheck_with_target
)
result = patch.apply()
self.assertEqual(result.status, PatchStatus.APPLIED)
self.assertEqual(received_target[0], "precheck_test_module.func")
self.assertEqual(self.mock_module.func(5), 50)
class TestAliasesGroundTruth(unittest.TestCase):
"""
Aliases测试 - 验证别名补丁功能
"""
def setUp(self):
"""创建测试模块"""
self.mock_module = types.ModuleType('alias_test_module')
self.mock_module.main = types.ModuleType('alias_test_module.main')
self.mock_module.alias1 = types.ModuleType('alias_test_module.alias1')
self.mock_module.alias2 = types.ModuleType('alias_test_module.alias2')
original_func = lambda x: x * 2
self.mock_module.main.func = original_func
self.mock_module.alias1.func = original_func
self.mock_module.alias2.func = original_func
sys.modules['alias_test_module'] = self.mock_module
sys.modules['alias_test_module.main'] = self.mock_module.main
sys.modules['alias_test_module.alias1'] = self.mock_module.alias1
sys.modules['alias_test_module.alias2'] = self.mock_module.alias2
def tearDown(self):
"""清理测试模块"""
for name in list(sys.modules.keys()):
if name.startswith('alias_test_module'):
del sys.modules[name]
def test_aliases_all_patched_ground_truth(self):
"""
测试所有别名都被补丁
Ground Truth:
- 主路径和所有别名都应用相同的补丁
- func(5) = 10 -> 50 (所有路径)
"""
self.assertEqual(self.mock_module.main.func(5), 10)
self.assertEqual(self.mock_module.alias1.func(5), 10)
self.assertEqual(self.mock_module.alias2.func(5), 10)
patch = AtomicPatch(
"alias_test_module.main.func",
lambda x: x * 10,
aliases=[
"alias_test_module.alias1.func",
"alias_test_module.alias2.func"
]
)
patch.apply()
self.assertEqual(self.mock_module.main.func(5), 50)
self.assertEqual(self.mock_module.alias1.func(5), 50)
self.assertEqual(self.mock_module.alias2.func(5), 50)
class TestPatcherIntegrationGroundTruth(unittest.TestCase):
"""
Patcher集成测试 - 验证Patcher的完整工作流程
"""
def setUp(self):
"""创建测试模块"""
self.mock_module = types.ModuleType('patcher_int_test')
self.mock_module.func1 = lambda x: x + 1
self.mock_module.func2 = lambda x: x + 2
self.mock_module.func3 = lambda x: x + 3
sys.modules['patcher_int_test'] = self.mock_module
def tearDown(self):
"""清理测试模块"""
if 'patcher_int_test' in sys.modules:
del sys.modules['patcher_int_test']
def test_patcher_apply_order_ground_truth(self):
"""
测试Patcher按顺序应用补丁
Ground Truth:
- 补丁按添加顺序应用
- 每个补丁独立生效
"""
patcher = Patcher()
patcher.add(AtomicPatch("patcher_int_test.func1", lambda x: x * 10))
patcher.add(AtomicPatch("patcher_int_test.func2", lambda x: x * 20))
patcher.add(AtomicPatch("patcher_int_test.func3", lambda x: x * 30))
patcher.apply()
self.assertEqual(self.mock_module.func1(5), 50)
self.assertEqual(self.mock_module.func2(5), 100)
self.assertEqual(self.mock_module.func3(5), 150)
def test_patcher_disable_ground_truth(self):
"""
测试Patcher禁用特定补丁
Ground Truth:
- 被禁用的补丁不会应用
- 其他补丁正常应用
"""
patcher = Patcher()
patcher.add(AtomicPatch("patcher_int_test.func1", lambda x: x * 10))
patcher.add(AtomicPatch("patcher_int_test.func2", lambda x: x * 20))
patcher.disable("patcher_int_test.func2")
patcher.apply()
self.assertEqual(self.mock_module.func1(5), 50)
self.assertEqual(self.mock_module.func2(5), 7)
def test_patcher_context_manager_ground_truth(self):
"""
测试Patcher作为上下文管理器
Ground Truth:
- with块内补丁生效
"""
patcher = Patcher()
patcher.add(AtomicPatch("patcher_int_test.func1", lambda x: x * 100))
with patcher:
self.assertEqual(self.mock_module.func1(5), 500)
class TestPatchClassGroundTruth(unittest.TestCase):
"""
Patch类测试 - 验证自定义Patch子类的功能
"""
def setUp(self):
"""创建测试模块"""
self.mock_module = types.ModuleType('patch_class_test')
self.mock_module.add = lambda a, b: a + b
self.mock_module.sub = lambda a, b: a - b
sys.modules['patch_class_test'] = self.mock_module
def tearDown(self):
"""清理测试模块"""
if 'patch_class_test' in sys.modules:
del sys.modules['patch_class_test']
def test_custom_patch_class_ground_truth(self):
"""
测试自定义Patch类
Ground Truth:
- Patch子类的patches()方法返回的补丁都被应用
"""
class MathPatch(Patch):
name = "math_patch"
@classmethod
def patches(cls, options=None):
return [
AtomicPatch("patch_class_test.add", lambda a, b: a + b + 100),
AtomicPatch("patch_class_test.sub", lambda a, b: a - b - 100),
]
patcher = Patcher()
patcher.add(MathPatch)
patcher.apply()
self.assertEqual(self.mock_module.add(3, 5), 108)
self.assertEqual(self.mock_module.sub(10, 3), -93)
def test_patch_class_with_options_ground_truth(self):
"""
测试带options的Patch类
Ground Truth:
- options参数传递到patches()方法
- 可以根据options动态生成补丁
"""
class ConfigurablePatch(Patch):
name = "configurable_patch"
@classmethod
def patches(cls, options=None):
multiplier = (options or {}).get('multiplier', 1)
return [
AtomicPatch(
"patch_class_test.add",
lambda a, b, m=multiplier: (a + b) * m
),
]
patcher = Patcher()
patcher.add(ConfigurablePatch, options={'multiplier': 10})
patcher.apply()
self.assertEqual(self.mock_module.add(3, 5), 80)
class TestErrorHandlingGroundTruth(unittest.TestCase):
"""
错误处理测试 - 验证各种错误情况的处理
"""
def setUp(self):
"""创建测试模块"""
self.mock_module = types.ModuleType('error_test_module')
self.mock_module.func = lambda x: x
sys.modules['error_test_module'] = self.mock_module
def tearDown(self):
"""清理测试模块"""
if 'error_test_module' in sys.modules:
del sys.modules['error_test_module']
def test_module_not_found_ground_truth(self):
"""
测试模块不存在时的处理
Ground Truth:
- 返回SKIPPED状态
- reason包含"module not found"
"""
patch = AtomicPatch("nonexistent_module.func", lambda x: x)
result = patch.apply()
self.assertEqual(result.status, PatchStatus.SKIPPED)
self.assertIn("module not found", result.reason)
def test_invalid_target_path_ground_truth(self):
"""
测试无效目标路径的处理
Ground Truth:
- 单段路径返回FAILED(用户代码错误)
- reason包含"invalid target path"
"""
patch = AtomicPatch("singlepart", lambda x: x)
result = patch.apply()
self.assertEqual(result.status, PatchStatus.FAILED)
self.assertIn("invalid target path", result.reason)
def test_target_wrapper_error_ground_truth(self):
"""
测试target_wrapper错误的处理
Ground Truth:
- wrapper抛出异常时返回FAILED
- reason包含错误信息
"""
def bad_wrapper(original):
raise RuntimeError("Wrapper failed!")
patch = AtomicPatch(
"error_test_module.func",
target_wrapper=bad_wrapper
)
result = patch.apply()
self.assertEqual(result.status, PatchStatus.FAILED)
self.assertIn("Wrapper failed", result.reason)
def test_replacement_wrapper_error_ground_truth(self):
"""
测试replacement_wrapper错误的处理
Ground Truth:
- wrapper抛出异常时返回FAILED
"""
def bad_wrapper(replacement):
raise RuntimeError("Replacement wrapper failed!")
patch = AtomicPatch(
"error_test_module.func",
lambda x: x * 2,
replacement_wrapper=bad_wrapper
)
result = patch.apply()
self.assertEqual(result.status, PatchStatus.FAILED)
self.assertIn("replacement_wrapper error", result.reason)
class TestGetInfoGroundTruth(unittest.TestCase):
"""
get_info测试 - 验证补丁信息获取功能
"""
def setUp(self):
"""创建测试模块"""
self.mock_module = types.ModuleType('info_test_module')
self.mock_module.original_func = mock_original_add
sys.modules['info_test_module'] = self.mock_module
def tearDown(self):
"""清理测试模块"""
if 'info_test_module' in sys.modules:
del sys.modules['info_test_module']
def test_get_info_contains_target_ground_truth(self):
"""
测试get_info包含目标路径
Ground Truth:
- info字符串包含完整的目标路径
"""
patch = AtomicPatch(
"info_test_module.original_func",
mock_replacement_add
)
patch.apply()
info = patch.get_info()
self.assertIn("info_test_module.original_func", info)
def test_get_info_contains_function_names_ground_truth(self):
"""
测试get_info包含函数名称
Ground Truth:
- info包含原始函数名和替换函数名
"""
patch = AtomicPatch(
"info_test_module.original_func",
mock_replacement_add
)
patch.apply()
info = patch.get_info()
self.assertIn("mock_original_add", info)
self.assertIn("mock_replacement_add", info)
def test_get_info_with_diff_ground_truth(self):
"""
测试get_info(show_diff=True)包含diff
Ground Truth:
- show_diff=True时,info包含源码差异
- diff包含unified格式标记
"""
patch = AtomicPatch(
"info_test_module.original_func",
mock_replacement_add
)
patch.apply()
info = patch.get_info(show_diff=True)
self.assertIn("---", info)
self.assertIn("+++", info)
self.assertIn("@@", info)
class TestCallableNameGroundTruth(unittest.TestCase):
"""
_get_callable_name测试 - 验证可调用对象名称获取
"""
def test_function_name_ground_truth(self):
"""
测试函数名称获取
Ground Truth:
- 返回函数的__qualname__或__name__
"""
name = _get_callable_name(mock_original_add)
self.assertEqual(name, "mock_original_add")
def test_lambda_name_ground_truth(self):
"""
测试lambda名称获取
Ground Truth:
- lambda函数名称包含"<lambda>"
"""
func = lambda x: x
name = _get_callable_name(func)
self.assertIn("lambda", name)
def test_class_name_ground_truth(self):
"""
测试类名称获取
Ground Truth:
- 返回类的__qualname__
"""
name = _get_callable_name(MockOriginalClass)
self.assertEqual(name, "MockOriginalClass")
def test_method_name_ground_truth(self):
"""
测试方法名称获取
Ground Truth:
- 返回方法的完整限定名
"""
name = _get_callable_name(MockOriginalClass.compute)
self.assertIn("compute", name)
def test_none_name_ground_truth(self):
"""
测试None的名称获取
Ground Truth:
- None返回"<None>"
"""
name = _get_callable_name(None)
self.assertEqual(name, "<None>")
if __name__ == "__main__":
unittest.main()