""" Tests for benchmarks/efficiency/resource_monitor.py Tests cover: - ResourceMonitor initialization - Start/stop monitoring - Resource data collection - Summary generation - Context manager usage """ import time class TestResourceMonitorInit: """Tests for ResourceMonitor initialization.""" def test_init_default_params(self): """Test initialization with default parameters.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() assert monitor.sampling_interval == 1.0 assert monitor.track_process is True assert monitor.track_system is True assert monitor.monitoring is False assert monitor.process_data == [] assert monitor.system_data == [] def test_init_custom_params(self): """Test initialization with custom parameters.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor( sampling_interval=0.5, track_process=False, track_system=True, ) assert monitor.sampling_interval == 0.5 assert monitor.track_process is False assert monitor.track_system is True class TestResourceMonitorStartStop: """Tests for start/stop monitoring.""" def test_start_without_psutil(self): """Test starting without psutil available.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.can_monitor = False # Should not raise, just log warning monitor.start() assert monitor.monitoring is False def test_start_already_monitoring(self): """Test starting when already monitoring.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.monitoring = True # Should not start another thread monitor.start() # Should remain monitoring assert monitor.monitoring is True def test_stop_not_monitoring(self): """Test stopping when not monitoring.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() # Should not raise monitor.stop() assert monitor.monitoring is False def test_stop_clears_monitoring_flag(self): """Test that stop clears the monitoring flag.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.monitoring = True monitor.monitor_thread = None monitor.stop() assert monitor.monitoring is False assert monitor.end_time is not None class TestResourceMonitorStats: """Tests for resource stats generation.""" def test_get_process_stats_no_data(self): """Test get_process_stats with no data.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() stats = monitor.get_process_stats() assert stats == {} def test_get_system_stats_no_data(self): """Test get_system_stats with no data.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() stats = monitor.get_system_stats() assert stats == {} def test_get_process_stats_with_data(self): """Test get_process_stats with process data.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.start_time = time.time() - 10 monitor.end_time = time.time() monitor.process_data = [ { "timestamp": time.time(), "cpu_percent": 50.0, "memory_rss": 100_000_000, "memory_vms": 200_000_000, "num_threads": 4, }, { "timestamp": time.time(), "cpu_percent": 60.0, "memory_rss": 110_000_000, "memory_vms": 210_000_000, "num_threads": 5, }, ] stats = monitor.get_process_stats() assert stats["sample_count"] == 2 assert stats["cpu_avg"] == 55.0 assert stats["cpu_max"] == 60.0 assert stats["cpu_min"] == 50.0 assert stats["thread_max"] == 5 def test_get_system_stats_with_data(self): """Test get_system_stats with system data.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.start_time = time.time() - 10 monitor.end_time = time.time() monitor.system_data = [ { "timestamp": time.time(), "cpu_percent": 30.0, "memory_percent": 50.0, "disk_percent": 60.0, "memory_total": 16_000_000_000, "disk_total": 500_000_000_000, }, { "timestamp": time.time(), "cpu_percent": 40.0, "memory_percent": 55.0, "disk_percent": 61.0, "memory_total": 16_000_000_000, "disk_total": 500_000_000_000, }, ] stats = monitor.get_system_stats() assert stats["sample_count"] == 2 assert stats["cpu_avg"] == 35.0 assert stats["cpu_max"] == 40.0 assert stats["memory_avg_percent"] == 52.5 def test_get_combined_stats(self): """Test get_combined_stats returns combined data.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.start_time = time.time() - 10 monitor.end_time = time.time() monitor.process_data = [ { "timestamp": time.time(), "cpu_percent": 50.0, "memory_rss": 100_000_000, "num_threads": 4, }, ] monitor.system_data = [ { "timestamp": time.time(), "cpu_percent": 30.0, "memory_percent": 50.0, "disk_percent": 60.0, "memory_total": 16_000_000_000, "disk_total": 500_000_000_000, }, ] stats = monitor.get_combined_stats() assert "duration" in stats assert "process_sample_count" in stats assert "system_sample_count" in stats def test_print_summary(self, capsys): """Test print_summary outputs correctly.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.start_time = time.time() - 10 monitor.end_time = time.time() monitor.process_data = [ { "timestamp": time.time(), "cpu_percent": 50.0, "memory_rss": 100_000_000, "num_threads": 4, }, ] monitor.print_summary() captured = capsys.readouterr() assert "RESOURCE USAGE SUMMARY" in captured.out def test_export_data(self): """Test export_data returns all collected data.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.start_time = time.time() - 10 monitor.end_time = time.time() monitor.process_data = [{"test": "data"}] monitor.system_data = [{"system": "data"}] data = monitor.export_data() assert data["start_time"] == monitor.start_time assert data["end_time"] == monitor.end_time assert data["sampling_interval"] == monitor.sampling_interval assert data["process_data"] == monitor.process_data assert data["system_data"] == monitor.system_data class TestResourceMonitorContextManager: """Tests for context manager functionality.""" def test_context_manager(self): """Test using ResourceMonitor as context manager.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.can_monitor = False # Disable actual monitoring with monitor.monitor(): pass # Should have stopped monitoring assert monitor.monitoring is False class TestResourceMonitorThreadSafety: """Tests for thread safety.""" def test_multiple_start_calls(self): """Test that multiple start calls don't create multiple threads.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.can_monitor = False # Should be safe to call multiple times monitor.start() monitor.start() monitor.start() # Should not be monitoring (psutil not available) assert monitor.monitoring is False class TestPsutilAvailability: """Tests for psutil availability handling.""" def test_psutil_available_flag(self): """Test that PSUTIL_AVAILABLE flag is set correctly.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( PSUTIL_AVAILABLE, ) # Should be a boolean assert isinstance(PSUTIL_AVAILABLE, bool) def test_monitor_with_psutil_unavailable(self): """Test monitoring when psutil is not available.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() # Simulate psutil not being available original_can_monitor = monitor.can_monitor monitor.can_monitor = False monitor.start() assert monitor.monitoring is False # Restore monitor.can_monitor = original_can_monitor class TestResourceMonitorDataCollection: """Tests for data collection methods.""" def test_process_data_structure(self): """Test that process data has expected structure.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.process_data = [ { "timestamp": time.time(), "cpu_percent": 25.0, "memory_rss": 50_000_000, "memory_vms": 100_000_000, "memory_shared": 10_000_000, "num_threads": 4, "open_files": 10, "status": "running", } ] # Check structure data = monitor.process_data[0] assert "timestamp" in data assert "cpu_percent" in data assert "memory_rss" in data def test_system_data_structure(self): """Test that system data has expected structure.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.system_data = [ { "timestamp": time.time(), "cpu_percent": 30.0, "cpu_per_core": [25.0, 35.0], "memory_total": 16_000_000_000, "memory_used": 8_000_000_000, "memory_percent": 50.0, } ] data = monitor.system_data[0] assert "timestamp" in data assert "cpu_percent" in data class TestResourceMonitorDuration: """Tests for duration tracking.""" def test_duration_calculation(self): """Test that duration is calculated correctly.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() start = time.time() - 5.0 end = time.time() monitor.start_time = start monitor.end_time = end monitor.process_data = [ { "timestamp": start, "cpu_percent": 50.0, "memory_rss": 100_000_000, "num_threads": 4, }, ] stats = monitor.get_process_stats() assert 4.9 < stats["duration"] < 5.1 # Allow for small variance class TestResourceMonitorSamplingInterval: """Tests for sampling interval configuration.""" def test_default_sampling_interval(self): """Test default sampling interval is 1.0 seconds.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() assert monitor.sampling_interval == 1.0 def test_custom_sampling_interval(self): """Test custom sampling interval is stored correctly.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor(sampling_interval=0.25) assert monitor.sampling_interval == 0.25 def test_very_small_sampling_interval(self): """Test very small sampling interval is accepted.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor(sampling_interval=0.01) assert monitor.sampling_interval == 0.01 class TestResourceMonitorTrackingOptions: """Tests for process/system tracking options.""" def test_track_process_only(self): """Test tracking only process resources.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor(track_process=True, track_system=False) assert monitor.track_process is True assert monitor.track_system is False def test_track_system_only(self): """Test tracking only system resources.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor(track_process=False, track_system=True) assert monitor.track_process is False assert monitor.track_system is True def test_track_both(self): """Test tracking both process and system resources.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor(track_process=True, track_system=True) assert monitor.track_process is True assert monitor.track_system is True def test_track_neither(self): """Test tracking neither process nor system resources.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor(track_process=False, track_system=False) assert monitor.track_process is False assert monitor.track_system is False class TestResourceMonitorMemoryCalculations: """Tests for memory calculation logic.""" def test_memory_rss_conversion_to_mb(self): """Test that memory RSS is correctly converted to MB.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.start_time = time.time() - 10 monitor.end_time = time.time() # 100 MB in bytes monitor.process_data = [ { "timestamp": time.time(), "cpu_percent": 50.0, "memory_rss": 104_857_600, # 100 MB in bytes "num_threads": 4, }, ] stats = monitor.get_process_stats() # Should be converted to MB assert 99 < stats["memory_max_mb"] < 101 def test_memory_stats_min_max_avg(self): """Test memory min/max/avg calculations.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.start_time = time.time() - 10 monitor.end_time = time.time() monitor.process_data = [ { "timestamp": time.time(), "cpu_percent": 50.0, "memory_rss": 50_000_000, # ~47.68 MB "num_threads": 4, }, { "timestamp": time.time(), "cpu_percent": 50.0, "memory_rss": 100_000_000, # ~95.37 MB "num_threads": 4, }, { "timestamp": time.time(), "cpu_percent": 50.0, "memory_rss": 150_000_000, # ~143.05 MB "num_threads": 4, }, ] stats = monitor.get_process_stats() assert ( stats["memory_min_mb"] < stats["memory_avg_mb"] < stats["memory_max_mb"] ) class TestResourceMonitorCPUCalculations: """Tests for CPU calculation logic.""" def test_cpu_stats_with_varying_values(self): """Test CPU stats with varying values.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.start_time = time.time() - 10 monitor.end_time = time.time() monitor.process_data = [ { "timestamp": time.time(), "cpu_percent": 10.0, "memory_rss": 100_000_000, "num_threads": 4, }, { "timestamp": time.time(), "cpu_percent": 50.0, "memory_rss": 100_000_000, "num_threads": 4, }, { "timestamp": time.time(), "cpu_percent": 90.0, "memory_rss": 100_000_000, "num_threads": 4, }, ] stats = monitor.get_process_stats() assert stats["cpu_min"] == 10.0 assert stats["cpu_max"] == 90.0 assert stats["cpu_avg"] == 50.0 class TestResourceMonitorSystemStats: """Tests for system stats calculations.""" def test_system_disk_stats(self): """Test system disk stats calculation.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.start_time = time.time() - 10 monitor.end_time = time.time() monitor.system_data = [ { "timestamp": time.time(), "cpu_percent": 30.0, "memory_percent": 50.0, "disk_percent": 40.0, "memory_total": 16_000_000_000, "disk_total": 500_000_000_000, }, { "timestamp": time.time(), "cpu_percent": 40.0, "memory_percent": 55.0, "disk_percent": 60.0, "memory_total": 16_000_000_000, "disk_total": 500_000_000_000, }, ] stats = monitor.get_system_stats() assert stats["disk_min_percent"] == 40.0 assert stats["disk_max_percent"] == 60.0 assert stats["disk_avg_percent"] == 50.0 def test_system_memory_total_conversion(self): """Test that system memory total is converted to GB.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.start_time = time.time() - 10 monitor.end_time = time.time() # 16 GB in bytes monitor.system_data = [ { "timestamp": time.time(), "cpu_percent": 30.0, "memory_percent": 50.0, "disk_percent": 40.0, "memory_total": 17_179_869_184, # 16 GB "disk_total": 500_000_000_000, }, ] stats = monitor.get_system_stats() assert 15.9 < stats["memory_total_gb"] < 16.1 class TestResourceMonitorCombinedStats: """Additional tests for combined stats.""" def test_combined_stats_process_memory_percent(self): """Test that combined stats calculates process memory percent.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.start_time = time.time() - 10 monitor.end_time = time.time() # Process using 1GB of 16GB system memory monitor.process_data = [ { "timestamp": time.time(), "cpu_percent": 50.0, "memory_rss": 1_073_741_824, # 1 GB "num_threads": 4, }, ] monitor.system_data = [ { "timestamp": time.time(), "cpu_percent": 30.0, "memory_percent": 50.0, "disk_percent": 40.0, "memory_total": 17_179_869_184, # 16 GB "disk_total": 500_000_000_000, }, ] stats = monitor.get_combined_stats() # 1 GB / 16 GB = 6.25% assert "process_memory_percent" in stats assert 6.0 < stats["process_memory_percent"] < 6.5 def test_combined_stats_includes_duration(self): """Test that combined stats includes duration.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.start_time = time.time() - 5.0 monitor.end_time = time.time() stats = monitor.get_combined_stats() assert "duration" in stats assert 4.9 < stats["duration"] < 5.1 class TestResourceMonitorExport: """Tests for data export functionality.""" def test_export_data_includes_timestamps(self): """Test that exported data includes timestamps.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.start_time = time.time() - 10 monitor.end_time = time.time() data = monitor.export_data() assert "start_time" in data assert "end_time" in data assert data["start_time"] is not None assert data["end_time"] is not None def test_export_data_includes_sampling_interval(self): """Test that exported data includes sampling interval.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor(sampling_interval=0.5) data = monitor.export_data() assert data["sampling_interval"] == 0.5 def test_export_data_includes_empty_lists_when_no_data(self): """Test that export returns empty lists when no data collected.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() data = monitor.export_data() assert data["process_data"] == [] assert data["system_data"] == [] class TestResourceMonitorEdgeCases: """Edge case tests for ResourceMonitor.""" def test_stats_with_single_sample(self): """Test stats calculation with single sample.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.start_time = time.time() - 1 monitor.end_time = time.time() monitor.process_data = [ { "timestamp": time.time(), "cpu_percent": 50.0, "memory_rss": 100_000_000, "num_threads": 4, }, ] stats = monitor.get_process_stats() # With single sample, min == max == avg assert stats["cpu_min"] == stats["cpu_max"] == stats["cpu_avg"] def test_duration_none_when_end_time_not_set(self): """Test that duration is None when end_time not set.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.start_time = time.time() monitor.end_time = None monitor.process_data = [ { "timestamp": time.time(), "cpu_percent": 50.0, "memory_rss": 100_000_000, "num_threads": 4, }, ] stats = monitor.get_process_stats() assert stats["duration"] is None def test_thread_max_tracking(self): """Test that max thread count is tracked correctly.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.start_time = time.time() - 10 monitor.end_time = time.time() monitor.process_data = [ { "timestamp": time.time(), "cpu_percent": 50.0, "memory_rss": 100_000_000, "num_threads": 4, }, { "timestamp": time.time(), "cpu_percent": 50.0, "memory_rss": 100_000_000, "num_threads": 8, }, { "timestamp": time.time(), "cpu_percent": 50.0, "memory_rss": 100_000_000, "num_threads": 6, }, ] stats = monitor.get_process_stats() assert stats["thread_max"] == 8 def test_print_summary_with_empty_data(self, capsys): """Test print_summary handles empty data gracefully.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() # Should not raise monitor.print_summary() captured = capsys.readouterr() assert "RESOURCE USAGE SUMMARY" in captured.out class TestResourceMonitorCanMonitorFlag: """Tests for can_monitor flag behavior.""" def test_can_monitor_matches_psutil_available(self): """Test that can_monitor matches PSUTIL_AVAILABLE.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, PSUTIL_AVAILABLE, ) monitor = ResourceMonitor() assert monitor.can_monitor == PSUTIL_AVAILABLE def test_start_does_nothing_when_cannot_monitor(self): """Test that start does nothing when can_monitor is False.""" from local_deep_research.benchmarks.efficiency.resource_monitor import ( ResourceMonitor, ) monitor = ResourceMonitor() monitor.can_monitor = False monitor.start() assert monitor.monitoring is False assert monitor.start_time is None