7a0da7932b
Backwards Compatibility / Verify Encryption Constants (push) Waiting to run
Backwards Compatibility / PyPI Version Compatibility (push) Waiting to run
Backwards Compatibility / Database Migration Tests (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Blocked by required conditions
Docker Tests (Consolidated) / detect-changes (push) Waiting to run
Docker Tests (Consolidated) / Build Test Image (push) Waiting to run
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Blocked by required conditions
Docker Tests (Consolidated) / Accessibility Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Unit Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Example Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / Production Image Smoke Test (push) Blocked by required conditions
Docker Tests (Consolidated) / Infrastructure Tests (push) Blocked by required conditions
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Waiting to run
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
912 lines
27 KiB
Python
912 lines
27 KiB
Python
"""
|
|
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
|