"""High-value tests for benchmarks/efficiency/resource_monitor.py. Covers ResourceMonitor init, start/stop lifecycle, stats computation, export_data, and context manager. """ from unittest.mock import patch from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) class TestResourceMonitorInit: """Test ResourceMonitor initialization.""" def test_default_sampling_interval(self): monitor = ResourceMonitor() assert monitor.sampling_interval == 1.0 def test_custom_sampling_interval(self): monitor = ResourceMonitor(sampling_interval=0.5) assert monitor.sampling_interval == 0.5 def test_not_monitoring_initially(self): monitor = ResourceMonitor() assert monitor.monitoring is False assert monitor.monitor_thread is None def test_empty_data_initially(self): monitor = ResourceMonitor() assert monitor.process_data == [] assert monitor.system_data == [] def test_track_flags_stored(self): monitor = ResourceMonitor(track_process=False, track_system=True) assert monitor.track_process is False assert monitor.track_system is True class TestResourceMonitorStats: """Test stats computation with pre-populated data.""" def test_get_process_stats_empty_returns_empty_dict(self): """Empty process_data returns empty dict.""" monitor = ResourceMonitor() assert monitor.get_process_stats() == {} def test_get_system_stats_empty_returns_empty_dict(self): """Empty system_data returns empty dict.""" monitor = ResourceMonitor() assert monitor.get_system_stats() == {} def test_get_process_stats_with_data(self): """Process stats are computed correctly from pre-populated data.""" monitor = ResourceMonitor() monitor.start_time = 100.0 monitor.end_time = 110.0 monitor.process_data = [ { "timestamp": 101.0, "cpu_percent": 10.0, "memory_rss": 100 * 1024 * 1024, # 100 MB "memory_vms": 200 * 1024 * 1024, "memory_shared": 0, "num_threads": 4, "open_files": 2, "status": "running", }, { "timestamp": 102.0, "cpu_percent": 20.0, "memory_rss": 200 * 1024 * 1024, # 200 MB "memory_vms": 300 * 1024 * 1024, "memory_shared": 0, "num_threads": 6, "open_files": 3, "status": "running", }, ] stats = monitor.get_process_stats() assert stats["duration"] == 10.0 assert stats["sample_count"] == 2 assert stats["cpu_min"] == 10.0 assert stats["cpu_max"] == 20.0 assert stats["cpu_avg"] == 15.0 assert stats["memory_min_mb"] == 100.0 assert stats["memory_max_mb"] == 200.0 assert stats["thread_max"] == 6 def test_get_system_stats_with_data(self): """System stats are computed correctly.""" monitor = ResourceMonitor() monitor.start_time = 100.0 monitor.end_time = 110.0 monitor.system_data = [ { "timestamp": 101.0, "cpu_percent": 30.0, "memory_total": 16 * 1024**3, "memory_available": 8 * 1024**3, "memory_used": 8 * 1024**3, "memory_percent": 50.0, "disk_total": 500 * 1024**3, "disk_used": 250 * 1024**3, "disk_percent": 50.0, }, ] stats = monitor.get_system_stats() assert stats["sample_count"] == 1 assert stats["cpu_min"] == 30.0 assert stats["memory_total_gb"] == 16.0 assert stats["disk_total_gb"] == 500.0 def test_get_combined_stats_merges_both(self): """Combined stats include prefixed process and system stats.""" monitor = ResourceMonitor() monitor.start_time = 100.0 monitor.end_time = 110.0 monitor.process_data = [ { "timestamp": 101.0, "cpu_percent": 10.0, "memory_rss": 100 * 1024 * 1024, "memory_vms": 200 * 1024 * 1024, "memory_shared": 0, "num_threads": 4, "open_files": 2, "status": "running", }, ] monitor.system_data = [ { "timestamp": 101.0, "cpu_percent": 30.0, "memory_total": 16 * 1024**3, "memory_available": 8 * 1024**3, "memory_used": 8 * 1024**3, "memory_percent": 50.0, "disk_total": 500 * 1024**3, "disk_used": 250 * 1024**3, "disk_percent": 50.0, }, ] stats = monitor.get_combined_stats() assert "process_cpu_avg" in stats assert "system_cpu_avg" in stats assert stats["duration"] == 10.0 class TestResourceMonitorExport: """Test export_data.""" def test_export_structure(self): """Export contains all expected keys.""" monitor = ResourceMonitor(sampling_interval=2.0) monitor.start_time = 100.0 monitor.end_time = 110.0 data = monitor.export_data() assert data["start_time"] == 100.0 assert data["end_time"] == 110.0 assert data["sampling_interval"] == 2.0 assert data["process_data"] == [] assert data["system_data"] == [] class TestResourceMonitorLifecycle: """Test start/stop lifecycle.""" def test_stop_when_not_monitoring_is_noop(self): """Stopping when not monitoring does nothing.""" monitor = ResourceMonitor() monitor.stop() # Should not raise assert monitor.monitoring is False @patch( "local_deep_research.benchmarks.efficiency.resource_monitor.PSUTIL_AVAILABLE", False, ) def test_start_without_psutil_is_noop(self): """Start without psutil logs warning but doesn't crash.""" monitor = ResourceMonitor() monitor.can_monitor = False monitor.start() assert monitor.monitoring is False