import types import time import pytest from target_simulator.simulation.simulation_controller import SimulationController class DummyVar: def __init__(self, value=None): self._v = value def get(self): return self._v def set(self, v): self._v = v class DummyComm: def __init__(self, use_json=False, send_results=None): self.is_open = True self._use_json_protocol = use_json # send_results can be a list or a callable self._send_calls = [] self._send_results = send_results def send_commands(self, cmds): self._send_calls.append(cmds) if callable(self._send_results): return self._send_results(cmds) if isinstance(self._send_results, list): if len(self._send_results) >= len(self._send_calls): return self._send_results[len(self._send_calls) - 1] return self._send_results[-1] return True def send_scenario(self, scenario): return True def router(self): return None class DummyCommManager: def __init__(self, target_comm=None): self.target_communicator = target_comm class DummyHub: def __init__(self, active_sequence=None): # active_sequence: iterable of booleans returned by has_active_real_targets self._seq = list(active_sequence) if active_sequence is not None else [] def has_active_real_targets(self): if self._seq: return self._seq.pop(0) return False def reset(self): pass class DummyScenario: def __init__(self): class T: _total_duration_s = 5.0 self.targets = [T()] def get_all_targets(self): return self.targets def reset_simulation(self): pass class DummyMainView: def __init__(self): self.is_simulation_running = DummyVar(False) self.update_time = DummyVar(1.0) self.time_multiplier = 1.0 self.scenario = DummyScenario() self.ppi_widget = types.SimpleNamespace(clear_trails=lambda: None, clear_previews=lambda: None) self.sim_slider_var = DummyVar(0.0) self.simulation_engine = None self.current_archive = None def _update_simulation_progress_display(self): pass def _update_button_states(self): pass def _refresh_analysis_list(self): pass def test_reset_returns_false_when_not_connected(): hub = DummyHub() cm = DummyCommManager(None) controller = SimulationController(cm, hub, config_manager=None, logger=None) mv = DummyMainView() assert controller.reset_radar_state(mv) is False def test_legacy_reset_success(monkeypatch): # Legacy communicator: _use_json_protocol False comm = DummyComm(use_json=False, send_results=[True]) cm = DummyCommManager(comm) hub = DummyHub(active_sequence=[False]) controller = SimulationController(cm, hub, config_manager=None, logger=None) mv = DummyMainView() assert controller.reset_radar_state(mv) is True def test_json_reset_fallback_to_per_target(monkeypatch): # JSON communicator that fails simple reset but succeeds per-target results = [False, True] # first send (json reset) -> False, second (per-target) -> True def send_results(cmds): # pop result accordingly return results.pop(0) comm = DummyComm(use_json=True, send_results=send_results) cm = DummyCommManager(comm) # hub returns False (cleared) after per-target hub = DummyHub(active_sequence=[True, False]) # Patch command_builder.build_json_reset_ids to return two payloads import target_simulator.simulation.simulation_controller as scmod monkeypatch.setattr(scmod.command_builder, "build_json_reset_ids", lambda: ["{\"id\":1}"]) controller = SimulationController(cm, hub, config_manager=None, logger=None) mv = DummyMainView() assert controller.reset_radar_state(mv) is True def test_start_simulation_creates_engine_and_sets_running(monkeypatch): # Patch SimulationEngine used in controller to a dummy engine class DummyEngine: def __init__(self, communicator=None, simulation_hub=None): self.started = False def set_time_multiplier(self, v): pass def set_update_interval(self, v): pass def load_scenario(self, s): pass def start(self): self.started = True def stop(self): self.started = False def is_running(self): return self.started monkeypatch.setattr("target_simulator.simulation.simulation_controller.SimulationEngine", DummyEngine) comm = DummyComm(use_json=False, send_results=[True]) cm = DummyCommManager(comm) hub = DummyHub() controller = SimulationController(cm, hub, config_manager=None, logger=None) mv = DummyMainView() # start simulation controller.start_simulation(mv) # engine should be created and main_view.simulation_engine set assert mv.simulation_engine is not None assert mv.is_simulation_running.get() is True