diff --git a/score/launch_manager/src/daemon/src/process_group_manager/BUILD b/score/launch_manager/src/daemon/src/process_group_manager/BUILD index 33ac2aafa..2b0a0e403 100644 --- a/score/launch_manager/src/daemon/src/process_group_manager/BUILD +++ b/score/launch_manager/src/daemon/src/process_group_manager/BUILD @@ -32,6 +32,19 @@ cc_library( visibility = ["//score:__subpackages__"], ) +cc_library( + name = "ialive_monitor_thread_mock", + testonly = True, + hdrs = ["alive_monitor_thread_mock.hpp"], + include_prefix = "score/mw/launch_manager/process_group_manager", + strip_include_prefix = "/score/launch_manager/src/daemon/src/process_group_manager", + visibility = ["//score:__subpackages__"], + deps = [ + ":ialive_monitor_thread", + "@googletest//:gtest", + ], +) + cc_library( name = "alive_monitor_thread", srcs = ["alive_monitor_thread.cpp"], diff --git a/score/launch_manager/src/daemon/src/process_group_manager/alive_monitor_thread_mock.hpp b/score/launch_manager/src/daemon/src/process_group_manager/alive_monitor_thread_mock.hpp new file mode 100644 index 000000000..d8f28ee61 --- /dev/null +++ b/score/launch_manager/src/daemon/src/process_group_manager/alive_monitor_thread_mock.hpp @@ -0,0 +1,41 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#ifndef SCORE_LCM_IALIVE_MONITOR_THREAD_MOCK_HPP_INCLUDED +#define SCORE_LCM_IALIVE_MONITOR_THREAD_MOCK_HPP_INCLUDED + +#include "score/mw/launch_manager/process_group_manager/ialive_monitor_thread.hpp" + +#include + +namespace score +{ +namespace lcm +{ +namespace internal +{ + +/// @brief Reusable gmock mock for IAliveMonitorThread, for use by tests of components that own an alive monitor +/// thread. +class MockAliveMonitorThread : public IAliveMonitorThread +{ + public: + MOCK_METHOD(bool, start, (), (override)); + MOCK_METHOD(void, stop, (), (override)); +}; + +} // namespace internal +} // namespace lcm +} // namespace score + +#endif // SCORE_LCM_IALIVE_MONITOR_THREAD_MOCK_HPP_INCLUDED diff --git a/score/launch_manager/src/daemon/src/process_group_manager/details/BUILD b/score/launch_manager/src/daemon/src/process_group_manager/details/BUILD index 2bc35491f..99c10ffbd 100644 --- a/score/launch_manager/src/daemon/src/process_group_manager/details/BUILD +++ b/score/launch_manager/src/daemon/src/process_group_manager/details/BUILD @@ -10,7 +10,7 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -load("@rules_cc//cc:defs.bzl", "cc_library") +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") cc_library( name = "process_info_node", @@ -44,7 +44,10 @@ cc_library( hdrs = ["graph.hpp"], include_prefix = "score/mw/launch_manager/process_group_manager/details", strip_include_prefix = "/score/launch_manager/src/daemon/src/process_group_manager/details", - visibility = ["//score/launch_manager/src/daemon/src/process_group_manager:__pkg__"], + visibility = [ + "//score/launch_manager/src/daemon/src/process_group_manager:__pkg__", + "//score/launch_manager/src/daemon/src/process_group_manager/details:__pkg__", + ], deps = [ ":process_info_node", ":safe_process_map", @@ -103,6 +106,26 @@ cc_test( ], ) +cc_test( + name = "process_group_manager_UT", + timeout = "long", + srcs = ["process_group_manager_UT.cpp"], + defines = select({ + "//config:lm_use_new_configuration": ["USE_NEW_CONFIGURATION"], + "//conditions:default": [], + }), + deps = [ + ":process_group_manager_impl", + "//score/launch_manager/src/daemon/src/configuration:config", + "//score/launch_manager/src/daemon/src/process_group_manager:ialive_monitor_thread_mock", + "//score/launch_manager/src/daemon/src/process_group_manager:process_group_manager_hdrs", + "//score/launch_manager/src/daemon/src/process_state_client:iprocess_state_notifier_mock", + "//score/launch_manager/src/daemon/src/recovery_client:recovery_client_mock", + "//score/launch_manager/src/daemon/src/watchdog:i_watchdog_if_mock", + "@googletest//:gtest_main", + ], +) + cc_library( name = "process_launcher", srcs = ["process_launcher.cpp"], diff --git a/score/launch_manager/src/daemon/src/process_group_manager/details/process_group_manager_UT.cpp b/score/launch_manager/src/daemon/src/process_group_manager/details/process_group_manager_UT.cpp new file mode 100644 index 000000000..6793ea8ef --- /dev/null +++ b/score/launch_manager/src/daemon/src/process_group_manager/details/process_group_manager_UT.cpp @@ -0,0 +1,248 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/mw/launch_manager/process_group_manager/process_group_manager.hpp" + +#include "score/mw/launch_manager/process_group_manager/alive_monitor_thread_mock.hpp" +#include "score/mw/launch_manager/process_state_client/iprocess_state_notifier_mock.hpp" +#include "score/mw/launch_manager/recovery_client/irecovery_client_mock.h" +#include "score/mw/launch_manager/watchdog/IWatchdogIfMock.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef USE_NEW_CONFIGURATION +#include "score/mw/launch_manager/configuration/config.hpp" +#endif + +using namespace testing; + +namespace score::lcm::internal +{ +namespace +{ + +using score::lcm::MockProcessStateNotifier; +using score::lcm::MockRecoveryClient; +using score::lcm::watchdog::MockWatchdogIf; + +#ifdef USE_NEW_CONFIGURATION +using score::mw::launch_manager::configuration::AliveSupervisionConfig; +using score::mw::launch_manager::configuration::ApplicationType; +using score::mw::launch_manager::configuration::ComponentConfig; +using score::mw::launch_manager::configuration::Config; +using score::mw::launch_manager::configuration::ConfigBuilder; +using score::mw::launch_manager::configuration::FallbackRunTargetConfig; +using score::mw::launch_manager::configuration::ProcessState; +using score::mw::launch_manager::configuration::ReadyCondition; +using score::mw::launch_manager::configuration::RunTargetConfig; +using score::mw::launch_manager::configuration::WatchdogConfig; + +Config makeMinimalConfig() +{ + ComponentConfig component; + component.name = "comp_a"; + component.description = "Component A"; + component.component_properties.binary_name = "true"; + component.component_properties.application_profile.application_type = ApplicationType::Native; + component.component_properties.application_profile.is_self_terminating = true; + component.component_properties.ready_condition = ReadyCondition{ProcessState::Running}; + component.deployment_config.ready_timeout_ms = 500U; + component.deployment_config.shutdown_timeout_ms = 500U; + component.deployment_config.bin_dir = "/bin"; + component.deployment_config.working_dir = "/workspaces/lifecycle"; + component.deployment_config.sandbox.uid = 1000U; + component.deployment_config.sandbox.gid = 1000U; + component.deployment_config.sandbox.scheduling_policy = SCHED_OTHER; + component.deployment_config.sandbox.scheduling_priority = 0; + + RunTargetConfig startup; + startup.name = "Startup"; + startup.description = "Initial run target"; + startup.depends_on = {"comp_a"}; + startup.transition_timeout_ms = 5000U; + startup.recovery_action.run_target = "fallback_run_target"; + + FallbackRunTargetConfig fallback; + fallback.description = "Safe state"; + fallback.transition_timeout_ms = 1500U; + + AliveSupervisionConfig alive; + alive.evaluation_cycle_ms = 500U; + + WatchdogConfig watchdog; + watchdog.device_file_path = "/dev/watchdog0"; + watchdog.max_timeout_ms = 5000U; + watchdog.deactivate_on_shutdown = true; + watchdog.require_magic_close = false; + + std::vector components; + components.push_back(std::move(component)); + + std::vector run_targets; + run_targets.push_back(std::move(startup)); + + return ConfigBuilder{} + .setComponents(std::move(components)) + .setRunTargets(std::move(run_targets)) + .setInitialRunTarget("Startup") + .setFallbackRunTarget(std::move(fallback)) + .setAliveSupervision(alive) + .setWatchdog(watchdog) + .build(); +} + +class ProcessGroupManagerWatchdogTest : public Test +{ + protected: + void SetUp() override + { + RecordProperty("TestType", "unit-test"); + RecordProperty("DerivationTechnique", "explorative-testing"); + + auto alive_monitor_thread = std::make_unique>(); + alive_monitor_thread_ = alive_monitor_thread.get(); + ON_CALL(*alive_monitor_thread_, start()).WillByDefault(Return(true)); + + auto recovery_client = std::make_shared>(); + recovery_client_ = recovery_client.get(); + ON_CALL(*recovery_client_, getNextRequest()).WillByDefault(Return(std::nullopt)); + ON_CALL(*recovery_client_, hasOverflow()).WillByDefault(Return(false)); + ON_CALL(*recovery_client_, sendRecoveryRequest(_)).WillByDefault(Return(true)); + + auto process_state_notifier = std::make_unique>(); + process_state_notifier_ = process_state_notifier.get(); + ON_CALL(*process_state_notifier_, constructReceiver()) + .WillByDefault(Return(ByMove(std::unique_ptr{}))); + ON_CALL(*process_state_notifier_, queuePosixProcess(_)).WillByDefault(Return(true)); + + auto watchdog = std::make_unique>(); + watchdog_ = watchdog.get(); + + process_group_manager_ = std::make_unique(std::move(alive_monitor_thread), + std::move(recovery_client), + std::move(process_state_notifier), + std::move(watchdog)); + } + + void TearDown() override + { + process_group_manager_->deinitialize(); + } + + MockAliveMonitorThread* alive_monitor_thread_{}; + MockRecoveryClient* recovery_client_{}; + MockProcessStateNotifier* process_state_notifier_{}; + MockWatchdogIf* watchdog_{}; + std::unique_ptr process_group_manager_; +}; + +TEST_F(ProcessGroupManagerWatchdogTest, GivenMinimalConfig_ExpectWatchdogMethodsCalledInSequence_WhenInitializeCalled) +{ + // Given + const auto config = makeMinimalConfig(); + + // Expected + InSequence sequence; + EXPECT_CALL(*alive_monitor_thread_, start()).WillOnce(Return(true)); + EXPECT_CALL(*watchdog_, init(_, _)).WillOnce(Return(true)); + EXPECT_CALL(*watchdog_, enable()).WillOnce(Return(true)); + EXPECT_CALL(*watchdog_, disable()).Times(1); + EXPECT_CALL(*alive_monitor_thread_, stop()).Times(1); + + // When + auto initialize_result = process_group_manager_->initialize(config); + + // Then + EXPECT_TRUE(initialize_result); +} + +TEST_F(ProcessGroupManagerWatchdogTest, GivenMinimalConfig_ExpectWatchdogServicePerCycle_WhenRunCalled) +{ + // Given + const auto config = makeMinimalConfig(); + + // Expected + EXPECT_CALL(*alive_monitor_thread_, start()).WillOnce(Return(true)); + EXPECT_CALL(*watchdog_, init(_, _)).WillOnce(Return(true)); + EXPECT_CALL(*watchdog_, enable()).WillOnce(Return(true)); + // Call cancel() to exit the run() loop after at least one cycle of serviceWatchdog() is called + EXPECT_CALL(*watchdog_, serviceWatchdog()).Times(AtLeast(1)).WillRepeatedly([this]() { + process_group_manager_->cancel(); + }); + // Called in deinitialize() after run() returns + EXPECT_CALL(*watchdog_, disable()).Times(1); + EXPECT_CALL(*alive_monitor_thread_, stop()).Times(1); + + // When + ASSERT_TRUE(process_group_manager_->initialize(config)); + auto run_result = process_group_manager_->run(); + + // Then + EXPECT_TRUE(run_result); +} + +TEST_F(ProcessGroupManagerWatchdogTest, GivenMinimalConfig_ExpectWatchdogFired_WhenRecoveryClientOverflowsDuringRunCall) +{ + // Given + const auto config = makeMinimalConfig(); + + // Expected + EXPECT_CALL(*alive_monitor_thread_, start()).WillOnce(Return(true)); + EXPECT_CALL(*watchdog_, init(_, _)).WillOnce(Return(true)); + EXPECT_CALL(*watchdog_, enable()).WillOnce(Return(true)); + ON_CALL(*recovery_client_, hasOverflow()).WillByDefault(Return(true)); + EXPECT_CALL(*watchdog_, fireWatchdogReaction()).Times(AtLeast(1)); + EXPECT_CALL(*watchdog_, serviceWatchdog()).Times(AtLeast(1)).WillRepeatedly([this]() { + process_group_manager_->cancel(); + }); + EXPECT_CALL(*watchdog_, disable()).Times(1); + EXPECT_CALL(*alive_monitor_thread_, stop()).Times(1); + + // When + ASSERT_TRUE(process_group_manager_->initialize(config)); + auto run_result = process_group_manager_->run(); + + // Then + EXPECT_TRUE(run_result); +} + +TEST_F(ProcessGroupManagerWatchdogTest, GivenMinimalConfig_ExpectWatchdogDisabled_WhenDeinitializeCalled) +{ + // Given + const auto config = makeMinimalConfig(); + + // Expected + EXPECT_CALL(*alive_monitor_thread_, start()).WillOnce(Return(true)); + EXPECT_CALL(*watchdog_, init(_, _)).WillOnce(Return(true)); + EXPECT_CALL(*watchdog_, enable()).WillOnce(Return(true)); + EXPECT_CALL(*watchdog_, disable()).Times(1); + EXPECT_CALL(*alive_monitor_thread_, stop()).Times(1); + + // When + auto initialize_result = process_group_manager_->initialize(config); + + // Then + EXPECT_TRUE(initialize_result); +} +#endif + +} // namespace +} // namespace score::lcm::internal diff --git a/score/launch_manager/src/daemon/src/process_state_client/BUILD b/score/launch_manager/src/daemon/src/process_state_client/BUILD index 368838fd6..2230829d0 100644 --- a/score/launch_manager/src/daemon/src/process_state_client/BUILD +++ b/score/launch_manager/src/daemon/src/process_state_client/BUILD @@ -48,6 +48,19 @@ cc_library( ], ) +cc_library( + name = "iprocess_state_notifier_mock", + testonly = True, + hdrs = ["iprocess_state_notifier_mock.hpp"], + include_prefix = "score/mw/launch_manager/process_state_client", + strip_include_prefix = "/score/launch_manager/src/daemon/src/process_state_client", + visibility = ["//score:__subpackages__"], + deps = [ + ":iprocess_state_notifier", + "@googletest//:gtest", + ], +) + cc_library( name = "process_state_notifier", srcs = ["process_state_notifier.cpp"], diff --git a/score/launch_manager/src/daemon/src/process_state_client/iprocess_state_notifier_mock.hpp b/score/launch_manager/src/daemon/src/process_state_client/iprocess_state_notifier_mock.hpp new file mode 100644 index 000000000..52d8d7745 --- /dev/null +++ b/score/launch_manager/src/daemon/src/process_state_client/iprocess_state_notifier_mock.hpp @@ -0,0 +1,40 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#ifndef IPROCESSSTATE_NOTIFIER_MOCK_HPP_INCLUDED +#define IPROCESSSTATE_NOTIFIER_MOCK_HPP_INCLUDED + +#include "score/mw/launch_manager/process_state_client/iprocess_state_notifier.hpp" + +#include + +#include + +namespace score +{ +namespace lcm +{ + +/// @brief Reusable gmock mock for IProcessStateNotifier, for use by tests of components that notify PHM of process +/// state changes. +class MockProcessStateNotifier : public IProcessStateNotifier +{ + public: + MOCK_METHOD(std::unique_ptr, constructReceiver, (), (override)); + MOCK_METHOD(bool, queuePosixProcess, (const score::lcm::PosixProcess& f_posixProcess), (noexcept, override)); +}; + +} // namespace lcm +} // namespace score + +#endif // IPROCESSSTATE_NOTIFIER_MOCK_HPP_INCLUDED diff --git a/score/launch_manager/src/daemon/src/recovery_client/BUILD b/score/launch_manager/src/daemon/src/recovery_client/BUILD index 0944da271..7d6d0b0c6 100644 --- a/score/launch_manager/src/daemon/src/recovery_client/BUILD +++ b/score/launch_manager/src/daemon/src/recovery_client/BUILD @@ -32,6 +32,19 @@ cc_library( ], ) +cc_library( + name = "recovery_client_mock", + testonly = True, + hdrs = ["irecovery_client_mock.h"], + include_prefix = "score/mw/launch_manager/recovery_client", + strip_include_prefix = "/score/launch_manager/src/daemon/src/recovery_client", + visibility = ["//score:__subpackages__"], + deps = [ + ":recovery_client", + "@googletest//:gtest", + ], +) + cc_test( name = "recovery_client_UT", srcs = ["recovery_client_UT.cpp"], diff --git a/score/launch_manager/src/daemon/src/recovery_client/irecovery_client_mock.h b/score/launch_manager/src/daemon/src/recovery_client/irecovery_client_mock.h new file mode 100644 index 000000000..bfefdf451 --- /dev/null +++ b/score/launch_manager/src/daemon/src/recovery_client/irecovery_client_mock.h @@ -0,0 +1,41 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#ifndef SCORE_LCM_IRECOVERYCLIENT_MOCK_H_ +#define SCORE_LCM_IRECOVERYCLIENT_MOCK_H_ + +#include "score/mw/launch_manager/recovery_client/irecovery_client.h" + +#include + +namespace score +{ +namespace lcm +{ + +/// @brief Reusable gmock mock for IRecoveryClient, for use by tests of components that either request recovery +/// (e.g. Alive Monitor) or process recovery requests (e.g. ProcessGroupManager). +class MockRecoveryClient : public IRecoveryClient +{ + public: + MOCK_METHOD(bool, + sendRecoveryRequest, + (const score::lcm::IdentifierHash& process_group_identifier), + (noexcept, override)); + MOCK_METHOD(std::optional, getNextRequest, (), (noexcept, override)); + MOCK_METHOD(bool, hasOverflow, (), (const, noexcept, override)); +}; + +} // namespace lcm +} // namespace score + +#endif // SCORE_LCM_IRECOVERYCLIENT_MOCK_H_ diff --git a/score/launch_manager/src/daemon/src/watchdog/BUILD b/score/launch_manager/src/daemon/src/watchdog/BUILD index 3b54e7b1e..6eb966889 100644 --- a/score/launch_manager/src/daemon/src/watchdog/BUILD +++ b/score/launch_manager/src/daemon/src/watchdog/BUILD @@ -31,3 +31,26 @@ cc_library( "//score/launch_manager/src/daemon/src/common:constants", ], ) + +cc_library( + name = "i_watchdog_if_mock", + testonly = True, + hdrs = ["IWatchdogIfMock.hpp"], + include_prefix = "score/mw/launch_manager/watchdog", + strip_include_prefix = "/score/launch_manager/src/daemon/src/watchdog", + visibility = ["//score:__subpackages__"], + deps = [ + ":i_watchdog_if", + "@googletest//:gtest", + ], +) + +cc_library( + name = "device_config_factory_stub", + testonly = True, + hdrs = ["DeviceConfigFactoryStub.hpp"], + include_prefix = "score/mw/launch_manager/watchdog", + strip_include_prefix = "/score/launch_manager/src/daemon/src/watchdog", + visibility = ["//score:__subpackages__"], + deps = [":i_device_config_factory"], +) diff --git a/score/launch_manager/src/daemon/src/watchdog/DeviceConfigFactoryStub.hpp b/score/launch_manager/src/daemon/src/watchdog/DeviceConfigFactoryStub.hpp new file mode 100644 index 000000000..75a27de0f --- /dev/null +++ b/score/launch_manager/src/daemon/src/watchdog/DeviceConfigFactoryStub.hpp @@ -0,0 +1,43 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#ifndef DEVICECONFIGFACTORYSTUB_HPP_INCLUDED +#define DEVICECONFIGFACTORYSTUB_HPP_INCLUDED + +#include "score/mw/launch_manager/watchdog/IDeviceConfigFactory.hpp" + +namespace score +{ +namespace lcm +{ +namespace watchdog +{ + +/// @brief Trivial stub for IDeviceConfigFactory, for use by tests that need to inject a valid, non-null factory +/// (e.g. into IWatchdogIf::init()) without asserting on it. +class DeviceConfigFactoryStub final : public IDeviceConfigFactory +{ + public: + DeviceConfigFactoryStub() = default; + + std::optional getDeviceConfigurations() const override + { + return DeviceConfigurations{}; + } +}; + +} // namespace watchdog +} // namespace lcm +} // namespace score + +#endif // DEVICECONFIGFACTORYSTUB_HPP_INCLUDED diff --git a/score/launch_manager/src/daemon/src/watchdog/IWatchdogIfMock.hpp b/score/launch_manager/src/daemon/src/watchdog/IWatchdogIfMock.hpp new file mode 100644 index 000000000..1a4c246a2 --- /dev/null +++ b/score/launch_manager/src/daemon/src/watchdog/IWatchdogIfMock.hpp @@ -0,0 +1,49 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#ifndef IWATCHDOGIFMOCK_HPP_INCLUDED +#define IWATCHDOGIFMOCK_HPP_INCLUDED + +#include "score/mw/launch_manager/watchdog/IWatchdogIf.hpp" + +#include + +namespace score +{ +namespace lcm +{ +namespace watchdog +{ + +/// @brief Reusable gmock mock for IWatchdogIf, for use by tests of components that service the watchdog. +class MockWatchdogIf : public IWatchdogIf +{ + public: + MockWatchdogIf() = default; + + MOCK_METHOD(bool, + init, + (const score::mw::launch_manager::configuration::WatchdogConfig& watchdog_config, + std::int64_t cycle_time_ns), + (noexcept, override)); + MOCK_METHOD(bool, enable, (), (noexcept, override)); + MOCK_METHOD(void, disable, (), (noexcept, override)); + MOCK_METHOD(void, serviceWatchdog, (), (noexcept, override)); + MOCK_METHOD(void, fireWatchdogReaction, (), (noexcept, override)); +}; + +} // namespace watchdog +} // namespace lcm +} // namespace score + +#endif // IWATCHDOGIFMOCK_HPP_INCLUDED