From c5b4ce7718d40edd4a26e4529819ee906a323b31 Mon Sep 17 00:00:00 2001 From: Nate Gay Date: Sat, 27 Sep 2025 17:37:29 -0500 Subject: [PATCH 1/5] Ensure watchdog integration test cleans up after itself --- .../Components/Watchdog/Watchdog.cpp | 28 ++++++++++--- .../Components/Watchdog/Watchdog.fpp | 25 ++++++++---- .../Components/Watchdog/Watchdog.hpp | 34 ++++++++++++---- .../Components/Watchdog/docs/sdd.md | 17 +++++--- FprimeZephyrReference/test/int/README.md | 7 ---- .../{integration_test.py => watchdog_test.py} | 40 ++++++++++++++----- README.md | 12 ++++++ 7 files changed, 120 insertions(+), 43 deletions(-) delete mode 100644 FprimeZephyrReference/test/int/README.md rename FprimeZephyrReference/test/int/{integration_test.py => watchdog_test.py} (66%) diff --git a/FprimeZephyrReference/Components/Watchdog/Watchdog.cpp b/FprimeZephyrReference/Components/Watchdog/Watchdog.cpp index 106a3c73..91c8e102 100644 --- a/FprimeZephyrReference/Components/Watchdog/Watchdog.cpp +++ b/FprimeZephyrReference/Components/Watchdog/Watchdog.cpp @@ -22,8 +22,8 @@ Watchdog ::~Watchdog() {} // ---------------------------------------------------------------------- void Watchdog ::run_handler(FwIndexType portNum, U32 context) { - // Only perform actions when stop not requested - if (!this->m_stopRequested) { + // Only perform actions when run is enabled + if (this->m_run) { // Toggle state every rate group call this->m_state = (this->m_state == Fw::On::ON) ? Fw::On::OFF : Fw::On::ON; this->m_transitions++; @@ -33,9 +33,19 @@ void Watchdog ::run_handler(FwIndexType portNum, U32 context) { } } +void Watchdog ::start_handler(FwIndexType portNum) { + // Start the watchdog + this->m_run = true; + + // Report watchdog started + this->log_ACTIVITY_HI_WatchdogStart(); +} + void Watchdog ::stop_handler(FwIndexType portNum) { - // Set the stop flag to stop watchdog petting - this->m_stopRequested = true; + // Stop the watchdog + this->m_run = false; + + // Report watchdog stopped this->log_ACTIVITY_HI_WatchdogStop(); } @@ -43,7 +53,15 @@ void Watchdog ::stop_handler(FwIndexType portNum) { // Handler implementations for commands // ---------------------------------------------------------------------- -void Watchdog ::TEST_STOP_WATCHDOG_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) { +void Watchdog ::START_WATCHDOG_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) { + // call start handler + this->start_handler(0); + + // Provide command response + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); +} + +void Watchdog ::STOP_WATCHDOG_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) { // call stop handler this->stop_handler(0); diff --git a/FprimeZephyrReference/Components/Watchdog/Watchdog.fpp b/FprimeZephyrReference/Components/Watchdog/Watchdog.fpp index d2842702..7d8c585b 100644 --- a/FprimeZephyrReference/Components/Watchdog/Watchdog.fpp +++ b/FprimeZephyrReference/Components/Watchdog/Watchdog.fpp @@ -1,23 +1,34 @@ module Components { - @ Component to blink an LED as a watchdog petter, driven by a rate group + @ Component to pet the watchdog, driven by a rate group passive component Watchdog { + @ Command to start the watchdog + sync command START_WATCHDOG( + ) - @ Command to stop the watchdog petter - sync command TEST_STOP_WATCHDOG( + @ Command to stop the watchdog + sync command STOP_WATCHDOG( ) - @ Telemetry channel counting watchdog petter transitions + @ Telemetry channel counting watchdog pet transitions telemetry WatchdogTransitions: U32 - @ Event logged when the watchdog petter LED turns on or off + @ Event logged when the watchdog is started + event WatchdogStart() \ + severity activity high \ + format "Watchdog started" + + @ Event logged when the watchdog is stopped event WatchdogStop() \ severity activity high \ - format "Watchdog no longer being pet!" + format "Watchdog stopped" @ Port receiving calls from the rate group sync input port run: Svc.Sched - @ Port to stop the watchdog petting + @ Port to start the watchdog + sync input port start: Fw.Signal + + @ Port to stop the watchdog sync input port stop: Fw.Signal @ Port sending calls to the GPIO driver diff --git a/FprimeZephyrReference/Components/Watchdog/Watchdog.hpp b/FprimeZephyrReference/Components/Watchdog/Watchdog.hpp index b81c4201..1f96bde6 100644 --- a/FprimeZephyrReference/Components/Watchdog/Watchdog.hpp +++ b/FprimeZephyrReference/Components/Watchdog/Watchdog.hpp @@ -39,22 +39,40 @@ class Watchdog : public WatchdogComponentBase { U32 context //!< The call order ) override; + //! Handler implementation for start + //! + //! Port to start the watchdog + void start_handler(FwIndexType portNum //!< The port number + ) override; + //! Handler implementation for stop //! - //! Port to stop the watchdog petting - void stop_handler(FwIndexType portNum) override; + //! Port to stop the watchdog + void stop_handler(FwIndexType portNum //!< The port number + ) override; - private: // ---------------------------------------------------------------------- // Handler implementations for commands // ---------------------------------------------------------------------- - void TEST_STOP_WATCHDOG_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) override; + //! Handler implementation for command START_WATCHDOG + //! + //! Command to start the watchdog + void START_WATCHDOG_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq //!< The command sequence number + ) override; + + //! Handler implementation for command STOP_WATCHDOG + //! + //! Command to stop the watchdog + void STOP_WATCHDOG_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq //!< The command sequence number + ) override; - std::atomic_bool m_stopRequested{false}; //! Flag to stop the watchdog petting - Fw::On m_state = Fw::On::OFF; //! Keeps track of GPIO state - U32 m_transitions = 0; //! The number of on/off transitions that have occurred - //! from FSW boot up + std::atomic_bool m_run{true}; //! Flag to start or stop the watchdog + Fw::On m_state = Fw::On::OFF; //! Keeps track of GPIO state + U32 m_transitions = 0; //! The number of on/off transitions that have occurred + //! from FSW boot up }; } // namespace Components diff --git a/FprimeZephyrReference/Components/Watchdog/docs/sdd.md b/FprimeZephyrReference/Components/Watchdog/docs/sdd.md index 32efcb2f..15d011fe 100644 --- a/FprimeZephyrReference/Components/Watchdog/docs/sdd.md +++ b/FprimeZephyrReference/Components/Watchdog/docs/sdd.md @@ -13,9 +13,11 @@ Requirement | Description | Verification Method | Verified? WD-001 | The `Components::Watchdog` component shall activate upon startup. | Inspection | Yes WD-002 | The `Components::Watchdog` component shall oscillate the watchdog GPIO pin (24) on/off on each rategroup tick. | Inspection | Yes WD-003 | The `Components::Watchdog` component shall provide telemetry for watchdog transition count. | Integration Test | In Progress -WD-004 | The `Components::Watchdog` component shall respond to stop signals to halt the watchdog petting. | Integration Test | Yes -WD-005 | The `Components::Watchdog` component shall provide a test command to stop the watchdog petting. | Integration Test | Yes -WD-006 | The `Components::Watchdog` component shall emit an event when the watchdog petting stops. | Integration Test | Yes +WD-004 | The `Components::Watchdog` component shall respond to stop signals to halt the watchdog. | Integration Test | Yes +WD-005 | The `Components::Watchdog` component shall provide a test command to stop the watchdog. | Integration Test | Yes +WD-006 | The `Components::Watchdog` component shall emit an event when the watchdog stops. | Integration Test | Yes +WD-007 | The `Components::Watchdog` component shall provide a test command to start the watchdog. | Integration Test | Yes +WD-008 | The `Components::Watchdog` component shall emit an event when the watchdog starts. | Integration Test | Yes ## 3. Design @@ -32,20 +34,23 @@ The `Components::Watchdog` component has the following component diagram: Port Data Type | Name | Direction | Kind | Usage -------------- | ---- | --------- | ---- | ----- [`Svc::Sched`]| run | Input | Synchronous | Receive periodic calls from rate group -[`Fw::Signal`]| stop | Input | Synchronous | Receive stop signal to stop watchdog petter +[`Fw::Signal`]| start | Input | Synchronous | Receive start signal to start watchdog +[`Fw::Signal`]| stop | Input | Synchronous | Receive stop signal to stop watchdog [`Drv::GpioWrite`]| gpioSet | Output | n/a | Control GPIO state through driver #### 3.1.3 Commands Name | Kind | Description ---- | ---- | ----- -TEST_STOP_WATCHDOG | Synchronous | calls the port `stop_runhandler` to stop the watchdog petter. +START_WATCHDOG | Synchronous | calls the port `start_runhandler` to start the watchdog. +STOP_WATCHDOG | Synchronous | calls the port `stop_runhandler` to stop the watchdog. #### 3.1.4 Events Name | Description ---- | ----- -WatchdogStop | Emits once the watchdog petting has stopped. . +WatchdogStart | Emits once the watchdog has started. +WatchdogStop | Emits once the watchdog has stopped. #### 3.1.5 Telemetry diff --git a/FprimeZephyrReference/test/int/README.md b/FprimeZephyrReference/test/int/README.md deleted file mode 100644 index 55debf97..00000000 --- a/FprimeZephyrReference/test/int/README.md +++ /dev/null @@ -1,7 +0,0 @@ -### To run Integration Tests: - -First, start GDS with: - - -Run Integration Test: -pytest integration_test.py --deployment ../../../build-artifacts/zephyr/fprime-zephyr-deployment diff --git a/FprimeZephyrReference/test/int/integration_test.py b/FprimeZephyrReference/test/int/watchdog_test.py similarity index 66% rename from FprimeZephyrReference/test/int/integration_test.py rename to FprimeZephyrReference/test/int/watchdog_test.py index 48fdd32c..6e96b50c 100644 --- a/FprimeZephyrReference/test/int/integration_test.py +++ b/FprimeZephyrReference/test/int/watchdog_test.py @@ -5,6 +5,27 @@ Tests are ordered so that stop tests run last. """ +import time +import pytest + +@pytest.fixture(autouse=True) +def teardown_start_watchdog(fprime_test_api): + """Fixture to ensure watchdog is started before and after each test""" + start_watchdog(fprime_test_api) + yield + start_watchdog(fprime_test_api) + +def start_watchdog(fprime_test_api): + """Helper function to start the watchdog""" + fprime_test_api.send_and_assert_command( + "ReferenceDeployment.watchdog.START_WATCHDOG", + max_delay=2 + ) + fprime_test_api.assert_event( + "ReferenceDeployment.watchdog.WatchdogStart", + timeout=2 + ) + def get_watchdog_transitions(fprime_test_api): """Helper function to request packet and get fresh WatchdogTransitions telemetry""" fprime_test_api.clear_histories() @@ -24,7 +45,6 @@ def test_01_watchdog_telemetry_basic(fprime_test_api): def test_02_watchdog_increments(fprime_test_api): """Test that WatchdogTransitions increments over time""" - import time initial_value = get_watchdog_transitions(fprime_test_api) time.sleep(2.0) # Wait for watchdog to run more cycles @@ -35,28 +55,28 @@ def test_02_watchdog_increments(fprime_test_api): def test_03_stop_watchdog_command(fprime_test_api): - """Test TEST_STOP_WATCHDOG command sends and emits WatchdogStop event""" + """ + Test STOP_WATCHDOG command sends and emits WatchdogStop + event and WatchdogTransitions stops incrementing + """ fprime_test_api.clear_histories() + # Send stop command fprime_test_api.send_and_assert_command( - "ReferenceDeployment.watchdog.TEST_STOP_WATCHDOG", + "ReferenceDeployment.watchdog.STOP_WATCHDOG", max_delay=2 ) + # Check for watchdog stop event fprime_test_api.assert_event( "ReferenceDeployment.watchdog.WatchdogStop", timeout=2 ) - -def test_04_watchdog_stops_incrementing(fprime_test_api): - """Test that WatchdogTransitions stops incrementing after TEST_STOP_WATCHDOG""" - import time - - # Get initial value (should be from stopped watchdog from previous test) + # Get watchdog transition count initial_value = get_watchdog_transitions(fprime_test_api) - # Wait and check that it's not incrementing (watchdog should already be stopped) + # Wait and check that it's no longer incrementing time.sleep(2.0) final_value = get_watchdog_transitions(fprime_test_api) diff --git a/README.md b/README.md index 1f07bc40..6508ff02 100644 --- a/README.md +++ b/README.md @@ -60,3 +60,15 @@ Finally, run the fprime-gds. ```shell make gds ``` + +## Running Integration Tests + +First, start GDS with: +```sh +make gds +``` + +Then, in another terminal, run the following command to execute the integration tests: +```sh +pytest FprimeZephyrReference/test/int --deployment build-artifacts/zephyr/fprime-zephyr-deployment +``` From 6bff6f21346cb5d4379b869c9ed756ca6185b306 Mon Sep 17 00:00:00 2001 From: Nate Gay Date: Sat, 27 Sep 2025 17:45:00 -0500 Subject: [PATCH 2/5] Add integration test target to Makefile --- Makefile | 4 ++++ README.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1e9d030c..2b794612 100644 --- a/Makefile +++ b/Makefile @@ -55,6 +55,10 @@ build: submodules zephyr-setup fprime-venv generate-if-needed ## Build FPrime-Ze @echo "Building..." @$(UV) run fprime-util build +.PHONY: test-integration +test-integration: + @$(UV) run pytest FprimeZephyrReference/test/int --deployment build-artifacts/zephyr/fprime-zephyr-deployment + .PHONY: clean clean: ## Remove all gitignored files git clean -dfX diff --git a/README.md b/README.md index 6508ff02..ef4019bb 100644 --- a/README.md +++ b/README.md @@ -70,5 +70,5 @@ make gds Then, in another terminal, run the following command to execute the integration tests: ```sh -pytest FprimeZephyrReference/test/int --deployment build-artifacts/zephyr/fprime-zephyr-deployment +make test-integration ``` From b21ace824cec62a68ba1f0db9c3f4a7cd65fbf37 Mon Sep 17 00:00:00 2001 From: Nate Gay Date: Sat, 27 Sep 2025 18:15:56 -0500 Subject: [PATCH 3/5] Update watchdog tests --- FprimeZephyrReference/test/int/watchdog_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FprimeZephyrReference/test/int/watchdog_test.py b/FprimeZephyrReference/test/int/watchdog_test.py index 6e96b50c..1ee09837 100644 --- a/FprimeZephyrReference/test/int/watchdog_test.py +++ b/FprimeZephyrReference/test/int/watchdog_test.py @@ -1,5 +1,5 @@ """ -integration_test.py: +watchdog_test.py: Simple integration tests for the Watchdog component. Tests are ordered so that stop tests run last. @@ -9,7 +9,7 @@ import pytest @pytest.fixture(autouse=True) -def teardown_start_watchdog(fprime_test_api): +def ensure_watchdog_running(fprime_test_api): """Fixture to ensure watchdog is started before and after each test""" start_watchdog(fprime_test_api) yield From 61125032b8c2b68676676d669efb7e4a71c4c992 Mon Sep 17 00:00:00 2001 From: Nate Gay Date: Sat, 27 Sep 2025 23:58:08 -0500 Subject: [PATCH 4/5] Add typehinting --- .pre-commit-config.yaml | 9 ++++ .../Components/Watchdog/docs/sdd.md | 2 +- .../test/int/watchdog_test.py | 47 ++++++++++--------- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index db6a4491..794b0d8d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,3 +24,12 @@ repos: - id: cpplint args: - --config=cpplint.cfg + +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.13.2 + hooks: + - id: ruff-check + args: [--fix] + - id: ruff-check + args: [--fix, --select, I] # import sorting + - id: ruff-format diff --git a/FprimeZephyrReference/Components/Watchdog/docs/sdd.md b/FprimeZephyrReference/Components/Watchdog/docs/sdd.md index 15d011fe..9719d9a5 100644 --- a/FprimeZephyrReference/Components/Watchdog/docs/sdd.md +++ b/FprimeZephyrReference/Components/Watchdog/docs/sdd.md @@ -12,7 +12,7 @@ Requirement | Description | Verification Method | Verified? ----------- | ----------- | ------------------- | --------- WD-001 | The `Components::Watchdog` component shall activate upon startup. | Inspection | Yes WD-002 | The `Components::Watchdog` component shall oscillate the watchdog GPIO pin (24) on/off on each rategroup tick. | Inspection | Yes -WD-003 | The `Components::Watchdog` component shall provide telemetry for watchdog transition count. | Integration Test | In Progress +WD-003 | The `Components::Watchdog` component shall provide telemetry for watchdog transition count. | Integration Test | Yes WD-004 | The `Components::Watchdog` component shall respond to stop signals to halt the watchdog. | Integration Test | Yes WD-005 | The `Components::Watchdog` component shall provide a test command to stop the watchdog. | Integration Test | Yes WD-006 | The `Components::Watchdog` component shall emit an event when the watchdog stops. | Integration Test | Yes diff --git a/FprimeZephyrReference/test/int/watchdog_test.py b/FprimeZephyrReference/test/int/watchdog_test.py index 1ee09837..d58276c5 100644 --- a/FprimeZephyrReference/test/int/watchdog_test.py +++ b/FprimeZephyrReference/test/int/watchdog_test.py @@ -6,55 +6,61 @@ """ import time + import pytest +from fprime_gds.common.data_types.ch_data import ChData +from fprime_gds.common.testing_fw.api import IntegrationTestAPI + @pytest.fixture(autouse=True) -def ensure_watchdog_running(fprime_test_api): +def ensure_watchdog_running(fprime_test_api: IntegrationTestAPI): """Fixture to ensure watchdog is started before and after each test""" start_watchdog(fprime_test_api) yield start_watchdog(fprime_test_api) -def start_watchdog(fprime_test_api): + +def start_watchdog(fprime_test_api: IntegrationTestAPI): """Helper function to start the watchdog""" fprime_test_api.send_and_assert_command( - "ReferenceDeployment.watchdog.START_WATCHDOG", - max_delay=2 + "ReferenceDeployment.watchdog.START_WATCHDOG", max_delay=2 ) fprime_test_api.assert_event( - "ReferenceDeployment.watchdog.WatchdogStart", - timeout=2 + "ReferenceDeployment.watchdog.WatchdogStart", timeout=2 ) -def get_watchdog_transitions(fprime_test_api): + +def get_watchdog_transitions(fprime_test_api: IntegrationTestAPI) -> int: """Helper function to request packet and get fresh WatchdogTransitions telemetry""" fprime_test_api.clear_histories() - fprime_test_api.send_and_assert_command("CdhCore.tlmSend.SEND_PKT", ["5"], max_delay=2) - result = fprime_test_api.assert_telemetry( - "ReferenceDeployment.watchdog.WatchdogTransitions", - start="NOW", timeout=3 + fprime_test_api.send_and_assert_command( + "CdhCore.tlmSend.SEND_PKT", ["5"], max_delay=2 + ) + result: ChData = fprime_test_api.assert_telemetry( + "ReferenceDeployment.watchdog.WatchdogTransitions", start="NOW", timeout=3 ) return result.get_val() -def test_01_watchdog_telemetry_basic(fprime_test_api): +def test_01_watchdog_telemetry_basic(fprime_test_api: IntegrationTestAPI): """Test that we can read WatchdogTransitions telemetry""" value = get_watchdog_transitions(fprime_test_api) assert value >= 0, f"WatchdogTransitions should be >= 0, got {value}" -def test_02_watchdog_increments(fprime_test_api): +def test_02_watchdog_increments(fprime_test_api: IntegrationTestAPI): """Test that WatchdogTransitions increments over time""" initial_value = get_watchdog_transitions(fprime_test_api) time.sleep(2.0) # Wait for watchdog to run more cycles updated_value = get_watchdog_transitions(fprime_test_api) - assert updated_value > initial_value, \ + assert updated_value > initial_value, ( f"WatchdogTransitions should increase. Initial: {initial_value}, Updated: {updated_value}" + ) -def test_03_stop_watchdog_command(fprime_test_api): +def test_03_stop_watchdog_command(fprime_test_api: IntegrationTestAPI): """ Test STOP_WATCHDOG command sends and emits WatchdogStop event and WatchdogTransitions stops incrementing @@ -63,15 +69,11 @@ def test_03_stop_watchdog_command(fprime_test_api): # Send stop command fprime_test_api.send_and_assert_command( - "ReferenceDeployment.watchdog.STOP_WATCHDOG", - max_delay=2 + "ReferenceDeployment.watchdog.STOP_WATCHDOG", max_delay=2 ) # Check for watchdog stop event - fprime_test_api.assert_event( - "ReferenceDeployment.watchdog.WatchdogStop", - timeout=2 - ) + fprime_test_api.assert_event("ReferenceDeployment.watchdog.WatchdogStop", timeout=2) # Get watchdog transition count initial_value = get_watchdog_transitions(fprime_test_api) @@ -80,5 +82,6 @@ def test_03_stop_watchdog_command(fprime_test_api): time.sleep(2.0) final_value = get_watchdog_transitions(fprime_test_api) - assert final_value == initial_value, \ + assert final_value == initial_value, ( f"Watchdog should remain stopped. Initial: {initial_value}, Final: {final_value}" + ) From 5837f0d0b133933ab2ea0986b66f92d12ca9942e Mon Sep 17 00:00:00 2001 From: Nate Gay Date: Sun, 28 Sep 2025 00:15:09 -0500 Subject: [PATCH 5/5] Comment fix --- FprimeZephyrReference/test/int/watchdog_test.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/FprimeZephyrReference/test/int/watchdog_test.py b/FprimeZephyrReference/test/int/watchdog_test.py index d58276c5..93ff36eb 100644 --- a/FprimeZephyrReference/test/int/watchdog_test.py +++ b/FprimeZephyrReference/test/int/watchdog_test.py @@ -1,8 +1,7 @@ """ watchdog_test.py: -Simple integration tests for the Watchdog component. -Tests are ordered so that stop tests run last. +Integration tests for the Watchdog component. """ import time