import unittest from Infinity_Vis_1.infinity_vis_1.engine import OutputSettings, RealtimeEngine from Infinity_Vis_1.infinity_vis_1.models import MappingSpec, PatternParams, SceneState, SegmentSpec, TileSpec class RecordingDDPClient: def __init__(self) -> None: self.sent_payloads: list[dict[str, bytes]] = [] def count_packets(self, host_payloads: dict[str, bytes]) -> int: return len(host_payloads) def send(self, host_payloads: dict[str, bytes]) -> int: self.sent_payloads.append(dict(host_payloads)) return self.count_packets(host_payloads) class EngineTests(unittest.TestCase): def _mapping(self) -> MappingSpec: return MappingSpec( name="engine-test", rows=1, cols=2, tiles=[ TileSpec( tile_id="r1c1", row=1, col=1, led_total=2, controller_ip="192.168.0.10", segments=[SegmentSpec(name="all", side="top", start_channel=1, led_count=2)], ), TileSpec( tile_id="r1c2", row=1, col=2, led_total=2, controller_ip="192.168.0.10", segments=[SegmentSpec(name="all", side="top", start_channel=1, led_count=2)], ), ], ) def test_preview_rate_is_decoupled_from_output_rate(self) -> None: engine = RealtimeEngine( self._mapping(), scene=SceneState(pattern_id="solid", params=PatternParams(primary_color="#FF0000")), settings=OutputSettings(output_fps=40.0, preview_fps=10.0, delta_sending=False), ddp_client=RecordingDDPClient(), ) first = engine.tick(now=1.0) second = engine.tick(now=1.03) third = engine.tick(now=1.11) self.assertTrue(first.output_due) self.assertTrue(first.preview_due) self.assertTrue(second.output_due) self.assertFalse(second.preview_due) self.assertTrue(third.output_due) self.assertTrue(third.preview_due) self.assertEqual(engine.diagnostics.output_frames, 3) self.assertEqual(engine.diagnostics.preview_frames, 2) def test_delta_sending_skips_unchanged_frames_but_keeps_keepalive(self) -> None: engine = RealtimeEngine( self._mapping(), scene=SceneState(pattern_id="solid", params=PatternParams(primary_color="#00FF00")), settings=OutputSettings(output_fps=20.0, preview_fps=0.0, delta_sending=True, keepalive_seconds=0.35), ddp_client=RecordingDDPClient(), ) first = engine.tick(now=1.00) second = engine.tick(now=1.05) third = engine.tick(now=1.41) self.assertEqual(list(first.changed_payloads), ["192.168.0.10"]) self.assertEqual(second.changed_payloads, {}) self.assertEqual(list(third.changed_payloads), ["192.168.0.10"]) self.assertEqual(engine.diagnostics.stale_output_frames, 1) self.assertEqual(engine.diagnostics.keepalive_controller_frames, 1) def test_send_due_uses_client_without_network(self) -> None: client = RecordingDDPClient() engine = RealtimeEngine( self._mapping(), scene=SceneState(pattern_id="solid", params=PatternParams(primary_color="#112233")), settings=OutputSettings(output_fps=30.0, preview_fps=0.0, delta_sending=False), ddp_client=client, ) result = engine.send_due(now=2.0) self.assertEqual(result.packet_count, 1) self.assertEqual(len(client.sent_payloads), 1) self.assertEqual(engine.diagnostics.packets_sent, 1) if __name__ == "__main__": unittest.main()