"""Tests for TelemetryCollector.""" from __future__ import annotations import time from collections import deque from unittest.mock import MagicMock, patch class TestTelemetryCollector: """Test telemetry calculations in isolation (no Qt event loop required).""" def _make_collector(self): """Construct a TelemetryCollector bypassing Qt machinery.""" from app.telemetry.telemetry_collector import TelemetryCollector with patch.object(TelemetryCollector, "__init__", return_value=None): col = TelemetryCollector.__new__(TelemetryCollector) col._frame_times = deque(maxlen=120) col._last_frame_time = 0.0 col._total_frames = 0 col._dropped_frames = 0 col._fps_window = deque() col._fps_window_size_s = 1.0 col._process = MagicMock() col._process.memory_info.return_value.rss = 50 * 1024 * 1024 # 50 MB return col def test_initial_snapshot_has_zero_fps(self): col = self._make_collector() snap = col._compute_snapshot() assert snap.fps == 0.0 def test_fps_counts_frames_in_window(self): col = self._make_collector() now = time.perf_counter() # Simulate 30 frames within the last second for i in range(30): col._fps_window.append(now - 0.9 + i * 0.03) snap = col._compute_snapshot() assert snap.fps == 30.0 def test_fps_excludes_old_frames(self): col = self._make_collector() now = time.perf_counter() # 10 frames older than 1 second — should not count for i in range(10): col._fps_window.append(now - 2.0 + i * 0.05) # 5 frames within the last second for i in range(5): col._fps_window.append(now - 0.4 + i * 0.05) snap = col._compute_snapshot() assert snap.fps == 5.0 def test_frame_time_average(self): col = self._make_collector() # 10 frames at 33.3 ms each interval = 0.0333 for _ in range(10): col._frame_times.append(interval) snap = col._compute_snapshot() assert abs(snap.frame_time_ms - interval * 1000) < 0.1 def test_drop_detection(self): col = self._make_collector() # Seed with 10 normal frames at ~16 ms normal_interval = 0.016 now = time.perf_counter() col._last_frame_time = now - normal_interval * 10 for _ in range(9): col._last_frame_time += normal_interval col._frame_times.append(normal_interval) # Simulate a big gap (3× normal) — should trigger drop detection col._last_frame_time = now - normal_interval * 10 # reset base # Manually call on_frame-like logic with patch("app.telemetry.telemetry_collector.time") as mock_time: # Set last_frame_time to something reasonable col._last_frame_time = now big_delta = normal_interval * 5 # 5× average → drop mock_time.perf_counter.return_value = now + big_delta # Replicate the drop detection logic delta = big_delta col._frame_times.append(delta) avg = sum(col._frame_times) / len(col._frame_times) if delta > avg * 2.5: col._dropped_frames += 1 assert col._dropped_frames == 1 def test_reset_counters(self): col = self._make_collector() col._total_frames = 100 col._dropped_frames = 5 col._frame_times.append(0.016) col._fps_window.append(time.perf_counter()) col.reset_counters() assert col._total_frames == 0 assert col._dropped_frames == 0 assert len(col._frame_times) == 0 assert len(col._fps_window) == 0 def test_snapshot_memory_mb(self): col = self._make_collector() snap = col._compute_snapshot() assert snap.memory_mb == 50.0