#!/usr/bin/env python """operator_e2e.py -- scripted end-to-end test of the operator console. Drives the real Operator window through its own methods on a timer: start a RELAY session -> launch 2 local btl4 instances -> wait for the mission LAUNCH to be observed by the session monitor -> report + shut down. Exercises the exact QProcess paths the buttons use (no synthetic clicks). Exit 0 = launched; 1 = timed out. """ import os import sys HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, os.path.join(HERE, "..", "tools")) from PySide6.QtCore import QTimer from PySide6.QtWidgets import QApplication, QMessageBox import btoperator # never block on dialogs in scripted mode QMessageBox.warning = staticmethod(lambda *a, **k: print("[e2e] WARN dialog:", a[2] if len(a) > 2 else a)) QMessageBox.information = staticmethod(lambda *a, **k: None) TIMEOUT_S = 120 result = {"ok": False} def main(): app = QApplication(sys.argv) btoperator.dark_palette(app) win = btoperator.Operator() win.show() def step_start(): print("[e2e] starting relay session") win._start_session() assert win.console_proc is not None, "session did not start" QTimer.singleShot(3000, step_launch) def step_launch(): print("[e2e] launching local instances") win._launch_local() assert len(win.game_procs) == 2, \ "expected 2 local instances, got %d" % len(win.game_procs) poll.start(2000) ticks = {"n": 0} pressed = {"launch": False} def step_poll(): ticks["n"] += 1 m = win.monitor states = dict(m.state) if m else {} print("[e2e] t=%ds states=%s ready=%s launched=%s" % (ticks["n"] * 2, states, m.ready if m else "?", m.launched if m else "?")) # MANUAL GATE: when the relay reports ready, press the Launch button. if m and m.ready and not m.launched and not pressed["launch"]: assert win.launch_btn.isEnabled(), "Launch button should be enabled" print("[e2e] relay READY -> pressing LAUNCH MISSION") win._launch_mission() pressed["launch"] = True if m and m.launched: result["ok"] = True status = win.pod_status.text().encode("ascii", "replace").decode() print("[e2e] LAUNCH observed (operator-gated=%s) -- pod status: %s" % (pressed["launch"], status)) finish() elif ticks["n"] * 2 >= TIMEOUT_S: print("[e2e] TIMEOUT") finish() def finish(): poll.stop() win._stop_session() QTimer.singleShot(500, app.quit) poll = QTimer() poll.timeout.connect(step_poll) QTimer.singleShot(1000, step_start) app.exec() print("[e2e] %s" % ("PASS" if result["ok"] else "FAIL")) return 0 if result["ok"] else 1 if __name__ == "__main__": sys.exit(main())