#!/usr/bin/env python3 """Offline tests for the EAI teleoperation session gate.""" from __future__ import annotations import importlib.util import json from pathlib import Path from types import ModuleType, SimpleNamespace import sys import unittest fake_omnisocket = ModuleType("omnisocket") fake_omnisocket.CONTROL_DEFAULTS = {} fake_omnisocket.MSG_TYPE_ERROR = 5 fake_omnisocket.Session = object sys.modules.setdefault("omnisocket", fake_omnisocket) module_path = Path(__file__).with_name("omnisocket_xtele_sender.py") spec = importlib.util.spec_from_file_location("omnisocket_xtele_sender", module_path) assert spec is not None and spec.loader is not None sender_module = importlib.util.module_from_spec(spec) spec.loader.exec_module(sender_module) XteleSender = sender_module.XteleSender def buttons(pressed: bool) -> dict[str, object]: return { "button": { "left": [False, False, pressed], "right": [False, False, pressed], }, "hand": {"position": {"left": 0.0, "right": 0.0}}, } class SessionGateTest(unittest.TestCase): def setUp(self) -> None: args = SimpleNamespace( start_stop_hold_s=3.0, start_marker_frames=50, source_timeout_s=0.25, max_feedback_age_ms=500.0, max_pending_frames=100, ) self.sender = XteleSender(args) def test_start_and_stop_each_require_a_new_continuous_hold(self) -> None: # Boot requires a release, so a button held across service restart # cannot start a session. self.assertIsNone(self.sender._update_teleop_gate(0.0, buttons(True))) self.assertIsNone(self.sender._update_teleop_gate(0.1, buttons(False))) self.assertIsNone(self.sender._update_teleop_gate(1.0, buttons(True))) self.assertIsNone(self.sender._update_teleop_gate(3.99, buttons(True))) self.assertEqual( self.sender._update_teleop_gate(4.01, buttons(True)), "start" ) first_id = self.sender.teleop_session_id self.assertTrue(self.sender.teleop_active) self.assertIsNotNone(first_id) # Keeping the same hold cannot immediately toggle the new session off. self.assertIsNone(self.sender._update_teleop_gate(8.0, buttons(True))) self.assertTrue(self.sender.teleop_active) self.assertIsNone(self.sender._update_teleop_gate(8.1, buttons(False))) self.assertIsNone(self.sender._update_teleop_gate(9.0, buttons(True))) self.assertIsNone(self.sender._update_teleop_gate(11.99, buttons(True))) self.assertEqual( self.sender._update_teleop_gate(12.01, buttons(True)), "stop" ) self.assertFalse(self.sender.teleop_active) self.assertEqual(self.sender.teleop_session_id, first_id) def test_releasing_during_hold_resets_the_timer(self) -> None: self.sender._update_teleop_gate(0.0, buttons(False)) self.sender._update_teleop_gate(1.0, buttons(True)) self.sender._update_teleop_gate(2.0, buttons(False)) self.sender._update_teleop_gate(3.0, buttons(True)) self.assertIsNone(self.sender._update_teleop_gate(5.9, buttons(True))) self.assertEqual( self.sender._update_teleop_gate(6.01, buttons(True)), "start" ) def test_transport_metadata_is_overwritten(self) -> None: data = buttons(False) data["tg3_transport"] = { "protocol_version": 999, "session_id": "forged", "session_state": "stop", } payload, merged = self.sender._build_payload( data, None, "a" * 32, 7, "start", ) self.assertFalse(merged) metadata = json.loads(payload)["tg3_transport"] self.assertEqual(metadata["protocol_version"], 2) self.assertEqual(metadata["session_id"], "a" * 32) self.assertEqual(metadata["session_seq"], 7) self.assertEqual(metadata["session_state"], "start") def test_stale_or_backlogged_transport_is_rejected(self) -> None: class FakeSession: def __init__(self, feedback_age: int, pending: int) -> None: self.feedback_age = feedback_age self.pending = pending def stats(self) -> dict[str, int]: return {"connected": 1, "registered": 1} def kcp_stats(self) -> dict[str, int]: return { "snd_queue": self.pending, "snd_buffer": 0, "last_feedback_age_ms": self.feedback_age, } self.sender.session_connected_at = 0.0 self.sender.session = FakeSession(600, 0) self.assertIn("feedback stale", self.sender._session_unhealthy_reason()) self.sender.session = FakeSession(1, 101) self.assertIn("pending queue", self.sender._session_unhealthy_reason()) if __name__ == "__main__": unittest.main()