mcp-feedback-enhanced/tests/unit/test_memory_monitor.py

394 lines
13 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
內存監控系統測試
================
測試集成式內存監控系統的功能包括
- 內存監控準確性
- 警告機制
- 清理觸發
- 統計和分析功能
"""
from datetime import datetime, timedelta
2025-06-11 03:25:08 +08:00
from unittest.mock import Mock, patch
import pytest
from src.mcp_feedback_enhanced.utils.memory_monitor import (
2025-06-11 03:25:08 +08:00
MemoryAlert,
MemoryMonitor,
MemorySnapshot,
get_memory_monitor,
)
class TestMemorySnapshot:
"""測試內存快照數據類"""
2025-06-11 03:25:08 +08:00
def test_memory_snapshot_creation(self):
"""測試內存快照創建"""
snapshot = MemorySnapshot(
timestamp=datetime.now(),
system_total=8 * 1024**3, # 8GB
system_available=4 * 1024**3, # 4GB
system_used=4 * 1024**3, # 4GB
system_percent=50.0,
process_rss=100 * 1024**2, # 100MB
process_vms=200 * 1024**2, # 200MB
process_percent=1.25,
2025-06-11 03:25:08 +08:00
gc_objects=10000,
)
2025-06-11 03:25:08 +08:00
assert snapshot.system_total == 8 * 1024**3
assert snapshot.system_percent == 50.0
assert snapshot.process_rss == 100 * 1024**2
assert snapshot.gc_objects == 10000
class TestMemoryAlert:
"""測試內存警告數據類"""
2025-06-11 03:25:08 +08:00
def test_memory_alert_creation(self):
"""測試內存警告創建"""
alert = MemoryAlert(
level="warning",
message="內存使用率較高: 85.0%",
timestamp=datetime.now(),
memory_percent=85.0,
2025-06-11 03:25:08 +08:00
recommended_action="考慮執行輕量級清理",
)
2025-06-11 03:25:08 +08:00
assert alert.level == "warning"
assert alert.memory_percent == 85.0
assert "85.0%" in alert.message
class TestMemoryMonitor:
"""測試內存監控器"""
2025-06-11 03:25:08 +08:00
def test_monitor_initialization(self):
"""測試監控器初始化"""
monitor = MemoryMonitor(
warning_threshold=0.7,
critical_threshold=0.85,
emergency_threshold=0.95,
2025-06-11 03:25:08 +08:00
monitoring_interval=10,
)
2025-06-11 03:25:08 +08:00
assert monitor.warning_threshold == 0.7
assert monitor.critical_threshold == 0.85
assert monitor.emergency_threshold == 0.95
assert monitor.monitoring_interval == 10
assert not monitor.is_monitoring
assert len(monitor.snapshots) == 0
assert len(monitor.alerts) == 0
2025-06-11 03:25:08 +08:00
@patch("src.mcp_feedback_enhanced.utils.memory_monitor.psutil")
def test_collect_memory_snapshot(self, mock_psutil):
"""測試內存快照收集"""
# 模擬 psutil 返回值
mock_virtual_memory = Mock()
mock_virtual_memory.total = 8 * 1024**3
mock_virtual_memory.available = 4 * 1024**3
mock_virtual_memory.used = 4 * 1024**3
mock_virtual_memory.percent = 50.0
2025-06-11 03:25:08 +08:00
mock_memory_info = Mock()
mock_memory_info.rss = 100 * 1024**2
mock_memory_info.vms = 200 * 1024**2
2025-06-11 03:25:08 +08:00
mock_process = Mock()
mock_process.memory_info.return_value = mock_memory_info
mock_process.memory_percent.return_value = 1.25
2025-06-11 03:25:08 +08:00
mock_psutil.virtual_memory.return_value = mock_virtual_memory
mock_psutil.Process.return_value = mock_process
2025-06-11 03:25:08 +08:00
monitor = MemoryMonitor()
snapshot = monitor._collect_memory_snapshot()
2025-06-11 03:25:08 +08:00
assert snapshot.system_total == 8 * 1024**3
assert snapshot.system_percent == 50.0
assert snapshot.process_rss == 100 * 1024**2
assert snapshot.process_percent == 1.25
2025-06-11 03:25:08 +08:00
def test_memory_status_classification(self):
"""測試內存狀態分類"""
monitor = MemoryMonitor(
2025-06-11 03:25:08 +08:00
warning_threshold=0.8, critical_threshold=0.9, emergency_threshold=0.95
)
2025-06-11 03:25:08 +08:00
assert monitor._get_memory_status(0.5) == "normal"
assert monitor._get_memory_status(0.85) == "warning"
assert monitor._get_memory_status(0.92) == "critical"
assert monitor._get_memory_status(0.97) == "emergency"
2025-06-11 03:25:08 +08:00
def test_callback_management(self):
"""測試回調函數管理"""
monitor = MemoryMonitor()
2025-06-11 03:25:08 +08:00
cleanup_callback = Mock()
alert_callback = Mock()
2025-06-11 03:25:08 +08:00
# 添加回調
monitor.add_cleanup_callback(cleanup_callback)
monitor.add_alert_callback(alert_callback)
2025-06-11 03:25:08 +08:00
assert cleanup_callback in monitor.cleanup_callbacks
assert alert_callback in monitor.alert_callbacks
2025-06-11 03:25:08 +08:00
# 移除回調
monitor.remove_cleanup_callback(cleanup_callback)
monitor.remove_alert_callback(alert_callback)
2025-06-11 03:25:08 +08:00
assert cleanup_callback not in monitor.cleanup_callbacks
assert alert_callback not in monitor.alert_callbacks
2025-06-11 03:25:08 +08:00
@patch("src.mcp_feedback_enhanced.utils.memory_monitor.gc")
def test_cleanup_triggering(self, mock_gc):
"""測試清理觸發"""
monitor = MemoryMonitor()
cleanup_callback = Mock()
monitor.add_cleanup_callback(cleanup_callback)
2025-06-11 03:25:08 +08:00
mock_gc.collect.return_value = 42
2025-06-11 03:25:08 +08:00
# 測試普通清理
monitor._trigger_cleanup()
2025-06-11 03:25:08 +08:00
assert monitor.cleanup_triggers_count == 1
cleanup_callback.assert_called_once()
mock_gc.collect.assert_called()
2025-06-11 03:25:08 +08:00
# 測試緊急清理
cleanup_callback.reset_mock()
mock_gc.collect.reset_mock()
2025-06-11 03:25:08 +08:00
monitor._trigger_emergency_cleanup()
2025-06-11 03:25:08 +08:00
# 緊急清理會調用多次垃圾回收
assert mock_gc.collect.call_count == 3
2025-06-11 03:25:08 +08:00
@patch("src.mcp_feedback_enhanced.utils.memory_monitor.psutil")
def test_memory_usage_checking(self, mock_psutil):
"""測試內存使用檢查和警告觸發"""
monitor = MemoryMonitor(
2025-06-11 03:25:08 +08:00
warning_threshold=0.8, critical_threshold=0.9, emergency_threshold=0.95
)
2025-06-11 03:25:08 +08:00
alert_callback = Mock()
cleanup_callback = Mock()
monitor.add_alert_callback(alert_callback)
monitor.add_cleanup_callback(cleanup_callback)
2025-06-11 03:25:08 +08:00
# 模擬不同的內存使用情況
test_cases = [
(75.0, "normal", 0, 0), # 正常情況
(85.0, "warning", 1, 0), # 警告情況
(92.0, "critical", 1, 1), # 危險情況
(97.0, "emergency", 1, 1), # 緊急情況
]
2025-06-11 03:25:08 +08:00
for (
memory_percent,
expected_status,
expected_alerts,
expected_cleanups,
) in test_cases:
# 重置計數器
alert_callback.reset_mock()
cleanup_callback.reset_mock()
monitor.alerts.clear()
monitor.cleanup_triggers_count = 0
2025-06-11 03:25:08 +08:00
# 創建模擬快照
snapshot = MemorySnapshot(
timestamp=datetime.now(),
system_total=8 * 1024**3,
system_available=int(8 * 1024**3 * (100 - memory_percent) / 100),
system_used=int(8 * 1024**3 * memory_percent / 100),
system_percent=memory_percent,
process_rss=100 * 1024**2,
process_vms=200 * 1024**2,
process_percent=1.25,
2025-06-11 03:25:08 +08:00
gc_objects=10000,
)
2025-06-11 03:25:08 +08:00
# 檢查內存使用
monitor._check_memory_usage(snapshot)
2025-06-11 03:25:08 +08:00
# 驗證結果
assert monitor._get_memory_status(memory_percent / 100.0) == expected_status
2025-06-11 03:25:08 +08:00
if expected_alerts > 0:
assert len(monitor.alerts) == expected_alerts
assert alert_callback.call_count == expected_alerts
2025-06-11 03:25:08 +08:00
if expected_cleanups > 0:
assert cleanup_callback.call_count == expected_cleanups
2025-06-11 03:25:08 +08:00
def test_memory_trend_analysis(self):
"""測試內存趨勢分析"""
monitor = MemoryMonitor()
2025-06-11 03:25:08 +08:00
# 測試數據不足的情況
assert monitor._analyze_memory_trend() == "insufficient_data"
2025-06-11 03:25:08 +08:00
# 添加穩定趨勢的快照
base_time = datetime.now()
for i in range(10):
snapshot = MemorySnapshot(
timestamp=base_time + timedelta(seconds=i * 30),
system_total=8 * 1024**3,
system_available=4 * 1024**3,
system_used=4 * 1024**3,
system_percent=50.0 + (i % 2), # 輕微波動
process_rss=100 * 1024**2,
process_vms=200 * 1024**2,
process_percent=1.25,
2025-06-11 03:25:08 +08:00
gc_objects=10000,
)
monitor.snapshots.append(snapshot)
2025-06-11 03:25:08 +08:00
assert monitor._analyze_memory_trend() == "stable"
2025-06-11 03:25:08 +08:00
# 清空並添加遞增趨勢的快照
monitor.snapshots.clear()
for i in range(10):
snapshot = MemorySnapshot(
timestamp=base_time + timedelta(seconds=i * 30),
system_total=8 * 1024**3,
system_available=4 * 1024**3,
system_used=4 * 1024**3,
system_percent=50.0 + i * 2, # 遞增趨勢
process_rss=100 * 1024**2,
process_vms=200 * 1024**2,
process_percent=1.25,
2025-06-11 03:25:08 +08:00
gc_objects=10000,
)
monitor.snapshots.append(snapshot)
2025-06-11 03:25:08 +08:00
assert monitor._analyze_memory_trend() == "increasing"
2025-06-11 03:25:08 +08:00
@patch("src.mcp_feedback_enhanced.utils.memory_monitor.psutil")
def test_get_current_memory_info(self, mock_psutil):
"""測試獲取當前內存信息"""
# 模擬 psutil 返回值
mock_virtual_memory = Mock()
mock_virtual_memory.total = 8 * 1024**3
mock_virtual_memory.available = 4 * 1024**3
mock_virtual_memory.used = 4 * 1024**3
mock_virtual_memory.percent = 50.0
2025-06-11 03:25:08 +08:00
mock_memory_info = Mock()
mock_memory_info.rss = 100 * 1024**2
mock_memory_info.vms = 200 * 1024**2
2025-06-11 03:25:08 +08:00
mock_process = Mock()
mock_process.memory_info.return_value = mock_memory_info
mock_process.memory_percent.return_value = 1.25
2025-06-11 03:25:08 +08:00
mock_psutil.virtual_memory.return_value = mock_virtual_memory
mock_psutil.Process.return_value = mock_process
2025-06-11 03:25:08 +08:00
monitor = MemoryMonitor()
info = monitor.get_current_memory_info()
2025-06-11 03:25:08 +08:00
assert "system" in info
assert "process" in info
assert info["system"]["total_gb"] == 8.0
assert info["system"]["usage_percent"] == 50.0
assert info["process"]["rss_mb"] == 100.0
assert info["status"] == "normal"
2025-06-11 03:25:08 +08:00
def test_memory_stats_calculation(self):
"""測試內存統計計算"""
monitor = MemoryMonitor()
monitor.start_time = datetime.now() - timedelta(minutes=5)
2025-06-11 03:25:08 +08:00
# 添加一些測試快照
base_time = datetime.now()
for i in range(5):
snapshot = MemorySnapshot(
timestamp=base_time + timedelta(seconds=i * 30),
system_total=8 * 1024**3,
system_available=4 * 1024**3,
system_used=4 * 1024**3,
system_percent=50.0 + i * 5, # 50%, 55%, 60%, 65%, 70%
process_rss=100 * 1024**2,
process_vms=200 * 1024**2,
process_percent=1.0 + i * 0.2, # 1.0%, 1.2%, 1.4%, 1.6%, 1.8%
2025-06-11 03:25:08 +08:00
gc_objects=10000,
)
monitor.snapshots.append(snapshot)
2025-06-11 03:25:08 +08:00
# 添加一些警告
2025-06-11 03:25:08 +08:00
monitor.alerts.append(
MemoryAlert(
level="warning",
message="Test warning",
timestamp=datetime.now(),
memory_percent=85.0,
recommended_action="Test action",
)
)
monitor.cleanup_triggers_count = 2
2025-06-11 03:25:08 +08:00
stats = monitor.get_memory_stats()
2025-06-11 03:25:08 +08:00
assert stats.snapshots_count == 5
assert stats.average_system_usage == 60.0 # (50+55+60+65+70)/5
assert stats.peak_system_usage == 70.0
assert stats.average_process_usage == 1.4 # (1.0+1.2+1.4+1.6+1.8)/5
assert stats.peak_process_usage == 1.8
assert stats.alerts_count == 1
assert stats.cleanup_triggers == 2
assert stats.monitoring_duration > 0
2025-06-11 03:25:08 +08:00
def test_export_memory_data(self):
"""測試內存數據導出"""
monitor = MemoryMonitor()
2025-06-11 03:25:08 +08:00
# 添加一些測試數據
2025-06-11 03:25:08 +08:00
monitor.alerts.append(
MemoryAlert(
level="warning",
message="Test warning",
timestamp=datetime.now(),
memory_percent=85.0,
recommended_action="Test action",
)
)
with patch.object(monitor, "get_current_memory_info") as mock_info:
mock_info.return_value = {
"system": {"usage_percent": 75.0},
2025-06-11 03:25:08 +08:00
"status": "warning",
}
2025-06-11 03:25:08 +08:00
exported_data = monitor.export_memory_data()
2025-06-11 03:25:08 +08:00
assert "config" in exported_data
assert "current_info" in exported_data
assert "stats" in exported_data
assert "recent_alerts" in exported_data
assert "is_monitoring" in exported_data
2025-06-11 03:25:08 +08:00
assert exported_data["config"]["warning_threshold"] == 0.8
assert len(exported_data["recent_alerts"]) == 1
def test_global_memory_monitor_singleton():
"""測試全域內存監控器單例模式"""
monitor1 = get_memory_monitor()
monitor2 = get_memory_monitor()
2025-06-11 03:25:08 +08:00
assert monitor1 is monitor2
assert isinstance(monitor1, MemoryMonitor)
if __name__ == "__main__":
pytest.main([__file__, "-v"])