diff --git a/PROVESFlightControllerReference/test/int/mode_manager_test.py b/PROVESFlightControllerReference/test/int/mode_manager_test.py index cb00ae73..7f42eb88 100644 --- a/PROVESFlightControllerReference/test/int/mode_manager_test.py +++ b/PROVESFlightControllerReference/test/int/mode_manager_test.py @@ -40,6 +40,7 @@ import pytest from common import proves_send_and_assert_command from fprime_gds.common.data_types.event_data import EventData +from fprime_gds.common.models.serialize.time_type import TimeType from fprime_gds.common.testing_fw.api import IntegrationTestAPI logger = logging.getLogger(__name__) @@ -543,6 +544,7 @@ def test_safe_08_clean_reboot_no_safe_mode( @pytest.mark.slow @pytest.mark.uart_only(reason="Requires reboot and GDS reconnect") +@pytest.mark.requires_watchdog_jumper def test_safe_09_command_loss_triggers_safe_mode_and_reboot( fprime_test_api: IntegrationTestAPI, start_gds ): @@ -581,24 +583,37 @@ def test_safe_09_command_loss_triggers_safe_mode_and_reboot( # Wait for the 1Hz run_handler to detect command loss (at most 2 seconds) fprime_test_api.assert_event(f"{component}.CommandLossDetected", timeout=5) - # Verify EnteringSafeMode event mentions loss of contact - events = fprime_test_api.get_event_test_history() - entering_events = [ - e for e in events if "EnteringSafeMode" in str(e.get_template().get_name()) - ] - assert len(entering_events) > 0, ( - "EnteringSafeMode event should be emitted on command loss" + # Verify EnteringSafeMode event mentions loss of contact. The firmware emits it a few + # tens of ms after CommandLossDetected, so wait for it rather than scraping the history + # snapshot (which races the EVR's arrival at the GDS). + entering_event = fprime_test_api.assert_event( + f"{component}.EnteringSafeMode", timeout=5 ) - assert "contact" in entering_events[-1].get_display_text().lower(), ( + assert "contact" in entering_event.get_display_text().lower(), ( "EnteringSafeMode should mention loss of contact" ) # stopWatchdog was called after safe mode entry — hardware reset expected in ~30 seconds - logger.info("Waiting for hardware reboot triggered by watchdog stop (~60s)...") - time.sleep(60.0) + reboot_start: TimeType = TimeType().set_datetime( + datetime.now(), time_base=TimeType.TimeBase("TB_DONT_CARE") + ) + logger.info("Waiting for hardware reboot triggered by watchdog stop...") + fprime_test_api.assert_event( + "CdhCore.version.FrameworkVersion", start=reboot_start, timeout=90 + ) - # Verify reboot occurred + # Verify reboot occurred. StartupManager increments the boot count lazily + # on its first 1Hz run tick, and FrameworkVersion is emitted during topology + # startup before rate groups run — so immediately after reboot detection + # GET_BOOT_COUNT can still return the pre-reboot count. Retry briefly until + # the increment lands. Asserting +1 (not just "a startup EVR arrived") also + # catches a boot loop: a watchdog that isn't re-fed on the new boot would + # keep resetting and drive the count past initial + 1. + deadline = time.monotonic() + 30.0 final_boot_count = _get_boot_count(fprime_test_api) + while final_boot_count == initial_boot_count and time.monotonic() < deadline: + time.sleep(2.0) + final_boot_count = _get_boot_count(fprime_test_api) assert final_boot_count == initial_boot_count + 1, ( f"Boot count should increment by 1 after command loss reboot. " f"Before: {initial_boot_count}, After: {final_boot_count}"