From eb2857a3208a725e44de7428eb70cd7d865de952 Mon Sep 17 00:00:00 2001 From: Lorant Lipcsei Date: Fri, 22 May 2026 02:45:10 +0100 Subject: [PATCH 01/20] AZB 97324 Source files for tests 183 and 184 --- .../recovery_action_complex_rep_failure/BUILD | 88 +++++++ .../control_client_mock.cpp | 60 +++++ .../recovery_action_complex_rep_failure.json | 156 +++++++++++++ .../recovery_action_complex_rep_failure.py | 53 +++++ .../recovery_action_initial.cpp | 47 ++++ .../recovery_action_reporting.cpp | 214 ++++++++++++++++++ .../recovery_action_simple_rep_failure/BUILD | 88 +++++++ .../control_client_mock.cpp | 62 +++++ .../recovery_action_initial.cpp | 47 ++++ .../recovery_action_reporting.cpp | 48 ++++ .../recovery_action_simple_rep_failure.json | 156 +++++++++++++ .../recovery_action_simple_rep_failure.py | 53 +++++ 12 files changed, 1072 insertions(+) create mode 100644 tests/integration/recovery_action_complex_rep_failure/BUILD create mode 100644 tests/integration/recovery_action_complex_rep_failure/control_client_mock.cpp create mode 100644 tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.json create mode 100644 tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.py create mode 100644 tests/integration/recovery_action_complex_rep_failure/recovery_action_initial.cpp create mode 100644 tests/integration/recovery_action_complex_rep_failure/recovery_action_reporting.cpp create mode 100644 tests/integration/recovery_action_simple_rep_failure/BUILD create mode 100644 tests/integration/recovery_action_simple_rep_failure/control_client_mock.cpp create mode 100644 tests/integration/recovery_action_simple_rep_failure/recovery_action_initial.cpp create mode 100644 tests/integration/recovery_action_simple_rep_failure/recovery_action_reporting.cpp create mode 100644 tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.json create mode 100644 tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.py diff --git a/tests/integration/recovery_action_complex_rep_failure/BUILD b/tests/integration/recovery_action_complex_rep_failure/BUILD new file mode 100644 index 000000000..e39320ad0 --- /dev/null +++ b/tests/integration/recovery_action_complex_rep_failure/BUILD @@ -0,0 +1,88 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +load("@rules_pkg//pkg:mappings.bzl", "pkg_attributes", "pkg_files") +load("@rules_pkg//pkg:tar.bzl", "pkg_tar") +load("//:defs.bzl", "launch_manager_config") +load("//tests/utils/bazel:integration.bzl", "integration_test") + +launch_manager_config( + name = "lm_recovery_action_complex_rep_failure_config", + config = "//tests/integration/recovery_action_complex_rep_failure:recovery_action_complex_rep_failure.json", + flatbuffer_out_dir = "etc", +) + +cc_binary( + name = "control_client_mock", + srcs = ["control_client_mock.cpp"], + deps = [ + "//src/control_client_lib", + "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", + "//tests/utils/test_helper", + "@googletest//:gtest_main", + ], +) + +cc_binary( + name = "recovery_action_initial", + srcs = ["recovery_action_initial.cpp"], + deps = [ + "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", + "//tests/utils/test_helper", + "@googletest//:gtest_main", + ], +) + +cc_binary( + name = "recovery_action_reporting", + srcs = ["recovery_action_reporting.cpp"], + deps = [ + "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", + "//src/lifecycle_client_lib", + "//tests/utils/test_helper", + "@googletest//:gtest_main", + ], +) + +pkg_files( + name = "recovery_action_complex_rep_failure_main_files", + srcs = [ + ":recovery_action_initial", + ":recovery_action_reporting", + ":control_client_mock", + "//src/launch_manager_daemon:launch_manager", + ], + attributes = pkg_attributes(mode = "0755"), + prefix = "tests/recovery_action_complex_rep_failure", +) + +pkg_files( + name = "recovery_action_complex_rep_failure_etc_files", + srcs = [":lm_recovery_action_complex_rep_failure_config"], + prefix = "tests/recovery_action_complex_rep_failure", +) + +pkg_tar( + name = "recovery_action_complex_rep_failure_binaries", + srcs = [ + ":recovery_action_complex_rep_failure_etc_files", + ":recovery_action_complex_rep_failure_main_files", + ], +) + +integration_test( + name = "recovery_action_complex_rep_failure", + srcs = ["recovery_action_complex_rep_failure.py"], + tags = ["integration"], + test_binaries = ":recovery_action_complex_rep_failure_binaries", + deps = ["//tests/utils/testing_utils"], +) diff --git a/tests/integration/recovery_action_complex_rep_failure/control_client_mock.cpp b/tests/integration/recovery_action_complex_rep_failure/control_client_mock.cpp new file mode 100644 index 000000000..71ebcfc0c --- /dev/null +++ b/tests/integration/recovery_action_complex_rep_failure/control_client_mock.cpp @@ -0,0 +1,60 @@ +/******************************************************************************** + * 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 + +#include "tests/utils/test_helper/test_helper.hpp" +#include +#include +#include + +// Given a correct configuration with: +// - An initial Run Target named "Startup" containing 2 components named "component_initial" and "control_client_mock" +// - A Run Target named "run_target_app_does_report_krunning_in_time" containing "control_client_mock" and "component_does_report_krunning_in_time" +// - A Run Target named "run_target_app_does_not_report_krunning_in_time" containing "control_client_mock" and "component_does_not_report_krunning_in_time" + +TEST(RecoveryActionComplexRepFailure, ControlClientMock) +{ + score::lcm::ControlClient client; + + // Establish communication with launch manager + TEST_STEP("Report kRunning from ControlClientMock") + { + auto result = score::lcm::LifecycleClient{}.ReportExecutionState(score::lcm::ExecutionState::kRunning); + ASSERT_TRUE(result.has_value()) << "ReportExecutionState() failed: " << result.error().Message(); + } + // Start the run target run_target_app_does_report_krunning_in_time + TEST_STEP("Activate RunTarget run_target_app_does_report_krunning_in_time") + { + score::cpp::stop_token stop_token; + auto result = client.ActivateRunTarget("run_target_app_does_report_krunning_in_time").Get(stop_token); + EXPECT_TRUE(result.has_value()) << "Activating target run_target_app_does_report_krunning_in_time failed: " << result.error().Message(); + } + sleep(2); + // Start the run target run_target_app_does_report_krunning_in_time + TEST_STEP("Activate RunTarget run_target_app_does_not_report_krunning_in_time") + { + score::cpp::stop_token stop_token; + auto result = client.ActivateRunTarget("run_target_app_does_not_report_krunning_in_time").Get(stop_token); + EXPECT_FALSE(result.has_value()) << "Activating target run_target_app_does_not_report_krunning_in_time failed: " << result.error().Message(); + } + + TEST_STEP("Activate RunTarget Off") + { + client.ActivateRunTarget("Off"); + } +} + +int main(int argc, char** argv) +{ + return TestRunner(__FILE__, true, true).RunTests(); +} diff --git a/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.json b/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.json new file mode 100644 index 000000000..f5191d777 --- /dev/null +++ b/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.json @@ -0,0 +1,156 @@ +{ + "schema_version": 1, + "defaults": { + "deployment_config": { + "bin_dir": "/tmp/tests/recovery_action_complex_rep_failure", + "ready_timeout": 1.0, + "shutdown_timeout": 1.0, + "ready_recovery_action": { + "restart": { + "number_of_attempts": 0 + } + }, + "recovery_action": { + "switch_run_target": { + "run_target": "fallback_run_target" + } + }, + "environmental_variables": { + "LD_LIBRARY_PATH": "/opt/lib" + }, + "sandbox": { + "uid": 0, + "gid": 0, + "scheduling_policy": "SCHED_OTHER", + "scheduling_priority": 0 + } + }, + "component_properties": { + "application_profile": { + "application_type": "Reporting", + "is_self_terminating": true, + "alive_supervision": { + "reporting_cycle": 0.1, + "min_indications": 1, + "max_indications": 3, + "failed_cycles_tolerance": 1 + } + }, + "ready_condition": { + "process_state": "Running" + } + } + }, + "components": { + "component_initial": { + "component_properties": { + "binary_name": "recovery_action_initial", + "process_arguments": [ + "S-CORE rules!" + ] + }, + "deployment_config": { + "ready_timeout": 1.0, + "shutdown_timeout": 1.0, + "environmental_variables": { + "PROCESSIDENTIFIER": "component_initial" + } + } + }, + "control_client_mock": { + "component_properties": { + "binary_name": "control_client_mock", + "application_profile": { + "application_type": "State_Manager", + "alive_supervision": { + "min_indications": 0 + } + } + }, + "deployment_config": { + "ready_timeout": 1.0, + "shutdown_timeout": 1.0, + "environmental_variables": { + "PROCESSIDENTIFIER": "control_client_mock" + } + } + }, + "component_does_report_krunning_in_time": { + "component_properties": { + "binary_name": "recovery_action_reporting", + "process_arguments": [ + "500.0" + ] + }, + "deployment_config": { + "ready_timeout": 1.0, + "shutdown_timeout": 1.0, + "environmental_variables": { + "PROCESSIDENTIFIER": "component_does_report_krunning_in_time" + } + } + }, + "component_does_not_report_krunning_in_time": { + "component_properties": { + "binary_name": "recovery_action_reporting", + "process_arguments": [ + "1500.0" + ] + }, + "deployment_config": { + "ready_timeout": 1.0, + "shutdown_timeout": 1.0, + "environmental_variables": { + "PROCESSIDENTIFIER": "component_does_not_report_krunning_in_time" + } + } + } + }, + "run_targets": { + "Startup": { + "depends_on": [ + "component_initial", + "control_client_mock" + ], + "recovery_action": { + "switch_run_target": { + "run_target": "fallback_run_target" + } + } + }, + "run_target_app_does_report_krunning_in_time": { + "depends_on": [ + "control_client_mock", + "component_does_report_krunning_in_time" + ], + "recovery_action": { + "switch_run_target": { + "run_target": "fallback_run_target" + } + } + }, + "run_target_app_does_not_report_krunning_in_time": { + "depends_on": [ + "control_client_mock", + "component_does_not_report_krunning_in_time" + ], + "recovery_action": { + "switch_run_target": { + "run_target": "fallback_run_target" + } + } + }, + "Off": { + "depends_on": [] + } + }, + "initial_run_target": "Startup", + "alive_supervision": { + "evaluation_cycle": 0.05 + }, + "fallback_run_target": { + "depends_on": [ + "control_client_mock" + ] + } +} diff --git a/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.py b/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.py new file mode 100644 index 000000000..b27351045 --- /dev/null +++ b/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.py @@ -0,0 +1,53 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +from tests.utils.testing_utils.run_until_file_deployed import run_until_file_deployed +from tests.utils.testing_utils.setup_test import setup_test +from tests.utils.testing_utils.test_results import ( + check_for_failures, + download_xml_results, +) +from attribute_plugin import add_test_properties + + +@add_test_properties( + partially_verifies=[ + "feat_req__lifecycle__controlif_status", + "feat_req__lifecycle__process_failure_react", + "feat_req__lifecycle__configurable_wait_time", + ], + test_type="requirements-based", + derivation_technique="requirements-analysis", +) +def test_recovery_action_complex_rep_failure(target, setup_test, test_output_dir, remote_test_dir): + """ + Objective: Verifies that recovery action is executed when the complex reporting of kRunning is not successful. + + Case 1: Using complex reporting, the process does report kRunning in time (500ms below boundary) + Expected Behaviour: Reporting kRunning is successful, recovery action is not executed. + + Case 2: Using complex reporting, the process does not report kRunning in time (500ms above boundary) + Expected Behaviour: Reporting kRunning is not successful, recovery action is executed. + """ + + run_until_file_deployed( + target=target, + binary_path=str(remote_test_dir / "launch_manager"), + file_path=remote_test_dir.parent / "test_end", + cwd=str(remote_test_dir), + timeout_s=10.0, + ) + + download_xml_results(target, remote_test_dir, test_output_dir) + all_files, failing_files = check_for_failures(test_output_dir) + assert len(all_files) == 3, f"Didn't find the expected number of files {all_files}" + assert len(failing_files) == 0, f"Found failures in files {failing_files}" diff --git a/tests/integration/recovery_action_complex_rep_failure/recovery_action_initial.cpp b/tests/integration/recovery_action_complex_rep_failure/recovery_action_initial.cpp new file mode 100644 index 000000000..b51fe2478 --- /dev/null +++ b/tests/integration/recovery_action_complex_rep_failure/recovery_action_initial.cpp @@ -0,0 +1,47 @@ +/******************************************************************************** + * 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 + +#include "tests/utils/test_helper/test_helper.hpp" +#include + +int g_argc; +char** g_argv; + +// Given a correct configuration with: +// - An initial Run Target named "Startup" +// - Startup contains 2 components named "component_initial" and "control_client_mock" +// - component_initial has command line parameter "S-CORE rules!" +TEST(RecoveryActionComplexRepFailure, RecoveryActionInitial) +{ + // Establish communication with launch manager + TEST_STEP("Report kRunning from RecoveryActionInitial") + { + auto result = score::lcm::LifecycleClient{}.ReportExecutionState(score::lcm::ExecutionState::kRunning); + EXPECT_TRUE(result.has_value()) << "ReportExecutionState() failed: " << result.error().Message(); + } + // Check arguments + TEST_STEP("Check args") + { + ASSERT_GT(g_argc, 1) << "Wrong number of arguments"; + EXPECT_STREQ(g_argv[1], "S-CORE rules!") << "Argument 2 was incorrect"; + } +} + +int main(int argc, char** argv) +{ + g_argc = argc; + g_argv = argv; + + return TestRunner(__FILE__, false).RunTests(); +} diff --git a/tests/integration/recovery_action_complex_rep_failure/recovery_action_reporting.cpp b/tests/integration/recovery_action_complex_rep_failure/recovery_action_reporting.cpp new file mode 100644 index 000000000..0e0f8563d --- /dev/null +++ b/tests/integration/recovery_action_complex_rep_failure/recovery_action_reporting.cpp @@ -0,0 +1,214 @@ +/******************************************************************************** + * 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 +#include + +#include + +#include "tests/utils/test_helper/test_helper.hpp" +#include +#include "src/lifecycle_client_lib/include/application.h" +#include "src/lifecycle_client_lib/include/runapplication.h" + +int g_argc; +char** g_argv; + +/// @brief CLI configuration options for the not_supervised_application process +struct Config +{ + std::int32_t responseTimeInMs{100}; + bool crashRequested{false}; + std::int32_t crashTimeInMs{1000}; + bool failToStart{false}; + bool verbose{false}; +}; + +std::string helpSstring = + "Usage:\n\ + -r Worst case response time to SIGTERM signal in milliseconds.\n\ + -c Simulate crash of the application, after specified time in milliseconds.\n\ + -s Simulate failure during start-up of the application.\n\ + -v Run in verbose mode.\n"; + +std::optional parseOptions(int argc, char *const *argv) noexcept +{ + Config config{}; + int c; + while ((c = getopt(argc, argv, ":r:c:svh")) != -1) + { + switch (static_cast(c)) + { + case 'r': + config.responseTimeInMs = std::stoi(optarg); + break; + + case 'c': + config.crashRequested = true; + config.crashTimeInMs = std::stoi(optarg); + break; + + case 's': + config.failToStart = true; + break; + + case 'h': + std::cout << helpSstring; + return std::nullopt; + + case 'v': + config.verbose = true; + break; + + case '?': + std::cout << "Unrecognized option: -" << static_cast(optopt) << std::endl; + std::cout << helpSstring; + return std::nullopt; + + default: + break; + } + } + return config; +} + +void set_process_name() +{ + const char* identifier = getenv("PROCESSIDENTIFIER"); +} + +class LifecycleApp final : public score::mw::lifecycle::Application +{ +public: + std::int32_t Initialize(const score::mw::lifecycle::ApplicationContext& appCtx) override + { + set_process_name(); + + // Build a classic argv for getopt() from ApplicationContext arguments + const auto& args = appCtx.get_arguments(); + m_argvStorage.clear(); + m_argvStorage.reserve(args.size() + 2); + + // Ensure argv[0] exists (getopt expects it) + if (args.empty()) + { + m_argvStorage.push_back(const_cast("LifecycleApp")); + } + else + { + for (const auto& s : args) + { + // NOTE: relies on the underlying storage staying alive during Initialize(). + m_argvStorage.push_back(const_cast(s.data())); + } + } + + m_argvStorage.push_back(nullptr); + + optind = 1; + + const int argcLocal = static_cast(m_argvStorage.size() - 1); + const auto config = parseOptions(argcLocal, m_argvStorage.data()); + if (!config) + { + return EXIT_FAILURE; + } + + m_config = *config; + + if (true == m_config.failToStart) + { + return EXIT_FAILURE; + } + + return 0; + } + + std::int32_t Run(const score::cpp::stop_token& stopToken) override + { + std::chrono::time_point startTime = std::chrono::steady_clock::now(); + std::chrono::duration runTime; + + timespec req{ + static_cast(m_config.responseTimeInMs / 1000), + static_cast((m_config.responseTimeInMs % 1000) * 1000000L) + }; + + auto timeLastVerboseLog = std::chrono::steady_clock::now(); + + while (!stopToken.stop_requested()) + { + if (true == m_config.crashRequested) + { + runTime = std::chrono::steady_clock::now() - startTime; + int timeTillCrash = static_cast(m_config.crashTimeInMs - runTime.count()); + + if (timeTillCrash < m_config.responseTimeInMs) + { + if (timeTillCrash > 0) + { + timespec crash_req{ + static_cast(timeTillCrash / 1000), + static_cast((timeTillCrash % 1000) * 1000000L) + }; + nanosleep(&crash_req, nullptr); + } + + std::abort(); + } + } + + if (m_config.verbose) + { + const auto now = std::chrono::steady_clock::now(); + if (now - timeLastVerboseLog >= std::chrono::seconds(1)) + { + std::cout << "LifecycleApp: Running in verbose mode" << std::endl; + timeLastVerboseLog = now; + } + } + + nanosleep(&req, nullptr); + } + + return EXIT_SUCCESS; + } + +private: + Config m_config{}; + std::vector m_argvStorage{}; +}; + +TEST(RecoveryActionComplexRepFailure, RecoveryActionReporting) +{ + // Check arguments + TEST_STEP("Check args") + { + ASSERT_GT(g_argc, 1) << "Wrong number of arguments"; + ASSERT_FALSE((g_argv[1][0] != '0') && (atoi(g_argv[1])) == 0) << "Argument must be a number"; + } + // Report kRunning with the appropriate delay + TEST_STEP("Report kRunning from RecoveryActionReporting") + { + std::this_thread::sleep_for(std::chrono::milliseconds(atoi(g_argv[1]))); + + score::mw::lifecycle::run_application(g_argc, g_argv); + } +} + +int main(int argc, char** argv) +{ + g_argc = argc; + g_argv = argv; + + return TestRunner(__FILE__, false).RunTests(); +} diff --git a/tests/integration/recovery_action_simple_rep_failure/BUILD b/tests/integration/recovery_action_simple_rep_failure/BUILD new file mode 100644 index 000000000..f9e745015 --- /dev/null +++ b/tests/integration/recovery_action_simple_rep_failure/BUILD @@ -0,0 +1,88 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +load("@rules_pkg//pkg:mappings.bzl", "pkg_attributes", "pkg_files") +load("@rules_pkg//pkg:tar.bzl", "pkg_tar") +load("//:defs.bzl", "launch_manager_config") +load("//tests/utils/bazel:integration.bzl", "integration_test") + +launch_manager_config( + name = "lm_recovery_action_simple_rep_failure_config", + config = "//tests/integration/recovery_action_simple_rep_failure:recovery_action_simple_rep_failure.json", + flatbuffer_out_dir = "etc", +) + +cc_binary( + name = "control_client_mock", + srcs = ["control_client_mock.cpp"], + deps = [ + "//src/control_client_lib", + "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", + "//tests/utils/test_helper", + "@googletest//:gtest_main", + ], +) + +cc_binary( + name = "recovery_action_initial", + srcs = ["recovery_action_initial.cpp"], + deps = [ + "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", + "//src/lifecycle_client_lib:lifecyclemanager", + "//tests/utils/test_helper", + "@googletest//:gtest_main", + ], +) + +cc_binary( + name = "recovery_action_reporting", + srcs = ["recovery_action_reporting.cpp"], + deps = [ + "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", + "//tests/utils/test_helper", + "@googletest//:gtest_main", + ], +) + +pkg_files( + name = "recovery_action_simple_rep_failure_main_files", + srcs = [ + ":recovery_action_initial", + ":recovery_action_reporting", + ":control_client_mock", + "//src/launch_manager_daemon:launch_manager", + ], + attributes = pkg_attributes(mode = "0755"), + prefix = "tests/recovery_action_simple_rep_failure", +) + +pkg_files( + name = "recovery_action_simple_rep_failure_etc_files", + srcs = [":lm_recovery_action_simple_rep_failure_config"], + prefix = "tests/recovery_action_simple_rep_failure", +) + +pkg_tar( + name = "recovery_action_simple_rep_failure_binaries", + srcs = [ + ":recovery_action_simple_rep_failure_etc_files", + ":recovery_action_simple_rep_failure_main_files", + ], +) + +integration_test( + name = "recovery_action_simple_rep_failure", + srcs = ["recovery_action_simple_rep_failure.py"], + tags = ["integration"], + test_binaries = ":recovery_action_simple_rep_failure_binaries", + deps = ["//tests/utils/testing_utils"], +) diff --git a/tests/integration/recovery_action_simple_rep_failure/control_client_mock.cpp b/tests/integration/recovery_action_simple_rep_failure/control_client_mock.cpp new file mode 100644 index 000000000..0d332b059 --- /dev/null +++ b/tests/integration/recovery_action_simple_rep_failure/control_client_mock.cpp @@ -0,0 +1,62 @@ +/******************************************************************************** + * 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 + +#include "tests/utils/test_helper/test_helper.hpp" +#include +#include +#include + +// Given a correct configuration with: +// - An initial Run Target named "Startup" containing 2 components named "component_initial" and "control_client_mock" +// - A Run Target named "run_target_app_does_report_krunning_in_time" containing "control_client_mock" and "component_does_report_krunning_in_time" +// - A Run Target named "run_target_app_does_not_report_krunning_in_time" containing "control_client_mock" and "component_does_not_report_krunning_in_time" + +TEST(RecoveryActionSimpleRepFailure, ControlClientMock) +{ + score::lcm::ControlClient client; + + ASSERT_TRUE(check_clean({test_end_location, fallback_file})); + + // Establish communication with launch manager + TEST_STEP("Report kRunning from ControlClientMock") + { + auto result = score::lcm::LifecycleClient{}.ReportExecutionState(score::lcm::ExecutionState::kRunning); + ASSERT_TRUE(result.has_value()) << "ReportExecutionState() failed: " << result.error().Message(); + } + // Start the run target run_target_app_does_report_krunning_in_time + TEST_STEP("Activate RunTarget run_target_app_does_report_krunning_in_time") + { + score::cpp::stop_token stop_token; + auto result = client.ActivateRunTarget("run_target_app_does_report_krunning_in_time").Get(stop_token); + EXPECT_TRUE(result.has_value()) << "Activating target run_target_app_does_report_krunning_in_time failed: " << result.error().Message(); + } + sleep(2); + // Start the run target run_target_app_does_report_krunning_in_time + TEST_STEP("Activate RunTarget run_target_app_does_not_report_krunning_in_time") + { + score::cpp::stop_token stop_token; + auto result = client.ActivateRunTarget("run_target_app_does_not_report_krunning_in_time").Get(stop_token); + EXPECT_FALSE(result.has_value()) << "Activating target run_target_app_does_not_report_krunning_in_time failed: " << result.error().Message(); + } + + TEST_STEP("Activate RunTarget Off") + { + client.ActivateRunTarget("Off"); + } +} + +int main(int argc, char** argv) +{ + return TestRunner(__FILE__, true, true).RunTests(); +} diff --git a/tests/integration/recovery_action_simple_rep_failure/recovery_action_initial.cpp b/tests/integration/recovery_action_simple_rep_failure/recovery_action_initial.cpp new file mode 100644 index 000000000..4c292739b --- /dev/null +++ b/tests/integration/recovery_action_simple_rep_failure/recovery_action_initial.cpp @@ -0,0 +1,47 @@ +/******************************************************************************** + * 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 + +#include "tests/utils/test_helper/test_helper.hpp" +#include + +int g_argc; +char** g_argv; + +// Given a correct configuration with: +// - An initial Run Target named "Startup" +// - Startup contains 2 components named "component_initial" and "control_client_mock" +// - component_initial has command line parameter "S-CORE rules!" +TEST(RecoveryActionSimpleRepFailure, RecoveryActionInitial) +{ + // Establish communication with launch manager + TEST_STEP("Report kRunning from RecoveryActionInitial") + { + auto result = score::lcm::LifecycleClient{}.ReportExecutionState(score::lcm::ExecutionState::kRunning); + EXPECT_TRUE(result.has_value()) << "ReportExecutionState() failed: " << result.error().Message(); + } + // Check arguments + TEST_STEP("Check args") + { + ASSERT_GT(g_argc, 1) << "Wrong number of arguments"; + EXPECT_STREQ(g_argv[1], "S-CORE rules!") << "Argument 2 was incorrect"; + } +} + +int main(int argc, char** argv) +{ + g_argc = argc; + g_argv = argv; + + return TestRunner(__FILE__, false).RunTests(); +} diff --git a/tests/integration/recovery_action_simple_rep_failure/recovery_action_reporting.cpp b/tests/integration/recovery_action_simple_rep_failure/recovery_action_reporting.cpp new file mode 100644 index 000000000..9118b4fda --- /dev/null +++ b/tests/integration/recovery_action_simple_rep_failure/recovery_action_reporting.cpp @@ -0,0 +1,48 @@ +/******************************************************************************** + * 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 +#include + +#include + +#include "tests/utils/test_helper/test_helper.hpp" +#include + +int g_argc; +char** g_argv; + +TEST(RecoveryActionSimpleRepFailure, RecoveryActionReporting) +{ + // Check arguments + TEST_STEP("Check args") + { + ASSERT_GT(g_argc, 1) << "Wrong number of arguments"; + ASSERT_FALSE((g_argv[1][0] != '0') && (atoi(g_argv[1])) == 0) << "Argument must be a number"; + } + // Report kRunning with the appropriate delay + TEST_STEP("Report kRunning from RecoveryActionReporting") + { + std::this_thread::sleep_for(std::chrono::milliseconds(atoi(g_argv[1]))); + + auto result = score::lcm::LifecycleClient{}.ReportExecutionState(score::lcm::ExecutionState::kRunning); + EXPECT_TRUE(result.has_value()) << "ReportExecutionState() failed: " << result.error().Message(); + } +} + +int main(int argc, char** argv) +{ + g_argc = argc; + g_argv = argv; + + return TestRunner(__FILE__, false).RunTests(); +} diff --git a/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.json b/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.json new file mode 100644 index 000000000..23894f26e --- /dev/null +++ b/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.json @@ -0,0 +1,156 @@ +{ + "schema_version": 1, + "defaults": { + "deployment_config": { + "bin_dir": "/tmp/tests/recovery_action_simple_rep_failure", + "ready_timeout": 1.0, + "shutdown_timeout": 1.0, + "ready_recovery_action": { + "restart": { + "number_of_attempts": 0 + } + }, + "recovery_action": { + "switch_run_target": { + "run_target": "fallback_run_target" + } + }, + "environmental_variables": { + "LD_LIBRARY_PATH": "/opt/lib" + }, + "sandbox": { + "uid": 0, + "gid": 0, + "scheduling_policy": "SCHED_OTHER", + "scheduling_priority": 0 + } + }, + "component_properties": { + "application_profile": { + "application_type": "Reporting", + "is_self_terminating": true, + "alive_supervision": { + "reporting_cycle": 0.1, + "min_indications": 1, + "max_indications": 3, + "failed_cycles_tolerance": 1 + } + }, + "ready_condition": { + "process_state": "Running" + } + } + }, + "components": { + "component_initial": { + "component_properties": { + "binary_name": "recovery_action_initial", + "process_arguments": [ + "S-CORE rules!" + ] + }, + "deployment_config": { + "ready_timeout": 1.0, + "shutdown_timeout": 1.0, + "environmental_variables": { + "PROCESSIDENTIFIER": "component_initial" + } + } + }, + "control_client_mock": { + "component_properties": { + "binary_name": "control_client_mock", + "application_profile": { + "application_type": "State_Manager", + "alive_supervision": { + "min_indications": 0 + } + } + }, + "deployment_config": { + "ready_timeout": 1.0, + "shutdown_timeout": 1.0, + "environmental_variables": { + "PROCESSIDENTIFIER": "control_client_mock" + } + } + }, + "component_does_report_krunning_in_time": { + "component_properties": { + "binary_name": "recovery_action_reporting", + "process_arguments": [ + "500.0" + ] + }, + "deployment_config": { + "ready_timeout": 1.0, + "shutdown_timeout": 1.0, + "environmental_variables": { + "PROCESSIDENTIFIER": "component_does_report_krunning_in_time" + } + } + }, + "component_does_not_report_krunning_in_time": { + "component_properties": { + "binary_name": "recovery_action_reporting", + "process_arguments": [ + "1500.0" + ] + }, + "deployment_config": { + "ready_timeout": 1.0, + "shutdown_timeout": 1.0, + "environmental_variables": { + "PROCESSIDENTIFIER": "component_does_not_report_krunning_in_time" + } + } + } + }, + "run_targets": { + "Startup": { + "depends_on": [ + "component_initial", + "control_client_mock" + ], + "recovery_action": { + "switch_run_target": { + "run_target": "fallback_run_target" + } + } + }, + "run_target_app_does_report_krunning_in_time": { + "depends_on": [ + "control_client_mock", + "component_does_report_krunning_in_time" + ], + "recovery_action": { + "switch_run_target": { + "run_target": "fallback_run_target" + } + } + }, + "run_target_app_does_not_report_krunning_in_time": { + "depends_on": [ + "control_client_mock", + "component_does_not_report_krunning_in_time" + ], + "recovery_action": { + "switch_run_target": { + "run_target": "fallback_run_target" + } + } + }, + "Off": { + "depends_on": [] + } + }, + "initial_run_target": "Startup", + "alive_supervision": { + "evaluation_cycle": 0.05 + }, + "fallback_run_target": { + "depends_on": [ + "control_client_mock" + ] + } +} diff --git a/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.py b/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.py new file mode 100644 index 000000000..a9bd94890 --- /dev/null +++ b/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.py @@ -0,0 +1,53 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* +from tests.utils.testing_utils.run_until_file_deployed import run_until_file_deployed +from tests.utils.testing_utils.setup_test import setup_test +from tests.utils.testing_utils.test_results import ( + check_for_failures, + download_xml_results, +) +from attribute_plugin import add_test_properties + + +@add_test_properties( + partially_verifies=[ + "feat_req__lifecycle__controlif_status", + "feat_req__lifecycle__process_failure_react", + "feat_req__lifecycle__configurable_wait_time", + ], + test_type="requirements-based", + derivation_technique="requirements-analysis", +) +def test_recovery_action_simple_rep_failure(target, setup_test, test_output_dir, remote_test_dir): + """ + Objective: Verifies that recovery action is executed when the simple reporting of kRunning is not successful. + + Case 1: Using simple reporting, the process does report kRunning in time (500ms below boundary) + Expected Behaviour: Reporting kRunning is successful, recovery action is not executed. + + Case 2: Using simple reporting, the process does not report kRunning in time (500ms above boundary) + Expected Behaviour: Reporting kRunning is not successful, recovery action is executed. + """ + + run_until_file_deployed( + target=target, + binary_path=str(remote_test_dir / "launch_manager"), + file_path=remote_test_dir.parent / "test_end", + cwd=str(remote_test_dir), + timeout_s=10.0, + ) + + download_xml_results(target, remote_test_dir, test_output_dir) + all_files, failing_files = check_for_failures(test_output_dir) + assert len(all_files) == 3, f"Didn't find the expected number of files {all_files}" + assert len(failing_files) == 0, f"Found failures in files {failing_files}" From 5bf0f77baea93cf87fc2ca4add3be2a34a9e58ae Mon Sep 17 00:00:00 2001 From: Timo Steuerwald Date: Wed, 27 May 2026 16:17:42 +0200 Subject: [PATCH 02/20] Fix build --- .../control_client_mock.cpp | 2 +- .../recovery_action_complex_rep_failure.py | 4 ++-- .../recovery_action_initial.cpp | 2 +- .../recovery_action_reporting.cpp | 2 +- .../control_client_mock.cpp | 2 +- .../recovery_action_initial.cpp | 2 +- .../recovery_action_reporting.cpp | 2 +- .../recovery_action_simple_rep_failure.py | 4 ++-- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/integration/recovery_action_complex_rep_failure/control_client_mock.cpp b/tests/integration/recovery_action_complex_rep_failure/control_client_mock.cpp index 71ebcfc0c..a69f4d550 100644 --- a/tests/integration/recovery_action_complex_rep_failure/control_client_mock.cpp +++ b/tests/integration/recovery_action_complex_rep_failure/control_client_mock.cpp @@ -56,5 +56,5 @@ TEST(RecoveryActionComplexRepFailure, ControlClientMock) int main(int argc, char** argv) { - return TestRunner(__FILE__, true, true).RunTests(); + return TestRunner(__FILE__, TerminationBehavior::kWait, TerminationNotification::kTestEnd).RunTests(); } diff --git a/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.py b/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.py index b27351045..d8986e1e0 100644 --- a/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.py +++ b/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.py @@ -13,7 +13,7 @@ from tests.utils.testing_utils.run_until_file_deployed import run_until_file_deployed from tests.utils.testing_utils.setup_test import setup_test from tests.utils.testing_utils.test_results import ( - check_for_failures, + get_failing_files, download_xml_results, ) from attribute_plugin import add_test_properties @@ -48,6 +48,6 @@ def test_recovery_action_complex_rep_failure(target, setup_test, test_output_dir ) download_xml_results(target, remote_test_dir, test_output_dir) - all_files, failing_files = check_for_failures(test_output_dir) + all_files, failing_files = get_failing_files(test_output_dir) assert len(all_files) == 3, f"Didn't find the expected number of files {all_files}" assert len(failing_files) == 0, f"Found failures in files {failing_files}" diff --git a/tests/integration/recovery_action_complex_rep_failure/recovery_action_initial.cpp b/tests/integration/recovery_action_complex_rep_failure/recovery_action_initial.cpp index b51fe2478..9c6a70d8e 100644 --- a/tests/integration/recovery_action_complex_rep_failure/recovery_action_initial.cpp +++ b/tests/integration/recovery_action_complex_rep_failure/recovery_action_initial.cpp @@ -43,5 +43,5 @@ int main(int argc, char** argv) g_argc = argc; g_argv = argv; - return TestRunner(__FILE__, false).RunTests(); + return TestRunner(__FILE__, TerminationBehavior::kContinue).RunTests(); } diff --git a/tests/integration/recovery_action_complex_rep_failure/recovery_action_reporting.cpp b/tests/integration/recovery_action_complex_rep_failure/recovery_action_reporting.cpp index 0e0f8563d..ed63632cb 100644 --- a/tests/integration/recovery_action_complex_rep_failure/recovery_action_reporting.cpp +++ b/tests/integration/recovery_action_complex_rep_failure/recovery_action_reporting.cpp @@ -210,5 +210,5 @@ int main(int argc, char** argv) g_argc = argc; g_argv = argv; - return TestRunner(__FILE__, false).RunTests(); + return TestRunner(__FILE__, TerminationBehavior::kContinue).RunTests(); } diff --git a/tests/integration/recovery_action_simple_rep_failure/control_client_mock.cpp b/tests/integration/recovery_action_simple_rep_failure/control_client_mock.cpp index 0d332b059..6092c9809 100644 --- a/tests/integration/recovery_action_simple_rep_failure/control_client_mock.cpp +++ b/tests/integration/recovery_action_simple_rep_failure/control_client_mock.cpp @@ -58,5 +58,5 @@ TEST(RecoveryActionSimpleRepFailure, ControlClientMock) int main(int argc, char** argv) { - return TestRunner(__FILE__, true, true).RunTests(); + return TestRunner(__FILE__, TerminationBehavior::kWait, TerminationNotification::kTestEnd).RunTests(); } diff --git a/tests/integration/recovery_action_simple_rep_failure/recovery_action_initial.cpp b/tests/integration/recovery_action_simple_rep_failure/recovery_action_initial.cpp index 4c292739b..ef1a951c9 100644 --- a/tests/integration/recovery_action_simple_rep_failure/recovery_action_initial.cpp +++ b/tests/integration/recovery_action_simple_rep_failure/recovery_action_initial.cpp @@ -43,5 +43,5 @@ int main(int argc, char** argv) g_argc = argc; g_argv = argv; - return TestRunner(__FILE__, false).RunTests(); + return TestRunner(__FILE__, TerminationBehavior::kContinue).RunTests(); } diff --git a/tests/integration/recovery_action_simple_rep_failure/recovery_action_reporting.cpp b/tests/integration/recovery_action_simple_rep_failure/recovery_action_reporting.cpp index 9118b4fda..d578bfb9a 100644 --- a/tests/integration/recovery_action_simple_rep_failure/recovery_action_reporting.cpp +++ b/tests/integration/recovery_action_simple_rep_failure/recovery_action_reporting.cpp @@ -44,5 +44,5 @@ int main(int argc, char** argv) g_argc = argc; g_argv = argv; - return TestRunner(__FILE__, false).RunTests(); + return TestRunner(__FILE__, TerminationBehavior::kContinue).RunTests(); } diff --git a/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.py b/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.py index a9bd94890..fe23a82c1 100644 --- a/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.py +++ b/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.py @@ -13,7 +13,7 @@ from tests.utils.testing_utils.run_until_file_deployed import run_until_file_deployed from tests.utils.testing_utils.setup_test import setup_test from tests.utils.testing_utils.test_results import ( - check_for_failures, + get_failing_files, download_xml_results, ) from attribute_plugin import add_test_properties @@ -48,6 +48,6 @@ def test_recovery_action_simple_rep_failure(target, setup_test, test_output_dir, ) download_xml_results(target, remote_test_dir, test_output_dir) - all_files, failing_files = check_for_failures(test_output_dir) + all_files, failing_files = get_failing_files(test_output_dir) assert len(all_files) == 3, f"Didn't find the expected number of files {all_files}" assert len(failing_files) == 0, f"Found failures in files {failing_files}" From 7d6d968f47a18551f17cd96289c387f3570085af Mon Sep 17 00:00:00 2001 From: Timo Steuerwald Date: Wed, 27 May 2026 17:33:05 +0200 Subject: [PATCH 03/20] Remove not needed cmp & its impl --- .../recovery_action_complex_rep_failure/BUILD | 11 ----- .../recovery_action_complex_rep_failure.json | 16 ------- .../recovery_action_complex_rep_failure.py | 2 +- .../recovery_action_initial.cpp | 47 ------------------- .../recovery_action_simple_rep_failure/BUILD | 12 ----- .../recovery_action_initial.cpp | 47 ------------------- .../recovery_action_simple_rep_failure.json | 16 ------- .../recovery_action_simple_rep_failure.py | 2 +- 8 files changed, 2 insertions(+), 151 deletions(-) delete mode 100644 tests/integration/recovery_action_complex_rep_failure/recovery_action_initial.cpp delete mode 100644 tests/integration/recovery_action_simple_rep_failure/recovery_action_initial.cpp diff --git a/tests/integration/recovery_action_complex_rep_failure/BUILD b/tests/integration/recovery_action_complex_rep_failure/BUILD index e39320ad0..0f9765400 100644 --- a/tests/integration/recovery_action_complex_rep_failure/BUILD +++ b/tests/integration/recovery_action_complex_rep_failure/BUILD @@ -32,16 +32,6 @@ cc_binary( ], ) -cc_binary( - name = "recovery_action_initial", - srcs = ["recovery_action_initial.cpp"], - deps = [ - "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", - "//tests/utils/test_helper", - "@googletest//:gtest_main", - ], -) - cc_binary( name = "recovery_action_reporting", srcs = ["recovery_action_reporting.cpp"], @@ -56,7 +46,6 @@ cc_binary( pkg_files( name = "recovery_action_complex_rep_failure_main_files", srcs = [ - ":recovery_action_initial", ":recovery_action_reporting", ":control_client_mock", "//src/launch_manager_daemon:launch_manager", diff --git a/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.json b/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.json index f5191d777..6d48a7c27 100644 --- a/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.json +++ b/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.json @@ -42,21 +42,6 @@ } }, "components": { - "component_initial": { - "component_properties": { - "binary_name": "recovery_action_initial", - "process_arguments": [ - "S-CORE rules!" - ] - }, - "deployment_config": { - "ready_timeout": 1.0, - "shutdown_timeout": 1.0, - "environmental_variables": { - "PROCESSIDENTIFIER": "component_initial" - } - } - }, "control_client_mock": { "component_properties": { "binary_name": "control_client_mock", @@ -109,7 +94,6 @@ "run_targets": { "Startup": { "depends_on": [ - "component_initial", "control_client_mock" ], "recovery_action": { diff --git a/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.py b/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.py index d8986e1e0..4f962bda5 100644 --- a/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.py +++ b/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.py @@ -49,5 +49,5 @@ def test_recovery_action_complex_rep_failure(target, setup_test, test_output_dir download_xml_results(target, remote_test_dir, test_output_dir) all_files, failing_files = get_failing_files(test_output_dir) - assert len(all_files) == 3, f"Didn't find the expected number of files {all_files}" + assert len(all_files) == 2, f"Didn't find the expected number of files {all_files}" assert len(failing_files) == 0, f"Found failures in files {failing_files}" diff --git a/tests/integration/recovery_action_complex_rep_failure/recovery_action_initial.cpp b/tests/integration/recovery_action_complex_rep_failure/recovery_action_initial.cpp deleted file mode 100644 index 9c6a70d8e..000000000 --- a/tests/integration/recovery_action_complex_rep_failure/recovery_action_initial.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************** - * 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 - -#include "tests/utils/test_helper/test_helper.hpp" -#include - -int g_argc; -char** g_argv; - -// Given a correct configuration with: -// - An initial Run Target named "Startup" -// - Startup contains 2 components named "component_initial" and "control_client_mock" -// - component_initial has command line parameter "S-CORE rules!" -TEST(RecoveryActionComplexRepFailure, RecoveryActionInitial) -{ - // Establish communication with launch manager - TEST_STEP("Report kRunning from RecoveryActionInitial") - { - auto result = score::lcm::LifecycleClient{}.ReportExecutionState(score::lcm::ExecutionState::kRunning); - EXPECT_TRUE(result.has_value()) << "ReportExecutionState() failed: " << result.error().Message(); - } - // Check arguments - TEST_STEP("Check args") - { - ASSERT_GT(g_argc, 1) << "Wrong number of arguments"; - EXPECT_STREQ(g_argv[1], "S-CORE rules!") << "Argument 2 was incorrect"; - } -} - -int main(int argc, char** argv) -{ - g_argc = argc; - g_argv = argv; - - return TestRunner(__FILE__, TerminationBehavior::kContinue).RunTests(); -} diff --git a/tests/integration/recovery_action_simple_rep_failure/BUILD b/tests/integration/recovery_action_simple_rep_failure/BUILD index f9e745015..509a4dcb7 100644 --- a/tests/integration/recovery_action_simple_rep_failure/BUILD +++ b/tests/integration/recovery_action_simple_rep_failure/BUILD @@ -32,17 +32,6 @@ cc_binary( ], ) -cc_binary( - name = "recovery_action_initial", - srcs = ["recovery_action_initial.cpp"], - deps = [ - "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", - "//src/lifecycle_client_lib:lifecyclemanager", - "//tests/utils/test_helper", - "@googletest//:gtest_main", - ], -) - cc_binary( name = "recovery_action_reporting", srcs = ["recovery_action_reporting.cpp"], @@ -56,7 +45,6 @@ cc_binary( pkg_files( name = "recovery_action_simple_rep_failure_main_files", srcs = [ - ":recovery_action_initial", ":recovery_action_reporting", ":control_client_mock", "//src/launch_manager_daemon:launch_manager", diff --git a/tests/integration/recovery_action_simple_rep_failure/recovery_action_initial.cpp b/tests/integration/recovery_action_simple_rep_failure/recovery_action_initial.cpp deleted file mode 100644 index ef1a951c9..000000000 --- a/tests/integration/recovery_action_simple_rep_failure/recovery_action_initial.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************** - * 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 - -#include "tests/utils/test_helper/test_helper.hpp" -#include - -int g_argc; -char** g_argv; - -// Given a correct configuration with: -// - An initial Run Target named "Startup" -// - Startup contains 2 components named "component_initial" and "control_client_mock" -// - component_initial has command line parameter "S-CORE rules!" -TEST(RecoveryActionSimpleRepFailure, RecoveryActionInitial) -{ - // Establish communication with launch manager - TEST_STEP("Report kRunning from RecoveryActionInitial") - { - auto result = score::lcm::LifecycleClient{}.ReportExecutionState(score::lcm::ExecutionState::kRunning); - EXPECT_TRUE(result.has_value()) << "ReportExecutionState() failed: " << result.error().Message(); - } - // Check arguments - TEST_STEP("Check args") - { - ASSERT_GT(g_argc, 1) << "Wrong number of arguments"; - EXPECT_STREQ(g_argv[1], "S-CORE rules!") << "Argument 2 was incorrect"; - } -} - -int main(int argc, char** argv) -{ - g_argc = argc; - g_argv = argv; - - return TestRunner(__FILE__, TerminationBehavior::kContinue).RunTests(); -} diff --git a/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.json b/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.json index 23894f26e..c26ab06e9 100644 --- a/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.json +++ b/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.json @@ -42,21 +42,6 @@ } }, "components": { - "component_initial": { - "component_properties": { - "binary_name": "recovery_action_initial", - "process_arguments": [ - "S-CORE rules!" - ] - }, - "deployment_config": { - "ready_timeout": 1.0, - "shutdown_timeout": 1.0, - "environmental_variables": { - "PROCESSIDENTIFIER": "component_initial" - } - } - }, "control_client_mock": { "component_properties": { "binary_name": "control_client_mock", @@ -109,7 +94,6 @@ "run_targets": { "Startup": { "depends_on": [ - "component_initial", "control_client_mock" ], "recovery_action": { diff --git a/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.py b/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.py index fe23a82c1..941fd0bf9 100644 --- a/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.py +++ b/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.py @@ -49,5 +49,5 @@ def test_recovery_action_simple_rep_failure(target, setup_test, test_output_dir, download_xml_results(target, remote_test_dir, test_output_dir) all_files, failing_files = get_failing_files(test_output_dir) - assert len(all_files) == 3, f"Didn't find the expected number of files {all_files}" + assert len(all_files) == 2, f"Didn't find the expected number of files {all_files}" assert len(failing_files) == 0, f"Found failures in files {failing_files}" From 514c2dacb35812255cd76328d229baa475cd49b3 Mon Sep 17 00:00:00 2001 From: Timo Steuerwald Date: Wed, 27 May 2026 17:49:15 +0200 Subject: [PATCH 04/20] Rename: Part one, folder only --- .../BUILD | 0 .../control_client_mock.cpp | 0 .../recovery_action_complex_rep_failure.json | 0 .../recovery_action_complex_rep_failure.py | 0 .../recovery_action_reporting.cpp | 0 .../BUILD | 0 .../control_client_mock.cpp | 0 .../recovery_action_reporting.cpp | 0 .../recovery_action_simple_rep_failure.json | 0 .../recovery_action_simple_rep_failure.py | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename tests/integration/{recovery_action_complex_rep_failure => process_complex_rep_failure}/BUILD (100%) rename tests/integration/{recovery_action_complex_rep_failure => process_complex_rep_failure}/control_client_mock.cpp (100%) rename tests/integration/{recovery_action_complex_rep_failure => process_complex_rep_failure}/recovery_action_complex_rep_failure.json (100%) rename tests/integration/{recovery_action_complex_rep_failure => process_complex_rep_failure}/recovery_action_complex_rep_failure.py (100%) rename tests/integration/{recovery_action_complex_rep_failure => process_complex_rep_failure}/recovery_action_reporting.cpp (100%) rename tests/integration/{recovery_action_simple_rep_failure => process_simple_rep_failure}/BUILD (100%) rename tests/integration/{recovery_action_simple_rep_failure => process_simple_rep_failure}/control_client_mock.cpp (100%) rename tests/integration/{recovery_action_simple_rep_failure => process_simple_rep_failure}/recovery_action_reporting.cpp (100%) rename tests/integration/{recovery_action_simple_rep_failure => process_simple_rep_failure}/recovery_action_simple_rep_failure.json (100%) rename tests/integration/{recovery_action_simple_rep_failure => process_simple_rep_failure}/recovery_action_simple_rep_failure.py (100%) diff --git a/tests/integration/recovery_action_complex_rep_failure/BUILD b/tests/integration/process_complex_rep_failure/BUILD similarity index 100% rename from tests/integration/recovery_action_complex_rep_failure/BUILD rename to tests/integration/process_complex_rep_failure/BUILD diff --git a/tests/integration/recovery_action_complex_rep_failure/control_client_mock.cpp b/tests/integration/process_complex_rep_failure/control_client_mock.cpp similarity index 100% rename from tests/integration/recovery_action_complex_rep_failure/control_client_mock.cpp rename to tests/integration/process_complex_rep_failure/control_client_mock.cpp diff --git a/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.json b/tests/integration/process_complex_rep_failure/recovery_action_complex_rep_failure.json similarity index 100% rename from tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.json rename to tests/integration/process_complex_rep_failure/recovery_action_complex_rep_failure.json diff --git a/tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.py b/tests/integration/process_complex_rep_failure/recovery_action_complex_rep_failure.py similarity index 100% rename from tests/integration/recovery_action_complex_rep_failure/recovery_action_complex_rep_failure.py rename to tests/integration/process_complex_rep_failure/recovery_action_complex_rep_failure.py diff --git a/tests/integration/recovery_action_complex_rep_failure/recovery_action_reporting.cpp b/tests/integration/process_complex_rep_failure/recovery_action_reporting.cpp similarity index 100% rename from tests/integration/recovery_action_complex_rep_failure/recovery_action_reporting.cpp rename to tests/integration/process_complex_rep_failure/recovery_action_reporting.cpp diff --git a/tests/integration/recovery_action_simple_rep_failure/BUILD b/tests/integration/process_simple_rep_failure/BUILD similarity index 100% rename from tests/integration/recovery_action_simple_rep_failure/BUILD rename to tests/integration/process_simple_rep_failure/BUILD diff --git a/tests/integration/recovery_action_simple_rep_failure/control_client_mock.cpp b/tests/integration/process_simple_rep_failure/control_client_mock.cpp similarity index 100% rename from tests/integration/recovery_action_simple_rep_failure/control_client_mock.cpp rename to tests/integration/process_simple_rep_failure/control_client_mock.cpp diff --git a/tests/integration/recovery_action_simple_rep_failure/recovery_action_reporting.cpp b/tests/integration/process_simple_rep_failure/recovery_action_reporting.cpp similarity index 100% rename from tests/integration/recovery_action_simple_rep_failure/recovery_action_reporting.cpp rename to tests/integration/process_simple_rep_failure/recovery_action_reporting.cpp diff --git a/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.json b/tests/integration/process_simple_rep_failure/recovery_action_simple_rep_failure.json similarity index 100% rename from tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.json rename to tests/integration/process_simple_rep_failure/recovery_action_simple_rep_failure.json diff --git a/tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.py b/tests/integration/process_simple_rep_failure/recovery_action_simple_rep_failure.py similarity index 100% rename from tests/integration/recovery_action_simple_rep_failure/recovery_action_simple_rep_failure.py rename to tests/integration/process_simple_rep_failure/recovery_action_simple_rep_failure.py From 95dc805e194285c3ff927295c9f0bb03129e9f25 Mon Sep 17 00:00:00 2001 From: Timo Steuerwald Date: Wed, 27 May 2026 18:24:08 +0200 Subject: [PATCH 05/20] Rename: Part two, files and content --- .../process_complex_rep_failure/BUILD | 32 +++++++++---------- .../process_complex_rep_failure.json} | 6 ++-- ...lure.py => process_complex_rep_failure.py} | 0 ...ting.cpp => process_complex_reporting.cpp} | 12 ++----- .../process_simple_rep_failure/BUILD | 32 +++++++++---------- .../process_simple_rep_failure.json} | 6 ++-- ...ilure.py => process_simple_rep_failure.py} | 0 ...rting.cpp => process_simple_reporting.cpp} | 4 +-- 8 files changed, 42 insertions(+), 50 deletions(-) rename tests/integration/{process_simple_rep_failure/recovery_action_simple_rep_failure.json => process_complex_rep_failure/process_complex_rep_failure.json} (95%) rename tests/integration/process_complex_rep_failure/{recovery_action_complex_rep_failure.py => process_complex_rep_failure.py} (100%) rename tests/integration/process_complex_rep_failure/{recovery_action_reporting.cpp => process_complex_reporting.cpp} (96%) rename tests/integration/{process_complex_rep_failure/recovery_action_complex_rep_failure.json => process_simple_rep_failure/process_simple_rep_failure.json} (95%) rename tests/integration/process_simple_rep_failure/{recovery_action_simple_rep_failure.py => process_simple_rep_failure.py} (100%) rename tests/integration/process_simple_rep_failure/{recovery_action_reporting.cpp => process_simple_reporting.cpp} (92%) diff --git a/tests/integration/process_complex_rep_failure/BUILD b/tests/integration/process_complex_rep_failure/BUILD index 0f9765400..2525309ec 100644 --- a/tests/integration/process_complex_rep_failure/BUILD +++ b/tests/integration/process_complex_rep_failure/BUILD @@ -16,8 +16,8 @@ load("//:defs.bzl", "launch_manager_config") load("//tests/utils/bazel:integration.bzl", "integration_test") launch_manager_config( - name = "lm_recovery_action_complex_rep_failure_config", - config = "//tests/integration/recovery_action_complex_rep_failure:recovery_action_complex_rep_failure.json", + name = "lm_process_complex_rep_failure_config", + config = "//tests/integration/process_complex_rep_failure:process_complex_rep_failure.json", flatbuffer_out_dir = "etc", ) @@ -33,8 +33,8 @@ cc_binary( ) cc_binary( - name = "recovery_action_reporting", - srcs = ["recovery_action_reporting.cpp"], + name = "process_complex_reporting", + srcs = ["process_complex_reporting.cpp"], deps = [ "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", "//src/lifecycle_client_lib", @@ -44,34 +44,34 @@ cc_binary( ) pkg_files( - name = "recovery_action_complex_rep_failure_main_files", + name = "process_complex_rep_failure_main_files", srcs = [ - ":recovery_action_reporting", + ":process_complex_reporting", ":control_client_mock", "//src/launch_manager_daemon:launch_manager", ], attributes = pkg_attributes(mode = "0755"), - prefix = "tests/recovery_action_complex_rep_failure", + prefix = "tests/process_complex_rep_failure", ) pkg_files( - name = "recovery_action_complex_rep_failure_etc_files", - srcs = [":lm_recovery_action_complex_rep_failure_config"], - prefix = "tests/recovery_action_complex_rep_failure", + name = "process_complex_rep_failure_etc_files", + srcs = [":lm_process_complex_rep_failure_config"], + prefix = "tests/process_complex_rep_failure", ) pkg_tar( - name = "recovery_action_complex_rep_failure_binaries", + name = "process_complex_rep_failure_binaries", srcs = [ - ":recovery_action_complex_rep_failure_etc_files", - ":recovery_action_complex_rep_failure_main_files", + ":process_complex_rep_failure_etc_files", + ":process_complex_rep_failure_main_files", ], ) integration_test( - name = "recovery_action_complex_rep_failure", - srcs = ["recovery_action_complex_rep_failure.py"], + name = "process_complex_rep_failure", + srcs = ["process_complex_rep_failure.py"], tags = ["integration"], - test_binaries = ":recovery_action_complex_rep_failure_binaries", + test_binaries = ":process_complex_rep_failure_binaries", deps = ["//tests/utils/testing_utils"], ) diff --git a/tests/integration/process_simple_rep_failure/recovery_action_simple_rep_failure.json b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.json similarity index 95% rename from tests/integration/process_simple_rep_failure/recovery_action_simple_rep_failure.json rename to tests/integration/process_complex_rep_failure/process_complex_rep_failure.json index c26ab06e9..b6d56f4fc 100644 --- a/tests/integration/process_simple_rep_failure/recovery_action_simple_rep_failure.json +++ b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.json @@ -2,7 +2,7 @@ "schema_version": 1, "defaults": { "deployment_config": { - "bin_dir": "/tmp/tests/recovery_action_simple_rep_failure", + "bin_dir": "/tmp/tests/process_complex_rep_failure", "ready_timeout": 1.0, "shutdown_timeout": 1.0, "ready_recovery_action": { @@ -62,7 +62,7 @@ }, "component_does_report_krunning_in_time": { "component_properties": { - "binary_name": "recovery_action_reporting", + "binary_name": "process_complex_reporting", "process_arguments": [ "500.0" ] @@ -77,7 +77,7 @@ }, "component_does_not_report_krunning_in_time": { "component_properties": { - "binary_name": "recovery_action_reporting", + "binary_name": "process_complex_reporting", "process_arguments": [ "1500.0" ] diff --git a/tests/integration/process_complex_rep_failure/recovery_action_complex_rep_failure.py b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py similarity index 100% rename from tests/integration/process_complex_rep_failure/recovery_action_complex_rep_failure.py rename to tests/integration/process_complex_rep_failure/process_complex_rep_failure.py diff --git a/tests/integration/process_complex_rep_failure/recovery_action_reporting.cpp b/tests/integration/process_complex_rep_failure/process_complex_reporting.cpp similarity index 96% rename from tests/integration/process_complex_rep_failure/recovery_action_reporting.cpp rename to tests/integration/process_complex_rep_failure/process_complex_reporting.cpp index ed63632cb..3df936e47 100644 --- a/tests/integration/process_complex_rep_failure/recovery_action_reporting.cpp +++ b/tests/integration/process_complex_rep_failure/process_complex_reporting.cpp @@ -80,19 +80,11 @@ std::optional parseOptions(int argc, char *const *argv) noexcept } return config; } - -void set_process_name() -{ - const char* identifier = getenv("PROCESSIDENTIFIER"); -} - class LifecycleApp final : public score::mw::lifecycle::Application { public: std::int32_t Initialize(const score::mw::lifecycle::ApplicationContext& appCtx) override { - set_process_name(); - // Build a classic argv for getopt() from ApplicationContext arguments const auto& args = appCtx.get_arguments(); m_argvStorage.clear(); @@ -188,7 +180,7 @@ class LifecycleApp final : public score::mw::lifecycle::Application std::vector m_argvStorage{}; }; -TEST(RecoveryActionComplexRepFailure, RecoveryActionReporting) +TEST(ProcessComplexRepFailure, ProcessComplexReporting) { // Check arguments TEST_STEP("Check args") @@ -197,7 +189,7 @@ TEST(RecoveryActionComplexRepFailure, RecoveryActionReporting) ASSERT_FALSE((g_argv[1][0] != '0') && (atoi(g_argv[1])) == 0) << "Argument must be a number"; } // Report kRunning with the appropriate delay - TEST_STEP("Report kRunning from RecoveryActionReporting") + TEST_STEP("Report kRunning from ProcessComplexReporting") { std::this_thread::sleep_for(std::chrono::milliseconds(atoi(g_argv[1]))); diff --git a/tests/integration/process_simple_rep_failure/BUILD b/tests/integration/process_simple_rep_failure/BUILD index 509a4dcb7..8622c7b80 100644 --- a/tests/integration/process_simple_rep_failure/BUILD +++ b/tests/integration/process_simple_rep_failure/BUILD @@ -16,8 +16,8 @@ load("//:defs.bzl", "launch_manager_config") load("//tests/utils/bazel:integration.bzl", "integration_test") launch_manager_config( - name = "lm_recovery_action_simple_rep_failure_config", - config = "//tests/integration/recovery_action_simple_rep_failure:recovery_action_simple_rep_failure.json", + name = "lm_process_simple_rep_failure_config", + config = "//tests/integration/process_simple_rep_failure:process_simple_rep_failure.json", flatbuffer_out_dir = "etc", ) @@ -33,8 +33,8 @@ cc_binary( ) cc_binary( - name = "recovery_action_reporting", - srcs = ["recovery_action_reporting.cpp"], + name = "process_simple_reporting", + srcs = ["process_simple_reporting.cpp"], deps = [ "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", "//tests/utils/test_helper", @@ -43,34 +43,34 @@ cc_binary( ) pkg_files( - name = "recovery_action_simple_rep_failure_main_files", + name = "process_simple_rep_failure_main_files", srcs = [ - ":recovery_action_reporting", + ":process_simple_reporting", ":control_client_mock", "//src/launch_manager_daemon:launch_manager", ], attributes = pkg_attributes(mode = "0755"), - prefix = "tests/recovery_action_simple_rep_failure", + prefix = "tests/process_simple_rep_failure", ) pkg_files( - name = "recovery_action_simple_rep_failure_etc_files", - srcs = [":lm_recovery_action_simple_rep_failure_config"], - prefix = "tests/recovery_action_simple_rep_failure", + name = "process_simple_rep_failure_etc_files", + srcs = [":lm_process_simple_rep_failure_config"], + prefix = "tests/process_simple_rep_failure", ) pkg_tar( - name = "recovery_action_simple_rep_failure_binaries", + name = "process_simple_rep_failure_binaries", srcs = [ - ":recovery_action_simple_rep_failure_etc_files", - ":recovery_action_simple_rep_failure_main_files", + ":process_simple_rep_failure_etc_files", + ":process_simple_rep_failure_main_files", ], ) integration_test( - name = "recovery_action_simple_rep_failure", - srcs = ["recovery_action_simple_rep_failure.py"], + name = "process_simple_rep_failure", + srcs = ["process_simple_rep_failure.py"], tags = ["integration"], - test_binaries = ":recovery_action_simple_rep_failure_binaries", + test_binaries = ":process_simple_rep_failure_binaries", deps = ["//tests/utils/testing_utils"], ) diff --git a/tests/integration/process_complex_rep_failure/recovery_action_complex_rep_failure.json b/tests/integration/process_simple_rep_failure/process_simple_rep_failure.json similarity index 95% rename from tests/integration/process_complex_rep_failure/recovery_action_complex_rep_failure.json rename to tests/integration/process_simple_rep_failure/process_simple_rep_failure.json index 6d48a7c27..44ce06e01 100644 --- a/tests/integration/process_complex_rep_failure/recovery_action_complex_rep_failure.json +++ b/tests/integration/process_simple_rep_failure/process_simple_rep_failure.json @@ -2,7 +2,7 @@ "schema_version": 1, "defaults": { "deployment_config": { - "bin_dir": "/tmp/tests/recovery_action_complex_rep_failure", + "bin_dir": "/tmp/tests/process_simple_rep_failure", "ready_timeout": 1.0, "shutdown_timeout": 1.0, "ready_recovery_action": { @@ -62,7 +62,7 @@ }, "component_does_report_krunning_in_time": { "component_properties": { - "binary_name": "recovery_action_reporting", + "binary_name": "process_simple_reporting", "process_arguments": [ "500.0" ] @@ -77,7 +77,7 @@ }, "component_does_not_report_krunning_in_time": { "component_properties": { - "binary_name": "recovery_action_reporting", + "binary_name": "process_simple_reporting", "process_arguments": [ "1500.0" ] diff --git a/tests/integration/process_simple_rep_failure/recovery_action_simple_rep_failure.py b/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py similarity index 100% rename from tests/integration/process_simple_rep_failure/recovery_action_simple_rep_failure.py rename to tests/integration/process_simple_rep_failure/process_simple_rep_failure.py diff --git a/tests/integration/process_simple_rep_failure/recovery_action_reporting.cpp b/tests/integration/process_simple_rep_failure/process_simple_reporting.cpp similarity index 92% rename from tests/integration/process_simple_rep_failure/recovery_action_reporting.cpp rename to tests/integration/process_simple_rep_failure/process_simple_reporting.cpp index d578bfb9a..ea2a8a639 100644 --- a/tests/integration/process_simple_rep_failure/recovery_action_reporting.cpp +++ b/tests/integration/process_simple_rep_failure/process_simple_reporting.cpp @@ -21,7 +21,7 @@ int g_argc; char** g_argv; -TEST(RecoveryActionSimpleRepFailure, RecoveryActionReporting) +TEST(ProcessSimpleRepFailure, ProcessSimpleReporting) { // Check arguments TEST_STEP("Check args") @@ -30,7 +30,7 @@ TEST(RecoveryActionSimpleRepFailure, RecoveryActionReporting) ASSERT_FALSE((g_argv[1][0] != '0') && (atoi(g_argv[1])) == 0) << "Argument must be a number"; } // Report kRunning with the appropriate delay - TEST_STEP("Report kRunning from RecoveryActionReporting") + TEST_STEP("Report kRunning from ProcessSimpleReporting") { std::this_thread::sleep_for(std::chrono::milliseconds(atoi(g_argv[1]))); From 33c40c1b2ac32db58b249a67cbeab610f351a650 Mon Sep 17 00:00:00 2001 From: Timo Steuerwald Date: Thu, 28 May 2026 13:20:54 +0200 Subject: [PATCH 06/20] Update test description --- .../process_complex_rep_failure/process_complex_rep_failure.py | 2 +- .../process_simple_rep_failure/process_simple_rep_failure.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py index 4f962bda5..7d69a05af 100644 --- a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py +++ b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py @@ -30,7 +30,7 @@ ) def test_recovery_action_complex_rep_failure(target, setup_test, test_output_dir, remote_test_dir): """ - Objective: Verifies that recovery action is executed when the complex reporting of kRunning is not successful. + Objective: Verifies that recovery action is executed when the reporting of kRunning via LifecycleClient library (named "complex reporting" in the following) is not happening in time and vice versa. Case 1: Using complex reporting, the process does report kRunning in time (500ms below boundary) Expected Behaviour: Reporting kRunning is successful, recovery action is not executed. diff --git a/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py b/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py index 941fd0bf9..da02c8638 100644 --- a/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py +++ b/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py @@ -30,7 +30,7 @@ ) def test_recovery_action_simple_rep_failure(target, setup_test, test_output_dir, remote_test_dir): """ - Objective: Verifies that recovery action is executed when the simple reporting of kRunning is not successful. + Objective: Verifies that recovery action is executed when the reporting of kRunning via LifecycleClient API (named "simple reporting" in the following) is not happening in time and vice versa. Case 1: Using simple reporting, the process does report kRunning in time (500ms below boundary) Expected Behaviour: Reporting kRunning is successful, recovery action is not executed. From 7526639cbbd7983e48532a253093e154b6b7e633 Mon Sep 17 00:00:00 2001 From: Timo Steuerwald Date: Fri, 29 May 2026 16:26:59 +0200 Subject: [PATCH 07/20] Drop verbose mode as it is nearly not used --- .../process_complex_reporting.cpp | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/tests/integration/process_complex_rep_failure/process_complex_reporting.cpp b/tests/integration/process_complex_rep_failure/process_complex_reporting.cpp index 3df936e47..fb49a74b2 100644 --- a/tests/integration/process_complex_rep_failure/process_complex_reporting.cpp +++ b/tests/integration/process_complex_rep_failure/process_complex_reporting.cpp @@ -30,15 +30,13 @@ struct Config bool crashRequested{false}; std::int32_t crashTimeInMs{1000}; bool failToStart{false}; - bool verbose{false}; }; std::string helpSstring = "Usage:\n\ -r Worst case response time to SIGTERM signal in milliseconds.\n\ -c Simulate crash of the application, after specified time in milliseconds.\n\ - -s Simulate failure during start-up of the application.\n\ - -v Run in verbose mode.\n"; + -s Simulate failure during start-up of the application.\n"; std::optional parseOptions(int argc, char *const *argv) noexcept { @@ -65,10 +63,6 @@ std::optional parseOptions(int argc, char *const *argv) noexcept std::cout << helpSstring; return std::nullopt; - case 'v': - config.verbose = true; - break; - case '?': std::cout << "Unrecognized option: -" << static_cast(optopt) << std::endl; std::cout << helpSstring; @@ -135,8 +129,6 @@ class LifecycleApp final : public score::mw::lifecycle::Application static_cast((m_config.responseTimeInMs % 1000) * 1000000L) }; - auto timeLastVerboseLog = std::chrono::steady_clock::now(); - while (!stopToken.stop_requested()) { if (true == m_config.crashRequested) @@ -158,17 +150,6 @@ class LifecycleApp final : public score::mw::lifecycle::Application std::abort(); } } - - if (m_config.verbose) - { - const auto now = std::chrono::steady_clock::now(); - if (now - timeLastVerboseLog >= std::chrono::seconds(1)) - { - std::cout << "LifecycleApp: Running in verbose mode" << std::endl; - timeLastVerboseLog = now; - } - } - nanosleep(&req, nullptr); } From f511ac56f97da3e42590dce3463b39f4602ca4c6 Mon Sep 17 00:00:00 2001 From: Timo Steuerwald Date: Fri, 29 May 2026 16:27:47 +0200 Subject: [PATCH 08/20] Code review rework: Fix Williams comments --- .../control_client_mock.cpp | 10 ++++------ .../process_complex_rep_failure.py | 10 +++------- .../control_client_mock.cpp | 8 +++----- .../process_simple_rep_failure.py | 14 +++++--------- 4 files changed, 15 insertions(+), 27 deletions(-) diff --git a/tests/integration/process_complex_rep_failure/control_client_mock.cpp b/tests/integration/process_complex_rep_failure/control_client_mock.cpp index a69f4d550..e9f7ccbc8 100644 --- a/tests/integration/process_complex_rep_failure/control_client_mock.cpp +++ b/tests/integration/process_complex_rep_failure/control_client_mock.cpp @@ -13,12 +13,11 @@ #include #include "tests/utils/test_helper/test_helper.hpp" -#include #include #include // Given a correct configuration with: -// - An initial Run Target named "Startup" containing 2 components named "component_initial" and "control_client_mock" +// - An initial Run Target named "Startup" containing component named "control_client_mock" // - A Run Target named "run_target_app_does_report_krunning_in_time" containing "control_client_mock" and "component_does_report_krunning_in_time" // - A Run Target named "run_target_app_does_not_report_krunning_in_time" containing "control_client_mock" and "component_does_not_report_krunning_in_time" @@ -39,13 +38,12 @@ TEST(RecoveryActionComplexRepFailure, ControlClientMock) auto result = client.ActivateRunTarget("run_target_app_does_report_krunning_in_time").Get(stop_token); EXPECT_TRUE(result.has_value()) << "Activating target run_target_app_does_report_krunning_in_time failed: " << result.error().Message(); } - sleep(2); - // Start the run target run_target_app_does_report_krunning_in_time + // Start the run target run_target_app_does_not_report_krunning_in_time TEST_STEP("Activate RunTarget run_target_app_does_not_report_krunning_in_time") { score::cpp::stop_token stop_token; auto result = client.ActivateRunTarget("run_target_app_does_not_report_krunning_in_time").Get(stop_token); - EXPECT_FALSE(result.has_value()) << "Activating target run_target_app_does_not_report_krunning_in_time failed: " << result.error().Message(); + EXPECT_FALSE(result.has_value()) << "Activating target run_target_app_does_not_report_krunning_in_time did not fail as expected."; } TEST_STEP("Activate RunTarget Off") @@ -56,5 +54,5 @@ TEST(RecoveryActionComplexRepFailure, ControlClientMock) int main(int argc, char** argv) { - return TestRunner(__FILE__, TerminationBehavior::kWait, TerminationNotification::kTestEnd).RunTests(); + return TestRunner(__FILE__, TerminationBehavior::kContinue, TerminationNotification::kTestEnd).RunTests(); } diff --git a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py index 7d69a05af..2174827d4 100644 --- a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py +++ b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py @@ -13,8 +13,7 @@ from tests.utils.testing_utils.run_until_file_deployed import run_until_file_deployed from tests.utils.testing_utils.setup_test import setup_test from tests.utils.testing_utils.test_results import ( - get_failing_files, - download_xml_results, + assert_test_results ) from attribute_plugin import add_test_properties @@ -28,7 +27,7 @@ test_type="requirements-based", derivation_technique="requirements-analysis", ) -def test_recovery_action_complex_rep_failure(target, setup_test, test_output_dir, remote_test_dir): +def test_recovery_action_complex_rep_failure(target, setup_test, assert_test_results, remote_test_dir): """ Objective: Verifies that recovery action is executed when the reporting of kRunning via LifecycleClient library (named "complex reporting" in the following) is not happening in time and vice versa. @@ -47,7 +46,4 @@ def test_recovery_action_complex_rep_failure(target, setup_test, test_output_dir timeout_s=10.0, ) - download_xml_results(target, remote_test_dir, test_output_dir) - all_files, failing_files = get_failing_files(test_output_dir) - assert len(all_files) == 2, f"Didn't find the expected number of files {all_files}" - assert len(failing_files) == 0, f"Found failures in files {failing_files}" + assert_test_results({"control_client_mock.xml", "process_complex_reporting.xml"}) diff --git a/tests/integration/process_simple_rep_failure/control_client_mock.cpp b/tests/integration/process_simple_rep_failure/control_client_mock.cpp index 6092c9809..baa4ccd88 100644 --- a/tests/integration/process_simple_rep_failure/control_client_mock.cpp +++ b/tests/integration/process_simple_rep_failure/control_client_mock.cpp @@ -13,12 +13,11 @@ #include #include "tests/utils/test_helper/test_helper.hpp" -#include #include #include // Given a correct configuration with: -// - An initial Run Target named "Startup" containing 2 components named "component_initial" and "control_client_mock" +// - An initial Run Target named "Startup" containing component named "control_client_mock" // - A Run Target named "run_target_app_does_report_krunning_in_time" containing "control_client_mock" and "component_does_report_krunning_in_time" // - A Run Target named "run_target_app_does_not_report_krunning_in_time" containing "control_client_mock" and "component_does_not_report_krunning_in_time" @@ -41,13 +40,12 @@ TEST(RecoveryActionSimpleRepFailure, ControlClientMock) auto result = client.ActivateRunTarget("run_target_app_does_report_krunning_in_time").Get(stop_token); EXPECT_TRUE(result.has_value()) << "Activating target run_target_app_does_report_krunning_in_time failed: " << result.error().Message(); } - sleep(2); // Start the run target run_target_app_does_report_krunning_in_time TEST_STEP("Activate RunTarget run_target_app_does_not_report_krunning_in_time") { score::cpp::stop_token stop_token; auto result = client.ActivateRunTarget("run_target_app_does_not_report_krunning_in_time").Get(stop_token); - EXPECT_FALSE(result.has_value()) << "Activating target run_target_app_does_not_report_krunning_in_time failed: " << result.error().Message(); + EXPECT_FALSE(result.has_value()) << "Activating target run_target_app_does_not_report_krunning_in_time did not fail as expected."; } TEST_STEP("Activate RunTarget Off") @@ -58,5 +56,5 @@ TEST(RecoveryActionSimpleRepFailure, ControlClientMock) int main(int argc, char** argv) { - return TestRunner(__FILE__, TerminationBehavior::kWait, TerminationNotification::kTestEnd).RunTests(); + return TestRunner(__FILE__, TerminationBehavior::kContinue, TerminationNotification::kTestEnd).RunTests(); } diff --git a/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py b/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py index da02c8638..13c933c8e 100644 --- a/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py +++ b/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py @@ -12,10 +12,7 @@ # ******************************************************************************* from tests.utils.testing_utils.run_until_file_deployed import run_until_file_deployed from tests.utils.testing_utils.setup_test import setup_test -from tests.utils.testing_utils.test_results import ( - get_failing_files, - download_xml_results, -) +from tests.utils.testing_utils.test_results import assert_test_results from attribute_plugin import add_test_properties @@ -28,7 +25,7 @@ test_type="requirements-based", derivation_technique="requirements-analysis", ) -def test_recovery_action_simple_rep_failure(target, setup_test, test_output_dir, remote_test_dir): +def test_recovery_action_simple_rep_failure(target, setup_test, assert_test_results, remote_test_dir): """ Objective: Verifies that recovery action is executed when the reporting of kRunning via LifecycleClient API (named "simple reporting" in the following) is not happening in time and vice versa. @@ -47,7 +44,6 @@ def test_recovery_action_simple_rep_failure(target, setup_test, test_output_dir, timeout_s=10.0, ) - download_xml_results(target, remote_test_dir, test_output_dir) - all_files, failing_files = get_failing_files(test_output_dir) - assert len(all_files) == 2, f"Didn't find the expected number of files {all_files}" - assert len(failing_files) == 0, f"Found failures in files {failing_files}" + assert_test_results( + {"control_client_mock.xml", "process_simple_reporting.xml"} + ) From 557cd00e2f6a9a42814d4c08ff47d10871efe903 Mon Sep 17 00:00:00 2001 From: Timo Steuerwald Date: Fri, 29 May 2026 16:38:19 +0200 Subject: [PATCH 09/20] Move test process cpp to test_helper folder --- .../process_complex_rep_failure/BUILD | 7 +- .../process_complex_rep_failure.json | 2 +- .../process_complex_rep_failure.py | 2 +- .../process_complex_reporting.cpp | 187 ------------------ tests/utils/test_helper/BUILD | 12 ++ .../test_helper/complex_reporting_process.cpp | 169 ++++++++++++++++ 6 files changed, 187 insertions(+), 192 deletions(-) delete mode 100644 tests/integration/process_complex_rep_failure/process_complex_reporting.cpp create mode 100644 tests/utils/test_helper/complex_reporting_process.cpp diff --git a/tests/integration/process_complex_rep_failure/BUILD b/tests/integration/process_complex_rep_failure/BUILD index 2525309ec..de25a0b0d 100644 --- a/tests/integration/process_complex_rep_failure/BUILD +++ b/tests/integration/process_complex_rep_failure/BUILD @@ -33,8 +33,9 @@ cc_binary( ) cc_binary( - name = "process_complex_reporting", - srcs = ["process_complex_reporting.cpp"], + name = "complex_reporting_process", + srcs = ["//tests/utils/test_helper:complex_reporting_process.cpp"], + visibility = ["//tests:__subpackages__"], deps = [ "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", "//src/lifecycle_client_lib", @@ -46,7 +47,7 @@ cc_binary( pkg_files( name = "process_complex_rep_failure_main_files", srcs = [ - ":process_complex_reporting", + ":complex_reporting_process", ":control_client_mock", "//src/launch_manager_daemon:launch_manager", ], diff --git a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.json b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.json index b6d56f4fc..fe6605d40 100644 --- a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.json +++ b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.json @@ -62,7 +62,7 @@ }, "component_does_report_krunning_in_time": { "component_properties": { - "binary_name": "process_complex_reporting", + "binary_name": "complex_reporting_process", "process_arguments": [ "500.0" ] diff --git a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py index 2174827d4..dc18985a7 100644 --- a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py +++ b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py @@ -46,4 +46,4 @@ def test_recovery_action_complex_rep_failure(target, setup_test, assert_test_res timeout_s=10.0, ) - assert_test_results({"control_client_mock.xml", "process_complex_reporting.xml"}) + assert_test_results({"control_client_mock.xml", "complex_reporting_process.xml"}) diff --git a/tests/integration/process_complex_rep_failure/process_complex_reporting.cpp b/tests/integration/process_complex_rep_failure/process_complex_reporting.cpp deleted file mode 100644 index fb49a74b2..000000000 --- a/tests/integration/process_complex_rep_failure/process_complex_reporting.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/******************************************************************************** - * 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 -#include - -#include - -#include "tests/utils/test_helper/test_helper.hpp" -#include -#include "src/lifecycle_client_lib/include/application.h" -#include "src/lifecycle_client_lib/include/runapplication.h" - -int g_argc; -char** g_argv; - -/// @brief CLI configuration options for the not_supervised_application process -struct Config -{ - std::int32_t responseTimeInMs{100}; - bool crashRequested{false}; - std::int32_t crashTimeInMs{1000}; - bool failToStart{false}; -}; - -std::string helpSstring = - "Usage:\n\ - -r Worst case response time to SIGTERM signal in milliseconds.\n\ - -c Simulate crash of the application, after specified time in milliseconds.\n\ - -s Simulate failure during start-up of the application.\n"; - -std::optional parseOptions(int argc, char *const *argv) noexcept -{ - Config config{}; - int c; - while ((c = getopt(argc, argv, ":r:c:svh")) != -1) - { - switch (static_cast(c)) - { - case 'r': - config.responseTimeInMs = std::stoi(optarg); - break; - - case 'c': - config.crashRequested = true; - config.crashTimeInMs = std::stoi(optarg); - break; - - case 's': - config.failToStart = true; - break; - - case 'h': - std::cout << helpSstring; - return std::nullopt; - - case '?': - std::cout << "Unrecognized option: -" << static_cast(optopt) << std::endl; - std::cout << helpSstring; - return std::nullopt; - - default: - break; - } - } - return config; -} -class LifecycleApp final : public score::mw::lifecycle::Application -{ -public: - std::int32_t Initialize(const score::mw::lifecycle::ApplicationContext& appCtx) override - { - // Build a classic argv for getopt() from ApplicationContext arguments - const auto& args = appCtx.get_arguments(); - m_argvStorage.clear(); - m_argvStorage.reserve(args.size() + 2); - - // Ensure argv[0] exists (getopt expects it) - if (args.empty()) - { - m_argvStorage.push_back(const_cast("LifecycleApp")); - } - else - { - for (const auto& s : args) - { - // NOTE: relies on the underlying storage staying alive during Initialize(). - m_argvStorage.push_back(const_cast(s.data())); - } - } - - m_argvStorage.push_back(nullptr); - - optind = 1; - - const int argcLocal = static_cast(m_argvStorage.size() - 1); - const auto config = parseOptions(argcLocal, m_argvStorage.data()); - if (!config) - { - return EXIT_FAILURE; - } - - m_config = *config; - - if (true == m_config.failToStart) - { - return EXIT_FAILURE; - } - - return 0; - } - - std::int32_t Run(const score::cpp::stop_token& stopToken) override - { - std::chrono::time_point startTime = std::chrono::steady_clock::now(); - std::chrono::duration runTime; - - timespec req{ - static_cast(m_config.responseTimeInMs / 1000), - static_cast((m_config.responseTimeInMs % 1000) * 1000000L) - }; - - while (!stopToken.stop_requested()) - { - if (true == m_config.crashRequested) - { - runTime = std::chrono::steady_clock::now() - startTime; - int timeTillCrash = static_cast(m_config.crashTimeInMs - runTime.count()); - - if (timeTillCrash < m_config.responseTimeInMs) - { - if (timeTillCrash > 0) - { - timespec crash_req{ - static_cast(timeTillCrash / 1000), - static_cast((timeTillCrash % 1000) * 1000000L) - }; - nanosleep(&crash_req, nullptr); - } - - std::abort(); - } - } - nanosleep(&req, nullptr); - } - - return EXIT_SUCCESS; - } - -private: - Config m_config{}; - std::vector m_argvStorage{}; -}; - -TEST(ProcessComplexRepFailure, ProcessComplexReporting) -{ - // Check arguments - TEST_STEP("Check args") - { - ASSERT_GT(g_argc, 1) << "Wrong number of arguments"; - ASSERT_FALSE((g_argv[1][0] != '0') && (atoi(g_argv[1])) == 0) << "Argument must be a number"; - } - // Report kRunning with the appropriate delay - TEST_STEP("Report kRunning from ProcessComplexReporting") - { - std::this_thread::sleep_for(std::chrono::milliseconds(atoi(g_argv[1]))); - - score::mw::lifecycle::run_application(g_argc, g_argv); - } -} - -int main(int argc, char** argv) -{ - g_argc = argc; - g_argv = argv; - - return TestRunner(__FILE__, TerminationBehavior::kContinue).RunTests(); -} diff --git a/tests/utils/test_helper/BUILD b/tests/utils/test_helper/BUILD index 553e23972..b609de8b6 100644 --- a/tests/utils/test_helper/BUILD +++ b/tests/utils/test_helper/BUILD @@ -15,6 +15,7 @@ exports_files( [ "reporting_process.cpp", "verification_process.cpp", + "complex_reporting_process.cpp", ], visibility = ["//tests:__subpackages__"], ) @@ -39,6 +40,17 @@ cc_binary( ], ) +cc_binary( + name = "complex_reporting_process", + srcs = ["complex_reporting_process.cpp"], + visibility = ["//tests:__subpackages__"], + deps = [ + ":test_helper", + "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", + "@googletest//:gtest_main", + ], +) + cc_binary( name = "verification_process", srcs = ["verification_process.cpp"], diff --git a/tests/utils/test_helper/complex_reporting_process.cpp b/tests/utils/test_helper/complex_reporting_process.cpp new file mode 100644 index 000000000..cb0fba9c0 --- /dev/null +++ b/tests/utils/test_helper/complex_reporting_process.cpp @@ -0,0 +1,169 @@ +/******************************************************************************** + * 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 +#include + +#include + +#include "src/lifecycle_client_lib/include/application.h" +#include "src/lifecycle_client_lib/include/runapplication.h" +#include "tests/utils/test_helper/test_helper.hpp" +#include + +int g_argc; +char **g_argv; + +/// @brief CLI configuration options for the not_supervised_application process +struct Config { + std::int32_t responseTimeInMs{100}; + bool crashRequested{false}; + std::int32_t crashTimeInMs{1000}; + bool failToStart{false}; +}; + +std::string helpSstring = "Usage:\n\ + -r Worst case response time to SIGTERM signal in milliseconds.\n\ + -c Simulate crash of the application, after specified time in milliseconds.\n\ + -s Simulate failure during start-up of the application.\n"; + +std::optional parseOptions(int argc, char *const *argv) noexcept { + Config config{}; + int c; + while ((c = getopt(argc, argv, ":r:c:svh")) != -1) { + switch (static_cast(c)) { + case 'r': + config.responseTimeInMs = std::stoi(optarg); + break; + + case 'c': + config.crashRequested = true; + config.crashTimeInMs = std::stoi(optarg); + break; + + case 's': + config.failToStart = true; + break; + + case 'h': + std::cout << helpSstring; + return std::nullopt; + + case '?': + std::cout << "Unrecognized option: -" << static_cast(optopt) + << std::endl; + std::cout << helpSstring; + return std::nullopt; + + default: + break; + } + } + return config; +} +class LifecycleApp final : public score::mw::lifecycle::Application { +public: + std::int32_t + Initialize(const score::mw::lifecycle::ApplicationContext &appCtx) override { + // Build a classic argv for getopt() from ApplicationContext arguments + const auto &args = appCtx.get_arguments(); + m_argvStorage.clear(); + m_argvStorage.reserve(args.size() + 2); + + // Ensure argv[0] exists (getopt expects it) + if (args.empty()) { + m_argvStorage.push_back(const_cast("LifecycleApp")); + } else { + for (const auto &s : args) { + // NOTE: relies on the underlying storage staying alive during + // Initialize(). + m_argvStorage.push_back(const_cast(s.data())); + } + } + + m_argvStorage.push_back(nullptr); + + optind = 1; + + const int argcLocal = static_cast(m_argvStorage.size() - 1); + const auto config = parseOptions(argcLocal, m_argvStorage.data()); + if (!config) { + return EXIT_FAILURE; + } + + m_config = *config; + + if (true == m_config.failToStart) { + return EXIT_FAILURE; + } + + return 0; + } + + std::int32_t Run(const score::cpp::stop_token &stopToken) override { + std::chrono::time_point startTime = + std::chrono::steady_clock::now(); + std::chrono::duration runTime; + + timespec req{ + static_cast(m_config.responseTimeInMs / 1000), + static_cast((m_config.responseTimeInMs % 1000) * 1000000L)}; + + while (!stopToken.stop_requested()) { + if (true == m_config.crashRequested) { + runTime = std::chrono::steady_clock::now() - startTime; + int timeTillCrash = + static_cast(m_config.crashTimeInMs - runTime.count()); + + if (timeTillCrash < m_config.responseTimeInMs) { + if (timeTillCrash > 0) { + timespec crash_req{ + static_cast(timeTillCrash / 1000), + static_cast((timeTillCrash % 1000) * 1000000L)}; + nanosleep(&crash_req, nullptr); + } + + std::abort(); + } + } + nanosleep(&req, nullptr); + } + + return EXIT_SUCCESS; + } + +private: + Config m_config{}; + std::vector m_argvStorage{}; +}; + +TEST(ComplexReportingProcess, ReportsRunning) { + // Check arguments + TEST_STEP("Check args") { + ASSERT_GT(g_argc, 1) << "Wrong number of arguments"; + ASSERT_FALSE((g_argv[1][0] != '0') && (atoi(g_argv[1])) == 0) + << "Argument must be a number"; + } + // Report kRunning with the appropriate delay + TEST_STEP("Report kRunning from ProcessComplexReporting") { + std::this_thread::sleep_for(std::chrono::milliseconds(atoi(g_argv[1]))); + + score::mw::lifecycle::run_application(g_argc, g_argv); + } +} + +int main(int argc, char **argv) { + g_argc = argc; + g_argv = argv; + + return TestRunner(__FILE__, TerminationBehavior::kContinue).RunTests(); +} From b9eafafcade448b95fd29f0cbea2c263409fdb45 Mon Sep 17 00:00:00 2001 From: Timo Steuerwald Date: Fri, 29 May 2026 18:57:49 +0200 Subject: [PATCH 10/20] Update list of requirements --- tests/integration/crash_on_startup/crash_on_startup.py | 8 +++++++- .../process_complex_rep_failure.py | 7 +++++-- .../process_simple_rep_failure.py | 7 +++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/integration/crash_on_startup/crash_on_startup.py b/tests/integration/crash_on_startup/crash_on_startup.py index b88bc439f..2cd9063e7 100644 --- a/tests/integration/crash_on_startup/crash_on_startup.py +++ b/tests/integration/crash_on_startup/crash_on_startup.py @@ -17,7 +17,13 @@ @add_test_properties( - fully_verifies=["feat_req__lifecycle__failure_detect"], + fully_verifies=[ + "feat_req__lifecycle__failure_detect", + "feat_req__lifecycle__recov_run_target_switch" + ], + partially_verifies=[ + "feat_req__lifecycle__recovery_action_support" + ], test_type="requirements-based", derivation_technique="requirements-analysis", ) diff --git a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py index dc18985a7..4b69f3106 100644 --- a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py +++ b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py @@ -20,9 +20,12 @@ @add_test_properties( partially_verifies=[ - "feat_req__lifecycle__controlif_status", "feat_req__lifecycle__process_failure_react", - "feat_req__lifecycle__configurable_wait_time", + "feat_req__lifecycle__recov_run_target_switch", + "feat_req__lifecycle__recovery_action_support" + ], + fully_verifies=[ + "feat_req__lifecycle__failure_detect", ], test_type="requirements-based", derivation_technique="requirements-analysis", diff --git a/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py b/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py index 13c933c8e..900e83a63 100644 --- a/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py +++ b/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py @@ -18,9 +18,12 @@ @add_test_properties( partially_verifies=[ - "feat_req__lifecycle__controlif_status", "feat_req__lifecycle__process_failure_react", - "feat_req__lifecycle__configurable_wait_time", + "feat_req__lifecycle__recov_run_target_switch", + "feat_req__lifecycle__recovery_action_support" + ], + fully_verifies=[ + "feat_req__lifecycle__failure_detect", ], test_type="requirements-based", derivation_technique="requirements-analysis", From 2bb8d128d668378b9da0a3d29bb31e0c29f8e456 Mon Sep 17 00:00:00 2001 From: Timo Steuerwald Date: Fri, 29 May 2026 19:01:16 +0200 Subject: [PATCH 11/20] Verify fallback run target has been activated --- .../process_complex_rep_failure/BUILD | 1 + .../control_client_mock.cpp | 94 ++++++++++++------- .../process_complex_rep_failure.json | 15 ++- .../process_simple_rep_failure/BUILD | 1 + .../control_client_mock.cpp | 94 ++++++++++++------- .../process_simple_rep_failure.json | 15 ++- 6 files changed, 151 insertions(+), 69 deletions(-) diff --git a/tests/integration/process_complex_rep_failure/BUILD b/tests/integration/process_complex_rep_failure/BUILD index de25a0b0d..4da2787f2 100644 --- a/tests/integration/process_complex_rep_failure/BUILD +++ b/tests/integration/process_complex_rep_failure/BUILD @@ -50,6 +50,7 @@ pkg_files( ":complex_reporting_process", ":control_client_mock", "//src/launch_manager_daemon:launch_manager", + "//tests/utils/test_helper:verification_process", ], attributes = pkg_attributes(mode = "0755"), prefix = "tests/process_complex_rep_failure", diff --git a/tests/integration/process_complex_rep_failure/control_client_mock.cpp b/tests/integration/process_complex_rep_failure/control_client_mock.cpp index e9f7ccbc8..09d9f1409 100644 --- a/tests/integration/process_complex_rep_failure/control_client_mock.cpp +++ b/tests/integration/process_complex_rep_failure/control_client_mock.cpp @@ -17,42 +17,70 @@ #include // Given a correct configuration with: -// - An initial Run Target named "Startup" containing component named "control_client_mock" -// - A Run Target named "run_target_app_does_report_krunning_in_time" containing "control_client_mock" and "component_does_report_krunning_in_time" -// - A Run Target named "run_target_app_does_not_report_krunning_in_time" containing "control_client_mock" and "component_does_not_report_krunning_in_time" +// - An initial Run Target named "Startup" containing component named +// "control_client_mock" +// - A Run Target named "run_target_app_does_report_krunning_in_time" +// containing "control_client_mock" and +// "component_does_report_krunning_in_time" +// - A Run Target named "run_target_app_does_not_report_krunning_in_time" +// containing "control_client_mock" and +// "component_does_not_report_krunning_in_time" -TEST(RecoveryActionComplexRepFailure, ControlClientMock) -{ - score::lcm::ControlClient client; +TEST(RecoveryActionComplexRepFailure, ControlClientMock) { + score::lcm::ControlClient client; - // Establish communication with launch manager - TEST_STEP("Report kRunning from ControlClientMock") - { - auto result = score::lcm::LifecycleClient{}.ReportExecutionState(score::lcm::ExecutionState::kRunning); - ASSERT_TRUE(result.has_value()) << "ReportExecutionState() failed: " << result.error().Message(); - } - // Start the run target run_target_app_does_report_krunning_in_time - TEST_STEP("Activate RunTarget run_target_app_does_report_krunning_in_time") - { - score::cpp::stop_token stop_token; - auto result = client.ActivateRunTarget("run_target_app_does_report_krunning_in_time").Get(stop_token); - EXPECT_TRUE(result.has_value()) << "Activating target run_target_app_does_report_krunning_in_time failed: " << result.error().Message(); - } - // Start the run target run_target_app_does_not_report_krunning_in_time - TEST_STEP("Activate RunTarget run_target_app_does_not_report_krunning_in_time") - { - score::cpp::stop_token stop_token; - auto result = client.ActivateRunTarget("run_target_app_does_not_report_krunning_in_time").Get(stop_token); - EXPECT_FALSE(result.has_value()) << "Activating target run_target_app_does_not_report_krunning_in_time did not fail as expected."; - } + ASSERT_TRUE(check_clean({test_end_location, fallback_file})); - TEST_STEP("Activate RunTarget Off") - { - client.ActivateRunTarget("Off"); - } + // Establish communication with launch manager + TEST_STEP("Report kRunning from ControlClientMock") { + auto result = score::lcm::LifecycleClient{}.ReportExecutionState( + score::lcm::ExecutionState::kRunning); + ASSERT_TRUE(result.has_value()) + << "ReportExecutionState() failed: " << result.error().Message(); + } + // Start the run target run_target_app_does_report_krunning_in_time + TEST_STEP("Activate RunTarget run_target_app_does_report_krunning_in_time") { + score::cpp::stop_token stop_token; + auto result = + client.ActivateRunTarget("run_target_app_does_report_krunning_in_time") + .Get(stop_token); + EXPECT_TRUE(result.has_value()) + << "Activating target run_target_app_does_report_krunning_in_time " + "failed: " + << result.error().Message(); + } + // Limitation: we cannot wait for the transition to fallback to complete + sleep(1); + // Then, the LM should exhaust retries and trigger the fallback + TEST_STEP("Verify fallback run target has not been activated") { + EXPECT_FALSE(std::filesystem::exists(fallback_file)) + << "Fallback run target should has not been activated"; + } + // Start the run target run_target_app_does_not_report_krunning_in_time + TEST_STEP( + "Activate RunTarget run_target_app_does_not_report_krunning_in_time") { + score::cpp::stop_token stop_token; + auto result = client + .ActivateRunTarget( + "run_target_app_does_not_report_krunning_in_time") + .Get(stop_token); + EXPECT_FALSE(result.has_value()) + << "Activating target run_target_app_does_not_report_krunning_in_time " + "did not fail as expected."; + } + // Limitation: we cannot wait for the transition to fallback to complete + sleep(1); + // Then, the LM should exhaust retries and trigger the fallback + TEST_STEP("Verify fallback run target was activated") { + EXPECT_TRUE(std::filesystem::exists(fallback_file)) + << "Fallback run target should have been activated"; + } + + TEST_STEP("Activate RunTarget Off") { client.ActivateRunTarget("Off"); } } -int main(int argc, char** argv) -{ - return TestRunner(__FILE__, TerminationBehavior::kContinue, TerminationNotification::kTestEnd).RunTests(); +int main(int argc, char **argv) { + return TestRunner(__FILE__, TerminationBehavior::kContinue, + TerminationNotification::kTestEnd) + .RunTests(); } diff --git a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.json b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.json index fe6605d40..5038e24fe 100644 --- a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.json +++ b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.json @@ -89,6 +89,18 @@ "PROCESSIDENTIFIER": "component_does_not_report_krunning_in_time" } } + }, + "verification_component": { + "component_properties": { + "binary_name": "verification_process", + "application_profile": { + "application_type": "Native", + "is_self_terminating": true + }, + "ready_condition": { + "process_state": "Terminated" + } + } } }, "run_targets": { @@ -134,7 +146,8 @@ }, "fallback_run_target": { "depends_on": [ - "control_client_mock" + "control_client_mock", + "verification_component" ] } } diff --git a/tests/integration/process_simple_rep_failure/BUILD b/tests/integration/process_simple_rep_failure/BUILD index 8622c7b80..98b9ab05e 100644 --- a/tests/integration/process_simple_rep_failure/BUILD +++ b/tests/integration/process_simple_rep_failure/BUILD @@ -48,6 +48,7 @@ pkg_files( ":process_simple_reporting", ":control_client_mock", "//src/launch_manager_daemon:launch_manager", + "//tests/utils/test_helper:verification_process", ], attributes = pkg_attributes(mode = "0755"), prefix = "tests/process_simple_rep_failure", diff --git a/tests/integration/process_simple_rep_failure/control_client_mock.cpp b/tests/integration/process_simple_rep_failure/control_client_mock.cpp index baa4ccd88..946dcec99 100644 --- a/tests/integration/process_simple_rep_failure/control_client_mock.cpp +++ b/tests/integration/process_simple_rep_failure/control_client_mock.cpp @@ -17,44 +17,70 @@ #include // Given a correct configuration with: -// - An initial Run Target named "Startup" containing component named "control_client_mock" -// - A Run Target named "run_target_app_does_report_krunning_in_time" containing "control_client_mock" and "component_does_report_krunning_in_time" -// - A Run Target named "run_target_app_does_not_report_krunning_in_time" containing "control_client_mock" and "component_does_not_report_krunning_in_time" +// - An initial Run Target named "Startup" containing component named +// "control_client_mock" +// - A Run Target named "run_target_app_does_report_krunning_in_time" +// containing "control_client_mock" and +// "component_does_report_krunning_in_time" +// - A Run Target named "run_target_app_does_not_report_krunning_in_time" +// containing "control_client_mock" and +// "component_does_not_report_krunning_in_time" -TEST(RecoveryActionSimpleRepFailure, ControlClientMock) -{ - score::lcm::ControlClient client; +TEST(RecoveryActionSimpleRepFailure, ControlClientMock) { + score::lcm::ControlClient client; - ASSERT_TRUE(check_clean({test_end_location, fallback_file})); + ASSERT_TRUE(check_clean({test_end_location, fallback_file})); - // Establish communication with launch manager - TEST_STEP("Report kRunning from ControlClientMock") - { - auto result = score::lcm::LifecycleClient{}.ReportExecutionState(score::lcm::ExecutionState::kRunning); - ASSERT_TRUE(result.has_value()) << "ReportExecutionState() failed: " << result.error().Message(); - } - // Start the run target run_target_app_does_report_krunning_in_time - TEST_STEP("Activate RunTarget run_target_app_does_report_krunning_in_time") - { - score::cpp::stop_token stop_token; - auto result = client.ActivateRunTarget("run_target_app_does_report_krunning_in_time").Get(stop_token); - EXPECT_TRUE(result.has_value()) << "Activating target run_target_app_does_report_krunning_in_time failed: " << result.error().Message(); - } - // Start the run target run_target_app_does_report_krunning_in_time - TEST_STEP("Activate RunTarget run_target_app_does_not_report_krunning_in_time") - { - score::cpp::stop_token stop_token; - auto result = client.ActivateRunTarget("run_target_app_does_not_report_krunning_in_time").Get(stop_token); - EXPECT_FALSE(result.has_value()) << "Activating target run_target_app_does_not_report_krunning_in_time did not fail as expected."; - } + // Establish communication with launch manager + TEST_STEP("Report kRunning from ControlClientMock") { + auto result = score::lcm::LifecycleClient{}.ReportExecutionState( + score::lcm::ExecutionState::kRunning); + ASSERT_TRUE(result.has_value()) + << "ReportExecutionState() failed: " << result.error().Message(); + } + // Start the run target run_target_app_does_report_krunning_in_time + TEST_STEP("Activate RunTarget run_target_app_does_report_krunning_in_time") { + score::cpp::stop_token stop_token; + auto result = + client.ActivateRunTarget("run_target_app_does_report_krunning_in_time") + .Get(stop_token); + EXPECT_TRUE(result.has_value()) + << "Activating target run_target_app_does_report_krunning_in_time " + "failed: " + << result.error().Message(); + } + // Limitation: we cannot wait for the transition to fallback to complete + sleep(1); + // Then, the LM should exhaust retries and trigger the fallback + TEST_STEP("Verify fallback run target has not been activated") { + EXPECT_FALSE(std::filesystem::exists(fallback_file)) + << "Fallback run target should has not been activated"; + } + // Start the run target run_target_app_does_report_krunning_in_time + TEST_STEP( + "Activate RunTarget run_target_app_does_not_report_krunning_in_time") { + score::cpp::stop_token stop_token; + auto result = client + .ActivateRunTarget( + "run_target_app_does_not_report_krunning_in_time") + .Get(stop_token); + EXPECT_FALSE(result.has_value()) + << "Activating target run_target_app_does_not_report_krunning_in_time " + "did not fail as expected."; + } + // Limitation: we cannot wait for the transition to fallback to complete + sleep(1); + // Then, the LM should exhaust retries and trigger the fallback + TEST_STEP("Verify fallback run target was activated") { + EXPECT_TRUE(std::filesystem::exists(fallback_file)) + << "Fallback run target should have been activated"; + } - TEST_STEP("Activate RunTarget Off") - { - client.ActivateRunTarget("Off"); - } + TEST_STEP("Activate RunTarget Off") { client.ActivateRunTarget("Off"); } } -int main(int argc, char** argv) -{ - return TestRunner(__FILE__, TerminationBehavior::kContinue, TerminationNotification::kTestEnd).RunTests(); +int main(int argc, char **argv) { + return TestRunner(__FILE__, TerminationBehavior::kContinue, + TerminationNotification::kTestEnd) + .RunTests(); } diff --git a/tests/integration/process_simple_rep_failure/process_simple_rep_failure.json b/tests/integration/process_simple_rep_failure/process_simple_rep_failure.json index 44ce06e01..ae234cd2f 100644 --- a/tests/integration/process_simple_rep_failure/process_simple_rep_failure.json +++ b/tests/integration/process_simple_rep_failure/process_simple_rep_failure.json @@ -89,6 +89,18 @@ "PROCESSIDENTIFIER": "component_does_not_report_krunning_in_time" } } + }, + "verification_component": { + "component_properties": { + "binary_name": "verification_process", + "application_profile": { + "application_type": "Native", + "is_self_terminating": true + }, + "ready_condition": { + "process_state": "Terminated" + } + } } }, "run_targets": { @@ -134,7 +146,8 @@ }, "fallback_run_target": { "depends_on": [ - "control_client_mock" + "control_client_mock", + "verification_component" ] } } From 2e9fab84bf3745e06db1824fa67d9b6da72fa688 Mon Sep 17 00:00:00 2001 From: Timo Steuerwald Date: Fri, 29 May 2026 19:15:20 +0200 Subject: [PATCH 12/20] Tiny documentation changes --- .../process_complex_rep_failure.py | 2 +- tests/utils/test_helper/complex_reporting_process.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py index 4b69f3106..80ba902cc 100644 --- a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py +++ b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py @@ -32,7 +32,7 @@ ) def test_recovery_action_complex_rep_failure(target, setup_test, assert_test_results, remote_test_dir): """ - Objective: Verifies that recovery action is executed when the reporting of kRunning via LifecycleClient library (named "complex reporting" in the following) is not happening in time and vice versa. + Objective: Verifies that recovery action is executed when the reporting of kRunning via mw::mw::lifecycle library (named "complex reporting" in the following) is not happening in time and vice versa. Case 1: Using complex reporting, the process does report kRunning in time (500ms below boundary) Expected Behaviour: Reporting kRunning is successful, recovery action is not executed. diff --git a/tests/utils/test_helper/complex_reporting_process.cpp b/tests/utils/test_helper/complex_reporting_process.cpp index cb0fba9c0..a39a63883 100644 --- a/tests/utils/test_helper/complex_reporting_process.cpp +++ b/tests/utils/test_helper/complex_reporting_process.cpp @@ -20,10 +20,15 @@ #include "tests/utils/test_helper/test_helper.hpp" #include +/// @file complex_reporting_process.cpp +/// @brief Monitored test process using mw::lifecycle (score::mw::lifecycle::Application / +/// run_application) to simulate configurable start, run, and crash +/// behaviours for integration tests of the lifecycle manager. + int g_argc; char **g_argv; -/// @brief CLI configuration options for the not_supervised_application process +/// @brief CLI configuration options struct Config { std::int32_t responseTimeInMs{100}; bool crashRequested{false}; From 9e26c66eee3836360dd2516f0112d0a27819f6c4 Mon Sep 17 00:00:00 2001 From: Lorant Lipcsei Date: Wed, 3 Jun 2026 08:06:36 +0100 Subject: [PATCH 13/20] AZB 97324 Addressing PR comments --- .../control_client_mock.cpp | 4 +-- .../process_complex_rep_failure.py | 3 +- .../control_client_mock.cpp | 6 ++-- .../process_simple_rep_failure.py | 1 + .../test_helper/complex_reporting_process.cpp | 31 ------------------- 5 files changed, 8 insertions(+), 37 deletions(-) diff --git a/tests/integration/process_complex_rep_failure/control_client_mock.cpp b/tests/integration/process_complex_rep_failure/control_client_mock.cpp index 09d9f1409..e33827812 100644 --- a/tests/integration/process_complex_rep_failure/control_client_mock.cpp +++ b/tests/integration/process_complex_rep_failure/control_client_mock.cpp @@ -51,10 +51,10 @@ TEST(RecoveryActionComplexRepFailure, ControlClientMock) { } // Limitation: we cannot wait for the transition to fallback to complete sleep(1); - // Then, the LM should exhaust retries and trigger the fallback + // Then, the LM should continue without triggering the fallback TEST_STEP("Verify fallback run target has not been activated") { EXPECT_FALSE(std::filesystem::exists(fallback_file)) - << "Fallback run target should has not been activated"; + << "Fallback run target should have not been activated"; } // Start the run target run_target_app_does_not_report_krunning_in_time TEST_STEP( diff --git a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py index 80ba902cc..0c3c7123a 100644 --- a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py +++ b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py @@ -32,13 +32,14 @@ ) def test_recovery_action_complex_rep_failure(target, setup_test, assert_test_results, remote_test_dir): """ - Objective: Verifies that recovery action is executed when the reporting of kRunning via mw::mw::lifecycle library (named "complex reporting" in the following) is not happening in time and vice versa. + Objective: Verifies that recovery action is executed when the reporting of kRunning via mw::lifecycle library (named "complex reporting" in the following) is not happening in time and vice versa. Case 1: Using complex reporting, the process does report kRunning in time (500ms below boundary) Expected Behaviour: Reporting kRunning is successful, recovery action is not executed. Case 2: Using complex reporting, the process does not report kRunning in time (500ms above boundary) Expected Behaviour: Reporting kRunning is not successful, recovery action is executed. + The recovery action switches to the fallback run target, the activation of the fallback run target is verified in the test. """ run_until_file_deployed( diff --git a/tests/integration/process_simple_rep_failure/control_client_mock.cpp b/tests/integration/process_simple_rep_failure/control_client_mock.cpp index 946dcec99..5b46caf0d 100644 --- a/tests/integration/process_simple_rep_failure/control_client_mock.cpp +++ b/tests/integration/process_simple_rep_failure/control_client_mock.cpp @@ -51,12 +51,12 @@ TEST(RecoveryActionSimpleRepFailure, ControlClientMock) { } // Limitation: we cannot wait for the transition to fallback to complete sleep(1); - // Then, the LM should exhaust retries and trigger the fallback + // Then, the LM should continue without triggering the fallback TEST_STEP("Verify fallback run target has not been activated") { EXPECT_FALSE(std::filesystem::exists(fallback_file)) - << "Fallback run target should has not been activated"; + << "Fallback run target should have not been activated"; } - // Start the run target run_target_app_does_report_krunning_in_time + // Start the run target run_target_app_does_not_report_krunning_in_time TEST_STEP( "Activate RunTarget run_target_app_does_not_report_krunning_in_time") { score::cpp::stop_token stop_token; diff --git a/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py b/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py index 900e83a63..2e15eeb9a 100644 --- a/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py +++ b/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py @@ -37,6 +37,7 @@ def test_recovery_action_simple_rep_failure(target, setup_test, assert_test_resu Case 2: Using simple reporting, the process does not report kRunning in time (500ms above boundary) Expected Behaviour: Reporting kRunning is not successful, recovery action is executed. + The recovery action switches to the fallback run target, the activation of the fallback run target is verified in the test. """ run_until_file_deployed( diff --git a/tests/utils/test_helper/complex_reporting_process.cpp b/tests/utils/test_helper/complex_reporting_process.cpp index a39a63883..eed184ca7 100644 --- a/tests/utils/test_helper/complex_reporting_process.cpp +++ b/tests/utils/test_helper/complex_reporting_process.cpp @@ -105,12 +105,6 @@ class LifecycleApp final : public score::mw::lifecycle::Application { return EXIT_FAILURE; } - m_config = *config; - - if (true == m_config.failToStart) { - return EXIT_FAILURE; - } - return 0; } @@ -119,35 +113,10 @@ class LifecycleApp final : public score::mw::lifecycle::Application { std::chrono::steady_clock::now(); std::chrono::duration runTime; - timespec req{ - static_cast(m_config.responseTimeInMs / 1000), - static_cast((m_config.responseTimeInMs % 1000) * 1000000L)}; - - while (!stopToken.stop_requested()) { - if (true == m_config.crashRequested) { - runTime = std::chrono::steady_clock::now() - startTime; - int timeTillCrash = - static_cast(m_config.crashTimeInMs - runTime.count()); - - if (timeTillCrash < m_config.responseTimeInMs) { - if (timeTillCrash > 0) { - timespec crash_req{ - static_cast(timeTillCrash / 1000), - static_cast((timeTillCrash % 1000) * 1000000L)}; - nanosleep(&crash_req, nullptr); - } - - std::abort(); - } - } - nanosleep(&req, nullptr); - } - return EXIT_SUCCESS; } private: - Config m_config{}; std::vector m_argvStorage{}; }; From 397a12ce9fea0180fe34d585de1cdae5d2877778 Mon Sep 17 00:00:00 2001 From: Lorant Lipcsei Date: Thu, 4 Jun 2026 10:14:49 +0100 Subject: [PATCH 14/20] AZB 97324 Removing unnecessary code from complex_reporting_process.cpp --- .../test_helper/complex_reporting_process.cpp | 78 ------------------- 1 file changed, 78 deletions(-) diff --git a/tests/utils/test_helper/complex_reporting_process.cpp b/tests/utils/test_helper/complex_reporting_process.cpp index eed184ca7..4d380e38b 100644 --- a/tests/utils/test_helper/complex_reporting_process.cpp +++ b/tests/utils/test_helper/complex_reporting_process.cpp @@ -28,96 +28,18 @@ int g_argc; char **g_argv; -/// @brief CLI configuration options -struct Config { - std::int32_t responseTimeInMs{100}; - bool crashRequested{false}; - std::int32_t crashTimeInMs{1000}; - bool failToStart{false}; -}; - -std::string helpSstring = "Usage:\n\ - -r Worst case response time to SIGTERM signal in milliseconds.\n\ - -c Simulate crash of the application, after specified time in milliseconds.\n\ - -s Simulate failure during start-up of the application.\n"; - -std::optional parseOptions(int argc, char *const *argv) noexcept { - Config config{}; - int c; - while ((c = getopt(argc, argv, ":r:c:svh")) != -1) { - switch (static_cast(c)) { - case 'r': - config.responseTimeInMs = std::stoi(optarg); - break; - - case 'c': - config.crashRequested = true; - config.crashTimeInMs = std::stoi(optarg); - break; - - case 's': - config.failToStart = true; - break; - - case 'h': - std::cout << helpSstring; - return std::nullopt; - - case '?': - std::cout << "Unrecognized option: -" << static_cast(optopt) - << std::endl; - std::cout << helpSstring; - return std::nullopt; - - default: - break; - } - } - return config; -} class LifecycleApp final : public score::mw::lifecycle::Application { public: std::int32_t Initialize(const score::mw::lifecycle::ApplicationContext &appCtx) override { - // Build a classic argv for getopt() from ApplicationContext arguments - const auto &args = appCtx.get_arguments(); - m_argvStorage.clear(); - m_argvStorage.reserve(args.size() + 2); - - // Ensure argv[0] exists (getopt expects it) - if (args.empty()) { - m_argvStorage.push_back(const_cast("LifecycleApp")); - } else { - for (const auto &s : args) { - // NOTE: relies on the underlying storage staying alive during - // Initialize(). - m_argvStorage.push_back(const_cast(s.data())); - } - } - - m_argvStorage.push_back(nullptr); - optind = 1; - const int argcLocal = static_cast(m_argvStorage.size() - 1); - const auto config = parseOptions(argcLocal, m_argvStorage.data()); - if (!config) { - return EXIT_FAILURE; - } - return 0; } std::int32_t Run(const score::cpp::stop_token &stopToken) override { - std::chrono::time_point startTime = - std::chrono::steady_clock::now(); - std::chrono::duration runTime; - return EXIT_SUCCESS; } - -private: - std::vector m_argvStorage{}; }; TEST(ComplexReportingProcess, ReportsRunning) { From 656d861cfc90f84e2b2927f4ce42029092cdf8dd Mon Sep 17 00:00:00 2001 From: Lorant Lipcsei Date: Fri, 5 Jun 2026 13:40:59 +0100 Subject: [PATCH 15/20] Updating the partially verified lists (#238) * Removing feat_req__lifecycle__process_failure_react for process_simple_rep_failure * Removing feat_req__lifecycle__process_failure_react for process_complex_rep_failure --- .../process_complex_rep_failure.py | 5 +---- .../process_simple_rep_failure/process_simple_rep_failure.py | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py index 0c3c7123a..bbf3c5a43 100644 --- a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py +++ b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py @@ -12,15 +12,12 @@ # ******************************************************************************* from tests.utils.testing_utils.run_until_file_deployed import run_until_file_deployed from tests.utils.testing_utils.setup_test import setup_test -from tests.utils.testing_utils.test_results import ( - assert_test_results -) +from tests.utils.testing_utils.test_results import assert_test_results from attribute_plugin import add_test_properties @add_test_properties( partially_verifies=[ - "feat_req__lifecycle__process_failure_react", "feat_req__lifecycle__recov_run_target_switch", "feat_req__lifecycle__recovery_action_support" ], diff --git a/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py b/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py index 2e15eeb9a..868ac1b50 100644 --- a/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py +++ b/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py @@ -18,7 +18,6 @@ @add_test_properties( partially_verifies=[ - "feat_req__lifecycle__process_failure_react", "feat_req__lifecycle__recov_run_target_switch", "feat_req__lifecycle__recovery_action_support" ], From 9bffb0e9f8f370c9b0d29f18bcad84470f157ca3 Mon Sep 17 00:00:00 2001 From: Lorant Lipcsei Date: Tue, 9 Jun 2026 11:23:36 +0100 Subject: [PATCH 16/20] AZB 97324 Fixing launch manager references in the BUILD files --- tests/integration/process_complex_rep_failure/BUILD | 6 +++--- tests/integration/process_simple_rep_failure/BUILD | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/integration/process_complex_rep_failure/BUILD b/tests/integration/process_complex_rep_failure/BUILD index 4da2787f2..31649cf24 100644 --- a/tests/integration/process_complex_rep_failure/BUILD +++ b/tests/integration/process_complex_rep_failure/BUILD @@ -25,8 +25,8 @@ cc_binary( name = "control_client_mock", srcs = ["control_client_mock.cpp"], deps = [ - "//src/control_client_lib", - "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", + "//score/launch_manager:control_cc", + "//score/launch_manager:lifecycle_cc", "//tests/utils/test_helper", "@googletest//:gtest_main", ], @@ -49,7 +49,7 @@ pkg_files( srcs = [ ":complex_reporting_process", ":control_client_mock", - "//src/launch_manager_daemon:launch_manager", + "//score/launch_manager", "//tests/utils/test_helper:verification_process", ], attributes = pkg_attributes(mode = "0755"), diff --git a/tests/integration/process_simple_rep_failure/BUILD b/tests/integration/process_simple_rep_failure/BUILD index 98b9ab05e..338222cd4 100644 --- a/tests/integration/process_simple_rep_failure/BUILD +++ b/tests/integration/process_simple_rep_failure/BUILD @@ -25,8 +25,8 @@ cc_binary( name = "control_client_mock", srcs = ["control_client_mock.cpp"], deps = [ - "//src/control_client_lib", - "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", + "//score/launch_manager:control_cc", + "//score/launch_manager:lifecycle_cc", "//tests/utils/test_helper", "@googletest//:gtest_main", ], @@ -47,7 +47,7 @@ pkg_files( srcs = [ ":process_simple_reporting", ":control_client_mock", - "//src/launch_manager_daemon:launch_manager", + "//score/launch_manager", "//tests/utils/test_helper:verification_process", ], attributes = pkg_attributes(mode = "0755"), From 443e232071e1f572c1cdd1031eef7ff9f659805c Mon Sep 17 00:00:00 2001 From: Lorant Lipcsei Date: Thu, 11 Jun 2026 09:17:27 +0100 Subject: [PATCH 17/20] Fixing source and BUILD files to reflect the folder structure (#238) --- tests/integration/process_complex_rep_failure/BUILD | 3 +-- .../control_client_mock.cpp | 10 +++++----- tests/integration/process_simple_rep_failure/BUILD | 2 +- .../process_simple_rep_failure/control_client_mock.cpp | 10 +++++----- .../process_simple_reporting.cpp | 4 ++-- tests/utils/test_helper/complex_reporting_process.cpp | 6 +++--- 6 files changed, 17 insertions(+), 18 deletions(-) diff --git a/tests/integration/process_complex_rep_failure/BUILD b/tests/integration/process_complex_rep_failure/BUILD index 31649cf24..18adcc32c 100644 --- a/tests/integration/process_complex_rep_failure/BUILD +++ b/tests/integration/process_complex_rep_failure/BUILD @@ -37,8 +37,7 @@ cc_binary( srcs = ["//tests/utils/test_helper:complex_reporting_process.cpp"], visibility = ["//tests:__subpackages__"], deps = [ - "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", - "//src/lifecycle_client_lib", + "//score/launch_manager:lifecycle_cc", "//tests/utils/test_helper", "@googletest//:gtest_main", ], diff --git a/tests/integration/process_complex_rep_failure/control_client_mock.cpp b/tests/integration/process_complex_rep_failure/control_client_mock.cpp index e33827812..42b32d3f3 100644 --- a/tests/integration/process_complex_rep_failure/control_client_mock.cpp +++ b/tests/integration/process_complex_rep_failure/control_client_mock.cpp @@ -13,8 +13,8 @@ #include #include "tests/utils/test_helper/test_helper.hpp" -#include -#include +#include +#include // Given a correct configuration with: // - An initial Run Target named "Startup" containing component named @@ -27,14 +27,14 @@ // "component_does_not_report_krunning_in_time" TEST(RecoveryActionComplexRepFailure, ControlClientMock) { - score::lcm::ControlClient client; + score::mw::lifecycle::ControlClient client; ASSERT_TRUE(check_clean({test_end_location, fallback_file})); // Establish communication with launch manager TEST_STEP("Report kRunning from ControlClientMock") { - auto result = score::lcm::LifecycleClient{}.ReportExecutionState( - score::lcm::ExecutionState::kRunning); + auto result = score::mw::lifecycle::LifecycleClient{}.ReportExecutionState( + score::mw::lifecycle::ExecutionState::kRunning); ASSERT_TRUE(result.has_value()) << "ReportExecutionState() failed: " << result.error().Message(); } diff --git a/tests/integration/process_simple_rep_failure/BUILD b/tests/integration/process_simple_rep_failure/BUILD index 338222cd4..3748f7d54 100644 --- a/tests/integration/process_simple_rep_failure/BUILD +++ b/tests/integration/process_simple_rep_failure/BUILD @@ -36,7 +36,7 @@ cc_binary( name = "process_simple_reporting", srcs = ["process_simple_reporting.cpp"], deps = [ - "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", + "//score/launch_manager:lifecycle_cc", "//tests/utils/test_helper", "@googletest//:gtest_main", ], diff --git a/tests/integration/process_simple_rep_failure/control_client_mock.cpp b/tests/integration/process_simple_rep_failure/control_client_mock.cpp index 5b46caf0d..7b6f96870 100644 --- a/tests/integration/process_simple_rep_failure/control_client_mock.cpp +++ b/tests/integration/process_simple_rep_failure/control_client_mock.cpp @@ -13,8 +13,8 @@ #include #include "tests/utils/test_helper/test_helper.hpp" -#include -#include +#include +#include // Given a correct configuration with: // - An initial Run Target named "Startup" containing component named @@ -27,14 +27,14 @@ // "component_does_not_report_krunning_in_time" TEST(RecoveryActionSimpleRepFailure, ControlClientMock) { - score::lcm::ControlClient client; + score::mw::lifecycle::ControlClient client; ASSERT_TRUE(check_clean({test_end_location, fallback_file})); // Establish communication with launch manager TEST_STEP("Report kRunning from ControlClientMock") { - auto result = score::lcm::LifecycleClient{}.ReportExecutionState( - score::lcm::ExecutionState::kRunning); + auto result = score::mw::lifecycle::LifecycleClient{}.ReportExecutionState( + score::mw::lifecycle::ExecutionState::kRunning); ASSERT_TRUE(result.has_value()) << "ReportExecutionState() failed: " << result.error().Message(); } diff --git a/tests/integration/process_simple_rep_failure/process_simple_reporting.cpp b/tests/integration/process_simple_rep_failure/process_simple_reporting.cpp index ea2a8a639..9258315ed 100644 --- a/tests/integration/process_simple_rep_failure/process_simple_reporting.cpp +++ b/tests/integration/process_simple_rep_failure/process_simple_reporting.cpp @@ -16,7 +16,7 @@ #include #include "tests/utils/test_helper/test_helper.hpp" -#include +#include int g_argc; char** g_argv; @@ -34,7 +34,7 @@ TEST(ProcessSimpleRepFailure, ProcessSimpleReporting) { std::this_thread::sleep_for(std::chrono::milliseconds(atoi(g_argv[1]))); - auto result = score::lcm::LifecycleClient{}.ReportExecutionState(score::lcm::ExecutionState::kRunning); + auto result = score::mw::lifecycle::LifecycleClient{}.ReportExecutionState(score::mw::lifecycle::ExecutionState::kRunning); EXPECT_TRUE(result.has_value()) << "ReportExecutionState() failed: " << result.error().Message(); } } diff --git a/tests/utils/test_helper/complex_reporting_process.cpp b/tests/utils/test_helper/complex_reporting_process.cpp index 4d380e38b..bcbeaaef3 100644 --- a/tests/utils/test_helper/complex_reporting_process.cpp +++ b/tests/utils/test_helper/complex_reporting_process.cpp @@ -15,10 +15,10 @@ #include -#include "src/lifecycle_client_lib/include/application.h" -#include "src/lifecycle_client_lib/include/runapplication.h" #include "tests/utils/test_helper/test_helper.hpp" -#include +#include "score/mw/lifecycle/application.h" +#include "score/mw/lifecycle/runapplication.h" +#include /// @file complex_reporting_process.cpp /// @brief Monitored test process using mw::lifecycle (score::mw::lifecycle::Application / From 5b7269a3ef1c39aa247409a5abf19232087cc39e Mon Sep 17 00:00:00 2001 From: Lorant Lipcsei Date: Thu, 11 Jun 2026 09:22:53 +0100 Subject: [PATCH 18/20] Fixing Test Helper BUILD file to reflect the folder structure (#238) --- tests/utils/test_helper/BUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/utils/test_helper/BUILD b/tests/utils/test_helper/BUILD index b609de8b6..85de2e91f 100644 --- a/tests/utils/test_helper/BUILD +++ b/tests/utils/test_helper/BUILD @@ -46,7 +46,7 @@ cc_binary( visibility = ["//tests:__subpackages__"], deps = [ ":test_helper", - "//src/launch_manager_daemon/lifecycle_client_lib:lifecycle_client", + "//score/launch_manager:lifecycle_cc", "@googletest//:gtest_main", ], ) From 0246a160ba3bf032a7900c1f19b90e6c8b92ce96 Mon Sep 17 00:00:00 2001 From: Lorant Lipcsei Date: Mon, 15 Jun 2026 14:27:20 +0100 Subject: [PATCH 19/20] AZB 97324 Fixing the config for process_complex_rep_failure --- .../process_complex_rep_failure.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.json b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.json index 5038e24fe..1d1bac8c9 100644 --- a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.json +++ b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.json @@ -77,7 +77,7 @@ }, "component_does_not_report_krunning_in_time": { "component_properties": { - "binary_name": "process_complex_reporting", + "binary_name": "complex_reporting_process", "process_arguments": [ "1500.0" ] From 7ea69fe2f5921950cd7c9bc3d1ae818d9d6acf63 Mon Sep 17 00:00:00 2001 From: Lorant Lipcsei Date: Tue, 16 Jun 2026 09:23:52 +0100 Subject: [PATCH 20/20] Fixing formatting (#238) --- tests/integration/crash_on_startup/crash_on_startup.py | 8 +++----- .../process_complex_rep_failure.py | 6 ++++-- tests/integration/process_simple_rep_failure/BUILD | 2 +- .../process_simple_rep_failure.py | 10 +++++----- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/integration/crash_on_startup/crash_on_startup.py b/tests/integration/crash_on_startup/crash_on_startup.py index 2cd9063e7..b6670dbd8 100644 --- a/tests/integration/crash_on_startup/crash_on_startup.py +++ b/tests/integration/crash_on_startup/crash_on_startup.py @@ -18,12 +18,10 @@ @add_test_properties( fully_verifies=[ - "feat_req__lifecycle__failure_detect", - "feat_req__lifecycle__recov_run_target_switch" - ], - partially_verifies=[ - "feat_req__lifecycle__recovery_action_support" + "feat_req__lifecycle__failure_detect", + "feat_req__lifecycle__recov_run_target_switch", ], + partially_verifies=["feat_req__lifecycle__recovery_action_support"], test_type="requirements-based", derivation_technique="requirements-analysis", ) diff --git a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py index bbf3c5a43..b3b5753fe 100644 --- a/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py +++ b/tests/integration/process_complex_rep_failure/process_complex_rep_failure.py @@ -19,7 +19,7 @@ @add_test_properties( partially_verifies=[ "feat_req__lifecycle__recov_run_target_switch", - "feat_req__lifecycle__recovery_action_support" + "feat_req__lifecycle__recovery_action_support", ], fully_verifies=[ "feat_req__lifecycle__failure_detect", @@ -27,7 +27,9 @@ test_type="requirements-based", derivation_technique="requirements-analysis", ) -def test_recovery_action_complex_rep_failure(target, setup_test, assert_test_results, remote_test_dir): +def test_recovery_action_complex_rep_failure( + target, setup_test, assert_test_results, remote_test_dir +): """ Objective: Verifies that recovery action is executed when the reporting of kRunning via mw::lifecycle library (named "complex reporting" in the following) is not happening in time and vice versa. diff --git a/tests/integration/process_simple_rep_failure/BUILD b/tests/integration/process_simple_rep_failure/BUILD index 3748f7d54..fae8ce6f4 100644 --- a/tests/integration/process_simple_rep_failure/BUILD +++ b/tests/integration/process_simple_rep_failure/BUILD @@ -45,8 +45,8 @@ cc_binary( pkg_files( name = "process_simple_rep_failure_main_files", srcs = [ - ":process_simple_reporting", ":control_client_mock", + ":process_simple_reporting", "//score/launch_manager", "//tests/utils/test_helper:verification_process", ], diff --git a/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py b/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py index 868ac1b50..77cfc5e5f 100644 --- a/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py +++ b/tests/integration/process_simple_rep_failure/process_simple_rep_failure.py @@ -19,7 +19,7 @@ @add_test_properties( partially_verifies=[ "feat_req__lifecycle__recov_run_target_switch", - "feat_req__lifecycle__recovery_action_support" + "feat_req__lifecycle__recovery_action_support", ], fully_verifies=[ "feat_req__lifecycle__failure_detect", @@ -27,7 +27,9 @@ test_type="requirements-based", derivation_technique="requirements-analysis", ) -def test_recovery_action_simple_rep_failure(target, setup_test, assert_test_results, remote_test_dir): +def test_recovery_action_simple_rep_failure( + target, setup_test, assert_test_results, remote_test_dir +): """ Objective: Verifies that recovery action is executed when the reporting of kRunning via LifecycleClient API (named "simple reporting" in the following) is not happening in time and vice versa. @@ -47,6 +49,4 @@ def test_recovery_action_simple_rep_failure(target, setup_test, assert_test_resu timeout_s=10.0, ) - assert_test_results( - {"control_client_mock.xml", "process_simple_reporting.xml"} - ) + assert_test_results({"control_client_mock.xml", "process_simple_reporting.xml"})