194 lines
5.9 KiB
Python
194 lines
5.9 KiB
Python
|
|
"""性能分析器测试"""
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
import time
|
||
|
|
from src.factors.engine.performance_profiler import PerformanceProfiler, ProfileRecord
|
||
|
|
|
||
|
|
|
||
|
|
class TestProfileRecord:
|
||
|
|
"""测试 ProfileRecord 数据类"""
|
||
|
|
|
||
|
|
def test_profile_record_creation(self):
|
||
|
|
"""测试创建 ProfileRecord"""
|
||
|
|
record = ProfileRecord(time_ms=1.5)
|
||
|
|
assert record.time_ms == 1.5
|
||
|
|
|
||
|
|
|
||
|
|
class TestPerformanceProfiler:
|
||
|
|
"""测试 PerformanceProfiler 类"""
|
||
|
|
|
||
|
|
def test_profiler_disabled_by_default(self):
|
||
|
|
"""测试默认情况下禁用"""
|
||
|
|
profiler = PerformanceProfiler()
|
||
|
|
assert profiler.enabled is False
|
||
|
|
|
||
|
|
def test_profiler_enabled(self):
|
||
|
|
"""测试启用性能分析器"""
|
||
|
|
profiler = PerformanceProfiler(enabled=True)
|
||
|
|
assert profiler.enabled is True
|
||
|
|
|
||
|
|
def test_measure_context_manager_disabled(self):
|
||
|
|
"""测试禁用时 measure 上下文管理器"""
|
||
|
|
profiler = PerformanceProfiler(enabled=False)
|
||
|
|
|
||
|
|
with profiler.measure("test_task"):
|
||
|
|
time.sleep(0.001)
|
||
|
|
|
||
|
|
# 禁用时不会记录数据
|
||
|
|
assert len(profiler.records) == 0
|
||
|
|
|
||
|
|
def test_measure_context_manager_enabled(self):
|
||
|
|
"""测试启用时 measure 上下文管理器"""
|
||
|
|
profiler = PerformanceProfiler(enabled=True)
|
||
|
|
|
||
|
|
with profiler.measure("test_task"):
|
||
|
|
time.sleep(0.01) # 10ms
|
||
|
|
|
||
|
|
assert "test_task" in profiler.records
|
||
|
|
assert profiler.records["test_task"].time_ms >= 0.01
|
||
|
|
|
||
|
|
def test_measure_multiple_calls_accumulate(self):
|
||
|
|
"""测试多次调用同名任务会累加时间"""
|
||
|
|
profiler = PerformanceProfiler(enabled=True)
|
||
|
|
|
||
|
|
# 第一次调用
|
||
|
|
with profiler.measure("task"):
|
||
|
|
time.sleep(0.01)
|
||
|
|
|
||
|
|
first_time = profiler.records["task"].time_ms
|
||
|
|
|
||
|
|
# 第二次调用
|
||
|
|
with profiler.measure("task"):
|
||
|
|
time.sleep(0.01)
|
||
|
|
|
||
|
|
# 时间应该累加
|
||
|
|
assert profiler.records["task"].time_ms > first_time
|
||
|
|
|
||
|
|
def test_measure_exception_handling(self):
|
||
|
|
"""测试异常发生时计时器正常闭合"""
|
||
|
|
profiler = PerformanceProfiler(enabled=True)
|
||
|
|
|
||
|
|
try:
|
||
|
|
with profiler.measure("failing_task"):
|
||
|
|
time.sleep(0.01)
|
||
|
|
raise ValueError("Test error")
|
||
|
|
except ValueError:
|
||
|
|
pass
|
||
|
|
|
||
|
|
# 异常发生时仍然记录了时间
|
||
|
|
assert "failing_task" in profiler.records
|
||
|
|
assert profiler.records["failing_task"].time_ms >= 0.01
|
||
|
|
|
||
|
|
def test_get_report_empty(self):
|
||
|
|
"""测试空记录时的报告"""
|
||
|
|
profiler = PerformanceProfiler(enabled=True)
|
||
|
|
report = profiler.get_report()
|
||
|
|
assert report == {}
|
||
|
|
|
||
|
|
def test_get_report_with_data(self):
|
||
|
|
"""测试有数据时的报告"""
|
||
|
|
profiler = PerformanceProfiler(enabled=True)
|
||
|
|
|
||
|
|
with profiler.measure("task1"):
|
||
|
|
time.sleep(0.02)
|
||
|
|
with profiler.measure("task2"):
|
||
|
|
time.sleep(0.01)
|
||
|
|
|
||
|
|
report = profiler.get_report()
|
||
|
|
|
||
|
|
assert report["enabled"] is True
|
||
|
|
assert report["factor_count"] == 2
|
||
|
|
assert report["total_time_ms"] > 0
|
||
|
|
assert report["avg_time_ms"] > 0
|
||
|
|
assert len(report["slowest_factors"]) <= 2
|
||
|
|
assert "all_records" in report
|
||
|
|
|
||
|
|
def test_get_report_sorts_by_time(self):
|
||
|
|
"""测试报告按时间排序"""
|
||
|
|
profiler = PerformanceProfiler(enabled=True)
|
||
|
|
|
||
|
|
# task1 耗时更长
|
||
|
|
with profiler.measure("task1"):
|
||
|
|
time.sleep(0.02)
|
||
|
|
|
||
|
|
with profiler.measure("task2"):
|
||
|
|
time.sleep(0.01)
|
||
|
|
|
||
|
|
report = profiler.get_report()
|
||
|
|
slowest = report["slowest_factors"]
|
||
|
|
|
||
|
|
assert slowest[0][0] == "task1" # 最慢的应该是 task1
|
||
|
|
|
||
|
|
def test_reset_clears_data(self):
|
||
|
|
"""测试重置清除数据"""
|
||
|
|
profiler = PerformanceProfiler(enabled=True)
|
||
|
|
|
||
|
|
with profiler.measure("task"):
|
||
|
|
time.sleep(0.01)
|
||
|
|
|
||
|
|
assert len(profiler.records) == 1
|
||
|
|
|
||
|
|
profiler.reset()
|
||
|
|
|
||
|
|
assert len(profiler.records) == 0
|
||
|
|
assert len(profiler._context_stack) == 0
|
||
|
|
|
||
|
|
def test_print_report_disabled(self):
|
||
|
|
"""测试禁用时打印报告"""
|
||
|
|
profiler = PerformanceProfiler(enabled=False)
|
||
|
|
# 应该正常执行不报错
|
||
|
|
profiler.print_report()
|
||
|
|
|
||
|
|
def test_print_report_empty(self, capsys):
|
||
|
|
"""测试空数据时打印报告"""
|
||
|
|
profiler = PerformanceProfiler(enabled=True)
|
||
|
|
profiler.print_report()
|
||
|
|
|
||
|
|
captured = capsys.readouterr()
|
||
|
|
assert "无性能数据" in captured.out
|
||
|
|
|
||
|
|
def test_print_report_with_data(self, capsys):
|
||
|
|
"""测试有数据时打印报告"""
|
||
|
|
profiler = PerformanceProfiler(enabled=True)
|
||
|
|
|
||
|
|
with profiler.measure("slow_task"):
|
||
|
|
time.sleep(0.02)
|
||
|
|
with profiler.measure("fast_task"):
|
||
|
|
time.sleep(0.01)
|
||
|
|
|
||
|
|
profiler.print_report()
|
||
|
|
|
||
|
|
captured = capsys.readouterr()
|
||
|
|
assert "Factor Engine 性能报告" in captured.out
|
||
|
|
assert "slow_task" in captured.out
|
||
|
|
assert "fast_task" in captured.out
|
||
|
|
|
||
|
|
def test_get_factor_time(self):
|
||
|
|
"""测试获取单个因子的计算时间"""
|
||
|
|
profiler = PerformanceProfiler(enabled=True)
|
||
|
|
|
||
|
|
with profiler.measure("task"):
|
||
|
|
time.sleep(0.01)
|
||
|
|
|
||
|
|
time_val = profiler.get_factor_time("task")
|
||
|
|
assert time_val is not None
|
||
|
|
assert time_val >= 0.01
|
||
|
|
|
||
|
|
# 不存在的因子返回 None
|
||
|
|
assert profiler.get_factor_time("nonexistent") is None
|
||
|
|
|
||
|
|
def test_nested_measure(self):
|
||
|
|
"""测试嵌套计时"""
|
||
|
|
profiler = PerformanceProfiler(enabled=True)
|
||
|
|
|
||
|
|
with profiler.measure("outer"):
|
||
|
|
time.sleep(0.01)
|
||
|
|
with profiler.measure("inner"):
|
||
|
|
time.sleep(0.01)
|
||
|
|
|
||
|
|
assert "outer" in profiler.records
|
||
|
|
assert "inner" in profiler.records
|
||
|
|
# 嵌套计时应该分别记录
|
||
|
|
assert profiler.records["outer"].time_ms >= 0.01
|
||
|
|
assert profiler.records["inner"].time_ms >= 0.01
|