import unittest
import time
from unittest.mock import Mock, patch, MagicMock
from olc.collector.local_resource.local_resource_collector import (
LocalResourceMeasurable,
LocalResourceMeter,
LocalResourceCollector,
)
class TestLocalResourceMeasurable(unittest.TestCase):
"""LocalResourceMeasurable 测试类"""
def test_init(self):
"""测试初始化"""
measurable = LocalResourceMeasurable(0.5, 0.6, 0.1, 0.2, 0.3)
self.assertEqual(measurable.cpu, 0.5)
self.assertEqual(measurable.memory, 0.6)
self.assertEqual(measurable.io, 0.1)
self.assertEqual(measurable.network, 0.2)
self.assertEqual(measurable.loadaverage, 0.3)
def test_get_properties(self):
"""测试获取属性列表"""
properties = LocalResourceMeasurable.get_properties()
self.assertEqual(properties, ["cpu", "memory", "io", "network", "loadaverage"])
def test_str_representation(self):
"""测试字符串表示"""
measurable = LocalResourceMeasurable(0.5, 0.6, 0.1, 0.2, 0.3)
str_repr = str(measurable)
self.assertIn("cpu=0.5", str_repr)
self.assertIn("memory=0.6", str_repr)
self.assertIn("io=0.1", str_repr)
self.assertIn("network=0.2", str_repr)
self.assertIn("loadaverage=0.3", str_repr)
def test_repr(self):
"""测试 repr"""
measurable = LocalResourceMeasurable(0.5, 0.6, 0.1, 0.2, 0.3)
self.assertEqual(repr(measurable), str(measurable))
class TestLocalResourceMeter(unittest.TestCase):
"""LocalResourceMeter 测试类"""
def setUp(self):
"""每个测试前重置单例,避免状态污染"""
LocalResourceMeter._instance = None
self.meter = LocalResourceMeter()
def tearDown(self):
"""每个测试后清理单例"""
LocalResourceMeter._instance = None
def test_singleton(self):
"""测试单例模式"""
instance1 = LocalResourceMeter.get_instance()
instance2 = LocalResourceMeter.get_instance()
self.assertIs(instance1, instance2)
@patch("olc.collector.local_resource.local_resource_collector.psutil")
def test_get_cpu_usage(self, mock_psutil):
"""测试获取 CPU 使用率"""
mock_psutil.cpu_percent.return_value = 50.0
result = self.meter._get_cpu_usage()
self.assertEqual(result, 0.5)
mock_psutil.cpu_percent.assert_called_once_with(interval=0)
@patch("olc.collector.local_resource.local_resource_collector.psutil")
def test_get_memory_usage(self, mock_psutil):
"""测试获取内存使用率"""
mock_memory = Mock()
mock_memory.percent = 75.0
mock_psutil.virtual_memory.return_value = mock_memory
result = self.meter._get_memory_usage()
self.assertEqual(result, 0.75)
mock_psutil.virtual_memory.assert_called_once()
@patch("olc.collector.local_resource.local_resource_collector.psutil")
def test_get_io_usage_first_call(self, mock_psutil):
"""测试首次获取 IO 使用率(应返回 0 并记录快照)"""
mock_counters = Mock()
mock_counters.read_bytes = 1024 * 1024
mock_counters.write_bytes = 512 * 1024
mock_psutil.disk_io_counters.return_value = mock_counters
result = self.meter._get_io_usage()
self.assertEqual(result, 0.0)
self.assertIsNotNone(self.meter._prev_disk_io)
self.assertEqual(self.meter._prev_disk_io, (1024 * 1024, 512 * 1024))
self.assertGreater(self.meter._prev_disk_time, 0)
@patch("olc.collector.local_resource.local_resource_collector.psutil")
def test_get_io_usage_subsequent_call(self, mock_psutil):
"""测试第二次获取 IO 使用率(应计算速率)"""
mock_counters = Mock()
mock_counters.read_bytes = 1024 * 1024
mock_counters.write_bytes = 512 * 1024
mock_psutil.disk_io_counters.return_value = mock_counters
self.meter._get_io_usage()
mock_counters.read_bytes = 1024 * 1024 + 500 * 1024 * 1024
mock_counters.write_bytes = 512 * 1024 + 500 * 1024 * 1024
prev_time = self.meter._prev_disk_time
self.meter._prev_disk_time = prev_time - 1.0
result = self.meter._get_io_usage()
self.assertEqual(result, 1.0)
@patch("olc.collector.local_resource.local_resource_collector.psutil")
def test_get_io_usage_none_counters(self, mock_psutil):
"""测试无法获取 IO 计数器时返回 0"""
mock_psutil.disk_io_counters.return_value = None
result = self.meter._get_io_usage()
self.assertEqual(result, 0.0)
@patch("olc.collector.local_resource.local_resource_collector.psutil")
def test_get_network_usage_first_call(self, mock_psutil):
"""测试首次获取网络使用率(应返回 0 并记录快照)"""
mock_counters = Mock()
mock_counters.bytes_sent = 1024 * 1024
mock_counters.bytes_recv = 2048 * 1024
mock_psutil.net_io_counters.return_value = mock_counters
result = self.meter._get_network_usage()
self.assertEqual(result, 0.0)
self.assertIsNotNone(self.meter._prev_net_io)
self.assertEqual(self.meter._prev_net_io, (1024 * 1024, 2048 * 1024))
self.assertGreater(self.meter._prev_net_time, 0)
@patch("olc.collector.local_resource.local_resource_collector.psutil")
def test_get_network_usage_subsequent_call(self, mock_psutil):
"""测试第二次获取网络使用率(应计算速率)"""
mock_counters = Mock()
mock_counters.bytes_sent = 1024 * 1024
mock_counters.bytes_recv = 1024 * 1024
mock_psutil.net_io_counters.return_value = mock_counters
self.meter._get_network_usage()
mock_counters.bytes_sent = 1024 * 1024 + 1000 * 1024 * 1024
mock_counters.bytes_recv = 1024 * 1024 + 1000 * 1024 * 1024
prev_time = self.meter._prev_net_time
self.meter._prev_net_time = prev_time - 1.0
result = self.meter._get_network_usage()
self.assertEqual(result, 1.0)
@patch("olc.collector.local_resource.local_resource_collector.psutil")
def test_get_load_usage_linux(self, mock_psutil):
"""测试 Linux 环境下获取负载使用率"""
mock_psutil.getloadavg.return_value = (2.0, 1.5, 1.0)
mock_psutil.cpu_count.return_value = 4
result = self.meter._get_load_usage()
self.assertEqual(result, 0.5)
@patch("olc.collector.local_resource.local_resource_collector.psutil")
def test_get_load_usage_high_load(self, mock_psutil):
"""测试高负载情况下截断到 1.0"""
mock_psutil.getloadavg.return_value = (10.0, 5.0, 2.0)
mock_psutil.cpu_count.return_value = 4
result = self.meter._get_load_usage()
self.assertEqual(result, 1.0)
@patch("olc.collector.local_resource.local_resource_collector.psutil")
def test_get_load_usage_windows_fallback(self, mock_psutil):
"""测试 Windows 环境下回退到 CPU 使用率"""
mock_psutil.getloadavg.side_effect = AttributeError("getloadavg not available")
mock_psutil.cpu_percent.return_value = 30.0
result = self.meter._get_load_usage()
self.assertEqual(result, 0.3)
mock_psutil.cpu_percent.assert_called_once_with(interval=0)
@patch("olc.collector.local_resource.local_resource_collector.psutil")
def test_get_load_usage_oserror_fallback(self, mock_psutil):
"""测试 OSError 时回退到 CPU 使用率"""
mock_psutil.getloadavg.side_effect = OSError("not supported")
mock_psutil.cpu_percent.return_value = 45.0
result = self.meter._get_load_usage()
self.assertEqual(result, 0.45)
@patch("olc.collector.local_resource.local_resource_collector.psutil")
def test_get_load_usage_no_cpu_count(self, mock_psutil):
"""测试无法获取 CPU 核心数时直接使用负载值"""
mock_psutil.getloadavg.return_value = (0.8, 0.5, 0.3)
mock_psutil.cpu_count.return_value = None
result = self.meter._get_load_usage()
self.assertEqual(result, 0.8)
@patch("olc.collector.local_resource.local_resource_collector.psutil")
def test_measure_success(self, mock_psutil):
"""测试成功测量所有指标"""
mock_psutil.cpu_percent.return_value = 50.0
mock_memory = Mock()
mock_memory.percent = 60.0
mock_psutil.virtual_memory.return_value = mock_memory
mock_psutil.disk_io_counters.return_value = None
mock_psutil.net_io_counters.return_value = Mock(bytes_sent=0, bytes_recv=0)
mock_psutil.getloadavg.return_value = (2.0, 1.5, 1.0)
mock_psutil.cpu_count.return_value = 4
result = self.meter.measure()
self.assertIsNotNone(result)
self.assertIsInstance(result, LocalResourceMeasurable)
self.assertEqual(result.cpu, 0.5)
self.assertEqual(result.memory, 0.6)
self.assertEqual(result.io, 0.0)
self.assertEqual(result.network, 0.0)
self.assertEqual(result.loadaverage, 0.5)
@patch("olc.collector.local_resource.local_resource_collector.psutil")
def test_measure_partial_exception(self, mock_psutil):
"""测试部分指标异常时返回默认值 -1.0"""
mock_psutil.cpu_percent.side_effect = Exception("cpu error")
mock_psutil.virtual_memory.side_effect = Exception("memory error")
mock_psutil.disk_io_counters.return_value = None
mock_psutil.net_io_counters.return_value = Mock(bytes_sent=0, bytes_recv=0)
mock_psutil.getloadavg.return_value = (2.0, 1.5, 1.0)
mock_psutil.cpu_count.return_value = 4
result = self.meter.measure()
self.assertIsNotNone(result)
self.assertEqual(result.cpu, -1.0)
self.assertEqual(result.memory, -1.0)
self.assertEqual(result.io, 0.0)
self.assertEqual(result.network, 0.0)
self.assertEqual(result.loadaverage, 0.5)
class TestLocalResourceCollector(unittest.TestCase):
"""LocalResourceCollector 测试类"""
def setUp(self):
self.mock_meter = MagicMock(spec=LocalResourceMeter)
self.collector = LocalResourceCollector(meter=self.mock_meter)
def test_get_name(self):
"""测试获取收集器名称"""
self.assertEqual(self.collector.get_name(), "localresource")
def test_get_storage(self):
"""测试获取存储"""
storage = self.collector.get_storage()
self.assertIsInstance(storage, list)
self.assertEqual(len(storage), 0)
def test_get_properties(self):
"""测试获取属性列表"""
properties = self.collector.get_properties()
self.assertEqual(properties, ["cpu", "memory", "io", "network", "loadaverage"])
def test_do_measure(self):
"""测试执行测量并存储结果"""
measurable = LocalResourceMeasurable(0.5, 0.6, 0.1, 0.2, 0.3)
self.mock_meter.measure.return_value = measurable
self.collector.do_measure()
storage = self.collector.get_storage()
self.assertEqual(len(storage), 1)
self.assertEqual(storage[0].cpu, 0.5)
self.assertEqual(storage[0].memory, 0.6)
self.assertEqual(storage[0].io, 0.1)
self.assertEqual(storage[0].network, 0.2)
self.assertEqual(storage[0].loadaverage, 0.3)
def test_do_measure_none_result(self):
"""测试测量返回 None 时不存储"""
self.mock_meter.measure.return_value = None
self.collector.do_measure()
storage = self.collector.get_storage()
self.assertEqual(len(storage), 0)
def test_do_measure_exception(self):
"""测试测量异常时不存储"""
self.mock_meter.measure.side_effect = Exception("measure error")
self.collector.do_measure()
storage = self.collector.get_storage()
self.assertEqual(len(storage), 0)
def test_storage_truncation(self):
"""测试存储截断"""
self.collector._sample = 3
for i in range(5):
measurable = LocalResourceMeasurable(
float(i) / 10,
float(i) / 10,
float(i) / 10,
float(i) / 10,
float(i) / 10,
)
self.collector._measurements.append(measurable)
new_measurable = LocalResourceMeasurable(0.9, 0.9, 0.9, 0.9, 0.9)
self.mock_meter.measure.return_value = new_measurable
self.collector.do_measure()
storage = self.collector.get_storage()
self.assertEqual(len(storage), 6)
def test_init_default_meter(self):
"""测试默认使用单例 Meter"""
with patch.object(LocalResourceMeter, "get_instance") as mock_get_instance:
mock_meter = Mock(spec=LocalResourceMeter)
mock_get_instance.return_value = mock_meter
collector = LocalResourceCollector()
self.assertEqual(collector._meter, mock_meter)
if __name__ == "__main__":
unittest.main()