diff --git a/score/mw/com/impl/traits.h b/score/mw/com/impl/traits.h index 891933ee5..653270ef4 100644 --- a/score/mw/com/impl/traits.h +++ b/score/mw/com/impl/traits.h @@ -142,7 +142,7 @@ class ProxyTrait // Note : at the moment the SkeletonField::Get implementation is not in the branch which means the skeleton and // proxy side does not have same template parameters. template - using Field = ProxyField; + using Field = ProxyField; template using Method = ProxyMethod; @@ -164,7 +164,7 @@ class SkeletonTrait template using Event = SkeletonEvent; - template + template using Field = SkeletonField; template diff --git a/score/mw/com/impl/traits_test.cpp b/score/mw/com/impl/traits_test.cpp index 975653d5f..85913f373 100644 --- a/score/mw/com/impl/traits_test.cpp +++ b/score/mw/com/impl/traits_test.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -69,10 +70,13 @@ class MyInterface : public InterfaceTrait::Base public: using InterfaceTrait::Base::Base; + static constexpr bool kEnableSet{std::is_same_v}; + typename InterfaceTrait::template Event some_event{*this, kEventName}; - typename InterfaceTrait::template Field some_field{*this, kFieldName}; + typename InterfaceTrait::template Field some_field{*this, kFieldName}; typename InterfaceTrait::template Method some_method{*this, kMethodName}; }; + using MyProxy = AsProxy; using MySkeleton = AsSkeleton; diff --git a/score/mw/com/test/common_test_resources/BUILD b/score/mw/com/test/common_test_resources/BUILD index 8b96b95b8..73ffefe8f 100644 --- a/score/mw/com/test/common_test_resources/BUILD +++ b/score/mw/com/test/common_test_resources/BUILD @@ -137,6 +137,22 @@ cc_library( ], ) +cc_library( + name = "process_synchronizer", + srcs = ["process_synchronizer.cpp"], + hdrs = ["process_synchronizer.h"], + features = COMPILER_WARNING_FEATURES, + visibility = ["//score/mw/com/test:__subpackages__"], + deps = [ + "//score/mw/com/test/common_test_resources:fail_test", + "//score/mw/com/test/common_test_resources:shared_memory_object_creator", + "//score/mw/com/test/common_test_resources:shared_memory_object_guard", + "@score_baselibs//score/mw/log", + "@score_baselibs//score/os/utils/interprocess:interprocess_notification", + "@score_baselibs//score/result", + ], +) + cc_library( name = "stop_token_sig_term_handler", srcs = [ diff --git a/score/mw/com/test/methods/methods_test_resources/process_synchronizer.cpp b/score/mw/com/test/common_test_resources/process_synchronizer.cpp similarity index 97% rename from score/mw/com/test/methods/methods_test_resources/process_synchronizer.cpp rename to score/mw/com/test/common_test_resources/process_synchronizer.cpp index d8b82aece..81d3142b0 100644 --- a/score/mw/com/test/methods/methods_test_resources/process_synchronizer.cpp +++ b/score/mw/com/test/common_test_resources/process_synchronizer.cpp @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#include "score/mw/com/test/methods/methods_test_resources/process_synchronizer.h" +#include "score/mw/com/test/common_test_resources/process_synchronizer.h" #include "score/mw/com/test/common_test_resources/fail_test.h" #include "score/mw/com/test/common_test_resources/shared_memory_object_creator.h" diff --git a/score/mw/com/test/methods/methods_test_resources/process_synchronizer.h b/score/mw/com/test/common_test_resources/process_synchronizer.h similarity index 88% rename from score/mw/com/test/methods/methods_test_resources/process_synchronizer.h rename to score/mw/com/test/common_test_resources/process_synchronizer.h index 9430db8cf..758c3570f 100644 --- a/score/mw/com/test/methods/methods_test_resources/process_synchronizer.h +++ b/score/mw/com/test/common_test_resources/process_synchronizer.h @@ -10,8 +10,8 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef SCORE_MW_COM_TEST_METHODS_METHODS_TEST_RESOURCES_PROCESS_SYNCHRONIZER_H -#define SCORE_MW_COM_TEST_METHODS_METHODS_TEST_RESOURCES_PROCESS_SYNCHRONIZER_H +#ifndef SCORE_MW_COM_TEST_COMMON_TEST_RESOURCES_PROCESS_SYNCHRONIZER_H +#define SCORE_MW_COM_TEST_COMMON_TEST_RESOURCES_PROCESS_SYNCHRONIZER_H #include "score/mw/com/test/common_test_resources/shared_memory_object_creator.h" #include "score/mw/com/test/common_test_resources/shared_memory_object_guard.h" @@ -46,4 +46,4 @@ class ProcessSynchronizer } // namespace score::mw::com::test -#endif // SCORE_MW_COM_TEST_METHODS_METHODS_TEST_RESOURCES_PROCESS_SYNCHRONIZER_H +#endif // SCORE_MW_COM_TEST_COMMON_TEST_RESOURCES_PROCESS_SYNCHRONIZER_H diff --git a/score/mw/com/test/field_initial_value/service.cpp b/score/mw/com/test/field_initial_value/service.cpp deleted file mode 100644 index b17710b43..000000000 --- a/score/mw/com/test/field_initial_value/service.cpp +++ /dev/null @@ -1,88 +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 "score/mw/com/test/common_test_resources/sctf_test_runner.h" -#include "score/mw/com/test/field_initial_value/test_datatype.h" - -#include - -#include -#include -#include - -namespace score::mw::com::test -{ - -namespace -{ - -using namespace std::chrono_literals; - -int run_service(const std::chrono::milliseconds& cycle_time, const score::cpp::stop_token& stop_token) -{ - auto instance_specifier_result = InstanceSpecifier::Create(std::string{kInstanceSpecifierString}); - if (!instance_specifier_result.has_value()) - { - std::cerr << "Unable to create instance specifier, terminating\n"; - return -3; - } - auto instance_specifier = std::move(instance_specifier_result).value(); - - auto service_result = TestDataSkeleton::Create(std::move(instance_specifier)); - if (!service_result.has_value()) - { - std::cerr << "Unable to construct TestDataSkeleton: " << service_result.error() << ", bailing!\n"; - return -4; - } - - TestDataSkeleton& lola_service{service_result.value()}; - - const auto update_result = lola_service.test_field.Update(kTestValue); - if (!update_result.has_value()) - { - std::cerr << "Unable to update test field: " << update_result.error() << ", bailing!\n"; - return -6; - } - const auto offer_result = lola_service.OfferService(); - if (!offer_result.has_value()) - { - std::cerr << "Unable to offer service for TestDataSkeleton: " << offer_result.error() << ", bailing!\n"; - return -5; - } - - while (!stop_token.stop_requested()) - { - std::this_thread::sleep_for(cycle_time); - } - - lola_service.StopOfferService(); - - return 0; -} - -} // namespace - -} // namespace score::mw::com::test - -int main(int argc, const char** argv) -{ - using Parameters = score::mw::com::test::SctfTestRunner::RunParameters::Parameters; - - const std::vector allowed_parameters{Parameters::CYCLE_TIME}; - score::mw::com::test::SctfTestRunner test_runner(argc, argv, allowed_parameters); - const auto& run_parameters = test_runner.GetRunParameters(); - const auto cycle_time = run_parameters.GetCycleTime(); - const auto stop_token = test_runner.GetStopToken(); - - return score::mw::com::test::run_service(cycle_time, stop_token); -} diff --git a/score/mw/com/test/fields/BUILD b/score/mw/com/test/fields/BUILD new file mode 100644 index 000000000..ca5de742e --- /dev/null +++ b/score/mw/com/test/fields/BUILD @@ -0,0 +1,12 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* diff --git a/score/mw/com/test/field_initial_value/BUILD b/score/mw/com/test/fields/field_initial_value/BUILD similarity index 80% rename from score/mw/com/test/field_initial_value/BUILD rename to score/mw/com/test/fields/field_initial_value/BUILD index e68fbbd18..884a98ddd 100644 --- a/score/mw/com/test/field_initial_value/BUILD +++ b/score/mw/com/test/fields/field_initial_value/BUILD @@ -40,11 +40,13 @@ cc_binary( features = COMPILER_WARNING_FEATURES + [ "aborts_upon_exception", ], - visibility = ["//score/mw/com/test/field_initial_value:__pkg__"], + visibility = ["//score/mw/com/test/fields/field_initial_value:__pkg__"], deps = [ ":test_datatype", "//score/mw/com", - "//score/mw/com/test/common_test_resources:sctf_test_runner", + "//score/mw/com/test/common_test_resources:fail_test", + "//score/mw/com/test/common_test_resources:process_synchronizer", + "//score/mw/com/test/common_test_resources:stop_token_sig_term_handler", "@score_baselibs//score/mw/log", ], ) @@ -56,10 +58,11 @@ cc_binary( features = COMPILER_WARNING_FEATURES + [ "aborts_upon_exception", ], - visibility = ["//score/mw/com/test/field_initial_value:__pkg__"], + visibility = ["//score/mw/com/test/fields/field_initial_value:__pkg__"], deps = [ ":test_datatype", "//score/mw/com", + "//score/mw/com/test/common_test_resources:process_synchronizer", "//score/mw/com/test/common_test_resources:sctf_test_runner", "@score_baselibs//score/mw/log", ], @@ -75,7 +78,7 @@ pkg_application( ], visibility = [ "//platform/aas/test/mw/com:__pkg__", - "//score/mw/com/test/field_initial_value:__subpackages__", + "//score/mw/com/test/fields/field_initial_value:__subpackages__", ], ) @@ -89,6 +92,6 @@ pkg_application( ], visibility = [ "//platform/aas/test/mw/com:__pkg__", - "//score/mw/com/test/field_initial_value:__subpackages__", + "//score/mw/com/test/fields/field_initial_value:__subpackages__", ], ) diff --git a/score/mw/com/test/field_initial_value/client.cpp b/score/mw/com/test/fields/field_initial_value/client.cpp similarity index 74% rename from score/mw/com/test/field_initial_value/client.cpp rename to score/mw/com/test/fields/field_initial_value/client.cpp index b58464eab..2a4ba8735 100644 --- a/score/mw/com/test/field_initial_value/client.cpp +++ b/score/mw/com/test/fields/field_initial_value/client.cpp @@ -11,8 +11,10 @@ * SPDX-License-Identifier: Apache-2.0 *******************************************************************************/ +#include "score/mw/com/test/common_test_resources/fail_test.h" +#include "score/mw/com/test/common_test_resources/process_synchronizer.h" #include "score/mw/com/test/common_test_resources/sctf_test_runner.h" -#include "score/mw/com/test/field_initial_value/test_datatype.h" +#include "score/mw/com/test/fields/field_initial_value/test_datatype.h" #include "score/mw/com/types.h" #include @@ -21,6 +23,7 @@ #include #include #include +#include #include namespace score::mw::com::test @@ -31,15 +34,22 @@ namespace constexpr auto kMaxNumSamples{1U}; -int run_client(const std::size_t num_retries, const std::chrono::milliseconds retry_backoff_time) +const std::string kInterprocessNotificationShmPath{"/field_initial_value_interprocess_notification"}; + +void run_client(const std::size_t num_retries, const std::chrono::milliseconds retry_backoff_time) { using score::mw::com::test::TestDataProxy; + auto process_synchronizer_result = ProcessSynchronizer::Create(kInterprocessNotificationShmPath); + if (!process_synchronizer_result.has_value()) + { + FailTest("Unable to create ProcessSynchronizer"); + } + auto instance_specifier_result = InstanceSpecifier::Create(std::string{kInstanceSpecifierString}); if (!instance_specifier_result.has_value()) { - std::cerr << "Unable to create instance specifier, terminating\n"; - return -7; + FailTest("Unable to create instance specifier"); } auto instance_specifier = std::move(instance_specifier_result).value(); @@ -54,22 +64,19 @@ int run_client(const std::size_t num_retries, const std::chrono::milliseconds re if (!lola_proxy_handles_result.has_value()) { - std::cerr << "Unable to get handles, terminating\n"; - return -1; + FailTest("Unable to get handles"); } auto lola_proxy_handles = service_discovery_future.get(); if (lola_proxy_handles.empty()) { - std::cerr << "Unable to find lola service, terminating\n"; - return -2; + FailTest("Unable to find lola service"); } auto lola_proxy_result = TestDataProxy::Create(lola_proxy_handles[0]); if (!lola_proxy_result.has_value()) { - std::cerr << "Unable to create lola proxy, terminating\n"; - return -3; + FailTest("Unable to create lola proxy"); } auto& lola_proxy = lola_proxy_result.value(); score::cpp::optional received_value; @@ -82,8 +89,7 @@ int run_client(const std::size_t num_retries, const std::chrono::milliseconds re retries--; if (retries <= 0) { - std::cerr << "Subscription failed!\n"; - return -4; + FailTest("Subscription failed"); } } std::ignore = lola_proxy.test_field.GetNewSamples( @@ -96,17 +102,15 @@ int run_client(const std::size_t num_retries, const std::chrono::milliseconds re if (!received_value.has_value()) { - std::cerr << "Lola didn't receive a sample!\n"; - return -5; + FailTest("Lola didn't receive a sample"); } if (received_value.value() != kTestValue) { - std::cerr << "Expecting:" << kTestValue << " Received:" << received_value.value() << "!\n "; - return -6; + FailTest("Received value does not match expected value"); } - return 0; + process_synchronizer_result->Notify(); } } // namespace @@ -122,7 +126,7 @@ int main(int argc, const char** argv) const auto& run_parameters = test_runner.GetRunParameters(); const auto num_retries = run_parameters.GetNumRetries(); const auto retry_backoff_time = run_parameters.GetRetryBackoffTime(); - const auto stop_token = test_runner.GetStopToken(); - return score::mw::com::test::run_client(num_retries, retry_backoff_time); + score::mw::com::test::run_client(num_retries, retry_backoff_time); + return EXIT_SUCCESS; } diff --git a/score/mw/com/test/field_initial_value/config/logging.json b/score/mw/com/test/fields/field_initial_value/config/logging.json similarity index 99% rename from score/mw/com/test/field_initial_value/config/logging.json rename to score/mw/com/test/fields/field_initial_value/config/logging.json index f224f16f9..de20e5520 100644 --- a/score/mw/com/test/field_initial_value/config/logging.json +++ b/score/mw/com/test/fields/field_initial_value/config/logging.json @@ -5,4 +5,3 @@ "logLevelThresholdConsole": "kDebug", "logMode": "kRemote|kConsole" } - diff --git a/score/mw/com/test/field_initial_value/config/mw_com_config.json b/score/mw/com/test/fields/field_initial_value/config/mw_com_config.json similarity index 78% rename from score/mw/com/test/field_initial_value/config/mw_com_config.json rename to score/mw/com/test/fields/field_initial_value/config/mw_com_config.json index bbb0beba5..36b40fe8e 100644 --- a/score/mw/com/test/field_initial_value/config/mw_com_config.json +++ b/score/mw/com/test/fields/field_initial_value/config/mw_com_config.json @@ -1,7 +1,7 @@ { "serviceTypes": [ { - "serviceTypeName": "/score/mw/com/test/field_initial_value", + "serviceTypeName": "/score/mw/com/test/fields/field_initial_value", "version": { "major": 1, "minor": 0 @@ -22,8 +22,8 @@ ], "serviceInstances": [ { - "instanceSpecifier": "test/field_initial_value", - "serviceTypeName": "/score/mw/com/test/field_initial_value", + "instanceSpecifier": "test/fields/field_initial_value", + "serviceTypeName": "/score/mw/com/test/fields/field_initial_value", "version": { "major": 1, "minor": 0 @@ -45,4 +45,3 @@ } ] } - diff --git a/score/mw/com/test/field_initial_value/integration_test/BUILD b/score/mw/com/test/fields/field_initial_value/integration_test/BUILD similarity index 87% rename from score/mw/com/test/field_initial_value/integration_test/BUILD rename to score/mw/com/test/fields/field_initial_value/integration_test/BUILD index 0c75b747b..85fe56d70 100644 --- a/score/mw/com/test/field_initial_value/integration_test/BUILD +++ b/score/mw/com/test/fields/field_initial_value/integration_test/BUILD @@ -16,8 +16,8 @@ load("//quality/integration_testing:integration_testing.bzl", "integration_test" pkg_filegroup( name = "filesystem", srcs = [ - "//score/mw/com/test/field_initial_value:client-pkg", - "//score/mw/com/test/field_initial_value:service-pkg", + "//score/mw/com/test/fields/field_initial_value:client-pkg", + "//score/mw/com/test/fields/field_initial_value:service-pkg", ], ) diff --git a/score/mw/com/test/field_initial_value/integration_test/test_field_initial_value.py b/score/mw/com/test/fields/field_initial_value/integration_test/test_field_initial_value.py similarity index 96% rename from score/mw/com/test/field_initial_value/integration_test/test_field_initial_value.py rename to score/mw/com/test/fields/field_initial_value/integration_test/test_field_initial_value.py index e0dd9c991..1bf5b0f00 100644 --- a/score/mw/com/test/field_initial_value/integration_test/test_field_initial_value.py +++ b/score/mw/com/test/fields/field_initial_value/integration_test/test_field_initial_value.py @@ -18,7 +18,7 @@ def client(target, **kwargs): def service(target, **kwargs): - args = ["--cycle-time", "250"] + args = [] return target.wrap_exec("bin/service", args, cwd="/opt/ServiceApp", **kwargs) diff --git a/score/mw/com/test/fields/field_initial_value/service.cpp b/score/mw/com/test/fields/field_initial_value/service.cpp new file mode 100644 index 000000000..3c5e6ee29 --- /dev/null +++ b/score/mw/com/test/fields/field_initial_value/service.cpp @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ + +#include "score/mw/com/runtime.h" +#include "score/mw/com/test/common_test_resources/fail_test.h" +#include "score/mw/com/test/common_test_resources/process_synchronizer.h" +#include "score/mw/com/test/common_test_resources/stop_token_sig_term_handler.h" +#include "score/mw/com/test/fields/field_initial_value/test_datatype.h" + +#include + +#include +#include +#include +#include + +namespace score::mw::com::test +{ + +namespace +{ + +const std::string kInterprocessNotificationShmPath{"/field_initial_value_interprocess_notification"}; + +void run_service(const score::cpp::stop_token& stop_token) +{ + auto process_synchronizer_result = ProcessSynchronizer::Create(kInterprocessNotificationShmPath); + if (!process_synchronizer_result.has_value()) + { + FailTest("Service: Unable to create ProcessSynchronizer"); + } + + auto instance_specifier_result = InstanceSpecifier::Create(std::string{kInstanceSpecifierString}); + if (!instance_specifier_result.has_value()) + { + FailTest("Service: Unable to create instance specifier"); + } + auto instance_specifier = std::move(instance_specifier_result).value(); + + auto service_result = TestDataSkeleton::Create(std::move(instance_specifier)); + if (!service_result.has_value()) + { + FailTest("Service: Unable to construct TestDataSkeleton: ", service_result.error()); + } + + TestDataSkeleton& lola_service{service_result.value()}; + + const auto update_result = lola_service.test_field.Update(kTestValue); + if (!update_result.has_value()) + { + FailTest("Service: Unable to update test field: ", update_result.error()); + } + const auto offer_result = lola_service.OfferService(); + if (!offer_result.has_value()) + { + FailTest("Service: Unable to offer service for TestDataSkeleton: ", offer_result.error()); + } + + if (!process_synchronizer_result->WaitWithAbort(stop_token)) + { + FailTest("Service: WaitWithAbort was stopped by stop_token instead of notification"); + } + + lola_service.StopOfferService(); +} + +} // namespace + +} // namespace score::mw::com::test + +int main(int argc, const char** argv) +{ + score::mw::com::runtime::InitializeRuntime(argc, argv); + + score::cpp::stop_source stop_source{}; + const bool sig_term_handler_setup_success = score::mw::com::SetupStopTokenSigTermHandler(stop_source); + if (!sig_term_handler_setup_success) + { + std::cerr << "Unable to set signal handler for SIGINT and/or SIGTERM, cautiously continuing\n"; + } + + score::mw::com::test::run_service(stop_source.get_token()); + return EXIT_SUCCESS; +} diff --git a/score/mw/com/test/field_initial_value/test_datatype.cpp b/score/mw/com/test/fields/field_initial_value/test_datatype.cpp similarity index 88% rename from score/mw/com/test/field_initial_value/test_datatype.cpp rename to score/mw/com/test/fields/field_initial_value/test_datatype.cpp index ab6b0f1ac..3a2a65351 100644 --- a/score/mw/com/test/field_initial_value/test_datatype.cpp +++ b/score/mw/com/test/fields/field_initial_value/test_datatype.cpp @@ -11,4 +11,4 @@ * SPDX-License-Identifier: Apache-2.0 *******************************************************************************/ -#include "score/mw/com/test/field_initial_value/test_datatype.h" +#include "score/mw/com/test/fields/field_initial_value/test_datatype.h" diff --git a/score/mw/com/test/field_initial_value/test_datatype.h b/score/mw/com/test/fields/field_initial_value/test_datatype.h similarity index 82% rename from score/mw/com/test/field_initial_value/test_datatype.h rename to score/mw/com/test/fields/field_initial_value/test_datatype.h index 3f7348692..42d0278bf 100644 --- a/score/mw/com/test/field_initial_value/test_datatype.h +++ b/score/mw/com/test/fields/field_initial_value/test_datatype.h @@ -11,8 +11,8 @@ * SPDX-License-Identifier: Apache-2.0 *******************************************************************************/ -#ifndef SCORE_MW_COM_TEST_FIELD_INITIAL_VALUE_TEST_DATATYPE_H -#define SCORE_MW_COM_TEST_FIELD_INITIAL_VALUE_TEST_DATATYPE_H +#ifndef SCORE_MW_COM_TEST_FIELDS_FIELD_INITIAL_VALUE_TEST_DATATYPE_H +#define SCORE_MW_COM_TEST_FIELDS_FIELD_INITIAL_VALUE_TEST_DATATYPE_H #include "score/mw/com/types.h" @@ -22,7 +22,7 @@ namespace score::mw::com::test { -constexpr const char* const kInstanceSpecifierString = "test/field_initial_value"; +constexpr const char* const kInstanceSpecifierString = "test/fields/field_initial_value"; const std::int32_t kTestValue = 18; template @@ -39,4 +39,4 @@ using TestDataSkeleton = score::mw::com::AsSkeleton; } // namespace score::mw::com::test -#endif // SCORE_MW_COM_TEST_FIELD_RECEIVE_HANDLER_SRC_LOLA_H +#endif // SCORE_MW_COM_TEST_FIELDS_FIELD_INITIAL_VALUE_TEST_DATATYPE_H diff --git a/score/mw/com/test/fields/set_and_notifier/BUILD b/score/mw/com/test/fields/set_and_notifier/BUILD new file mode 100644 index 000000000..fb866313b --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/BUILD @@ -0,0 +1,102 @@ +# ******************************************************************************* +# 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_cc//cc:defs.bzl", "cc_binary") +load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") +load("//bazel/tools:json_schema_validator.bzl", "validate_json_schema_test") +load("//score/mw/com/test:pkg_application.bzl", "pkg_application") + +validate_json_schema_test( + name = "validate_lola_schema", + json = "config/mw_com_config.json", + schema = "//score/mw/com:config_schema", + tags = ["lint"], +) + +validate_json_schema_test( + name = "validate_initial_only_field_lola_schema", + json = "config/initial_only_field_mw_com_config.json", + schema = "//score/mw/com:config_schema", + tags = ["lint"], +) + +cc_binary( + name = "provider", + srcs = ["main_provider.cpp"], + data = [ + "config/initial_only_field_mw_com_config.json", + "config/mw_com_config.json", + ], + features = COMPILER_WARNING_FEATURES + [ + "aborts_upon_exception", + ], + visibility = ["//score/mw/com/test/fields:__pkg__"], + deps = [ + "//score/mw/com", + "//score/mw/com/test/common_test_resources:command_line_parser", + "//score/mw/com/test/common_test_resources:fail_test", + "//score/mw/com/test/common_test_resources:stop_token_sig_term_handler", + "//score/mw/com/test/fields/set_and_notifier/fields_test_resources:field_provider", + "@score_baselibs//score/mw/log", + ], +) + +cc_binary( + name = "consumer", + srcs = ["main_consumer.cpp"], + data = [ + "config/initial_only_field_mw_com_config.json", + "config/mw_com_config.json", + ], + features = COMPILER_WARNING_FEATURES + [ + "aborts_upon_exception", + ], + visibility = ["//score/mw/com/test/fields:__pkg__"], + deps = [ + "//score/mw/com", + "//score/mw/com/test/common_test_resources:command_line_parser", + "//score/mw/com/test/common_test_resources:fail_test", + "//score/mw/com/test/fields/set_and_notifier/fields_test_resources:field_consumer", + "@score_baselibs//score/mw/log", + ], +) + +pkg_application( + name = "provider-pkg", + app_name = "MainProviderApp", + bin = [":provider"], + etc = [ + "config/initial_only_field_mw_com_config.json", + "config/logging.json", + "config/mw_com_config.json", + ], + visibility = [ + "//platform/aas/test/mw/com:__pkg__", + "//score/mw/com/test/fields/set_and_notifier:__subpackages__", + ], +) + +pkg_application( + name = "consumer-pkg", + app_name = "MainConsumerApp", + bin = [":consumer"], + etc = [ + "config/initial_only_field_mw_com_config.json", + "config/logging.json", + "config/mw_com_config.json", + ], + visibility = [ + "//platform/aas/test/mw/com:__pkg__", + "//score/mw/com/test/fields/set_and_notifier:__subpackages__", + ], +) diff --git a/score/mw/com/test/fields/set_and_notifier/config/initial_only_field_mw_com_config.json b/score/mw/com/test/fields/set_and_notifier/config/initial_only_field_mw_com_config.json new file mode 100644 index 000000000..d93820845 --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/config/initial_only_field_mw_com_config.json @@ -0,0 +1,47 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/score/mw/com/test/fields/set_and_notifier", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 3427, + "fields": [ + { + "fieldName": "initial_only_field", + "fieldId": 1 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "test/fields/set_and_notifier", + "serviceTypeName": "/score/mw/com/test/fields/set_and_notifier", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1023, + "asil-level": "QM", + "binding": "SHM", + "fields": [ + { + "fieldName": "initial_only_field", + "maxSubscribers": 1, + "numberOfSampleSlots": 1 + } + ] + } + ] + } + ] +} diff --git a/score/mw/com/test/fields/set_and_notifier/config/logging.json b/score/mw/com/test/fields/set_and_notifier/config/logging.json new file mode 100644 index 000000000..a49f8cded --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/config/logging.json @@ -0,0 +1,7 @@ +{ + "appId": "FSAN", + "appDesc": "field_set_and_notifier", + "logLevel": "kDebug", + "logLevelThresholdConsole": "kDebug", + "logMode": "kRemote|kConsole" +} diff --git a/score/mw/com/test/fields/set_and_notifier/config/mw_com_config.json b/score/mw/com/test/fields/set_and_notifier/config/mw_com_config.json new file mode 100644 index 000000000..766a20bb2 --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/config/mw_com_config.json @@ -0,0 +1,48 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/score/mw/com/test/fields/set_and_notifier", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 3428, + "fields": [ + { + "fieldName": "set_enabled_field", + "fieldId": 1, + "Set": true + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "test/fields/set_and_notifier", + "serviceTypeName": "/score/mw/com/test/fields/set_and_notifier", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1024, + "asil-level": "QM", + "binding": "SHM", + "fields": [ + { + "fieldName": "set_enabled_field", + "maxSubscribers": 1, + "numberOfSampleSlots": 1 + } + ] + } + ] + } + ] +} diff --git a/score/mw/com/test/fields/set_and_notifier/fields_test_resources/BUILD b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/BUILD new file mode 100644 index 000000000..fbc0e14eb --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/BUILD @@ -0,0 +1,70 @@ +# ******************************************************************************* +# 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_cc//cc:defs.bzl", "cc_library") +load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") + +cc_library( + name = "field_types", + srcs = [ + "initial_only_field.cpp", + "set_enabled_field.cpp", + "test_constants.cpp", + ], + hdrs = [ + "initial_only_field.h", + "set_enabled_field.h", + "test_constants.h", + ], + features = COMPILER_WARNING_FEATURES, + visibility = [ + "//score/mw/com/test/fields/set_and_notifier:__subpackages__", + ], + deps = [ + "//score/mw/com", + ], +) + +cc_library( + name = "field_consumer", + srcs = ["field_consumer.cpp"], + hdrs = ["field_consumer.h"], + features = COMPILER_WARNING_FEATURES, + visibility = [ + "//score/mw/com/test/fields/set_and_notifier:__subpackages__", + ], + deps = [ + ":field_types", + "//score/mw/com", + "//score/mw/com/test/common_test_resources:fail_test", + "//score/mw/com/test/common_test_resources:process_synchronizer", + "//score/mw/com/test/common_test_resources:proxy_container", + ], +) + +cc_library( + name = "field_provider", + srcs = ["field_provider.cpp"], + hdrs = ["field_provider.h"], + features = COMPILER_WARNING_FEATURES, + visibility = [ + "//score/mw/com/test/fields/set_and_notifier:__subpackages__", + ], + deps = [ + ":field_types", + "//score/mw/com", + "//score/mw/com/test/common_test_resources:fail_test", + "//score/mw/com/test/common_test_resources:process_synchronizer", + "//score/mw/com/test/common_test_resources:skeleton_container", + ], +) diff --git a/score/mw/com/test/fields/set_and_notifier/fields_test_resources/field_consumer.cpp b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/field_consumer.cpp new file mode 100644 index 000000000..083593a22 --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/field_consumer.cpp @@ -0,0 +1,347 @@ +/******************************************************************************* + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ + +#include "score/mw/com/test/fields/set_and_notifier/fields_test_resources/field_consumer.h" + +#include "score/mw/com/test/common_test_resources/fail_test.h" +#include "score/mw/com/test/common_test_resources/process_synchronizer.h" +#include "score/mw/com/test/common_test_resources/proxy_container.h" +#include "score/mw/com/test/fields/set_and_notifier/fields_test_resources/initial_only_field.h" +#include "score/mw/com/test/fields/set_and_notifier/fields_test_resources/set_enabled_field.h" +#include "score/mw/com/test/fields/set_and_notifier/fields_test_resources/test_constants.h" +#include "score/mw/com/types.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace score::mw::com::test +{ +namespace +{ + +constexpr auto kMaxNumSamples{1U}; +const std::string kInterprocessNotificationShmPath{"/fields_set_and_notifier_interprocess_notification"}; +const std::string kNotifierConsumerReadyShmPath{"/fields_notifier_consumer_ready"}; + +// InstanceSpecifier::Create can only fail if the provided string is invalid. +// Verified once here; all test functions reuse this constant. +const InstanceSpecifier kInstanceSpecifier = InstanceSpecifier::Create(std::string{kInstanceSpecifierString}).value(); + +template +bool WaitForSubscription(FieldType& field, std::size_t retries, const std::chrono::milliseconds retry_backoff_time) +{ + std::mutex mutex; + std::condition_variable cv; + bool subscribed{false}; + + const auto handler_result = field.SetSubscriptionStateChangeHandler( + [&mutex, &cv, &subscribed](score::mw::com::impl::SubscriptionState new_state) noexcept -> bool { + if (new_state == score::mw::com::impl::SubscriptionState::kSubscribed) + { + { + std::lock_guard lock{mutex}; + subscribed = true; + } + cv.notify_one(); + return false; + } + return true; + }); + + if (!handler_result.has_value()) + { + return false; + } + + // subscription may have become kSubscribed before the handler was registered + if (field.GetSubscriptionState() == score::mw::com::impl::SubscriptionState::kSubscribed) + { + std::ignore = field.UnsetSubscriptionStateChangeHandler(); + return true; + } + + const auto total_wait_time = retry_backoff_time * retries; + std::unique_lock lock{mutex}; + const bool success = cv.wait_for(lock, total_wait_time, [&subscribed] { + return subscribed; + }); + if (!success) + { + std::ignore = field.UnsetSubscriptionStateChangeHandler(); + } + return success; +} + +template +bool WaitForValue(FieldType& field, + const std::int32_t expected_value, + std::size_t retries, + const std::chrono::milliseconds retry_backoff_time) +{ + struct SyncState + { + std::mutex mutex{}; + std::condition_variable cv{}; + bool value_received{false}; + }; + SyncState sync_state; + + const auto handler_result = field.SetReceiveHandler([&field, &sync_state, expected_value]() noexcept { + score::cpp::optional received; + std::ignore = field.GetNewSamples( + [&received](const auto& sample_ptr) noexcept { + received = *sample_ptr; + }, + kMaxNumSamples); + if (received.has_value() && received.value() == expected_value) + { + { + std::lock_guard lock{sync_state.mutex}; + sync_state.value_received = true; + } + sync_state.cv.notify_one(); + } + }); + + if (!handler_result.has_value()) + { + return false; + } + + // Handle race: value may already be in the buffer before the handler was registered + { + score::cpp::optional existing; + std::ignore = field.GetNewSamples( + [&existing](const auto& sample_ptr) noexcept { + existing = *sample_ptr; + }, + kMaxNumSamples); + if (existing.has_value() && existing.value() == expected_value) + { + std::ignore = field.UnsetReceiveHandler(); + return true; + } + } + + const auto total_wait_time = retry_backoff_time * retries; + bool success; + { + std::unique_lock lock{sync_state.mutex}; + success = sync_state.cv.wait_for(lock, total_wait_time, [&sync_state] { + return sync_state.value_received; + }); + } + std::ignore = field.UnsetReceiveHandler(); + return success; +} + +} // namespace + +void run_notifier_consumer(const std::size_t num_retries, const std::chrono::milliseconds retry_backoff_time) +{ + auto consumer_ready_synchronizer_result = ProcessSynchronizer::Create(kNotifierConsumerReadyShmPath); + if (!consumer_ready_synchronizer_result.has_value()) + { + FailTest("Consumer: Could not create consumer ready ProcessSynchronizer"); + } + auto& consumer_ready_synchronizer = consumer_ready_synchronizer_result.value(); + + auto done_synchronizer_result = ProcessSynchronizer::Create(kInterprocessNotificationShmPath); + if (!done_synchronizer_result.has_value()) + { + FailTest("Consumer: Could not create done ProcessSynchronizer"); + } + ExitFunctionGuard exit_guard{[&consumer_ready_synchronizer_result, &done_synchronizer_result]() { + consumer_ready_synchronizer_result->Notify(); + done_synchronizer_result->Notify(); + }}; + + // Step 1. Find service and create proxy + std::cout << "\nConsumer: Step 1" << std::endl; + ProxyContainer proxy_container{}; + proxy_container.CreateProxy(kInstanceSpecifier, "set_and_notifier"); + auto& proxy = proxy_container.GetProxy(); + + // Step 2. Subscribe to field and wait for subscription + std::cout << "\nConsumer: Step 2" << std::endl; + std::ignore = proxy.initial_only_field.Subscribe(kMaxNumSamples); + if (!WaitForSubscription(proxy.initial_only_field, num_retries, retry_backoff_time)) + { + FailTest("Consumer: Subscription failed in notifier scenario"); + } + + // Step 3. Verify initial value received + std::cout << "\nConsumer: Step 3" << std::endl; + const bool initial_value_received = + WaitForValue(proxy.initial_only_field, kInitialValue, num_retries, retry_backoff_time); + if (!initial_value_received) + { + FailTest("Consumer: Did not receive expected initial value ", kInitialValue, " in notifier scenario"); + } + + // Step 4. Register receive handler for updated value before notifying provider, to avoid missing the + // notification if the provider calls Update() before the handler is registered. + std::cout << "\nConsumer: Step 4" << std::endl; + struct SyncState + { + std::mutex mutex{}; + std::condition_variable cv{}; + bool value_received{false}; + }; + SyncState sync_state; + + const auto handler_result = proxy.initial_only_field.SetReceiveHandler([&proxy, &sync_state]() noexcept { + score::cpp::optional received; + std::ignore = proxy.initial_only_field.GetNewSamples( + [&received](const auto& sample_ptr) noexcept { + received = *sample_ptr; + }, + kMaxNumSamples); + if (received.has_value() && received.value() == kUpdatedValue) + { + { + std::lock_guard lock{sync_state.mutex}; + sync_state.value_received = true; + } + sync_state.cv.notify_one(); + } + }); + if (!handler_result.has_value()) + { + FailTest("Consumer: Could not register receive handler for updated value in notifier scenario"); + } + + consumer_ready_synchronizer.Notify(); + + // Step 5. Verify updated value received after provider publishes update + std::cout << "\nConsumer: Step 5" << std::endl; + + // Handle race: value may already be in the buffer before Notify() returned + bool updated_value_received = false; + { + score::cpp::optional existing; + std::ignore = proxy.initial_only_field.GetNewSamples( + [&existing](const auto& sample_ptr) noexcept { + existing = *sample_ptr; + }, + kMaxNumSamples); + if (existing.has_value() && existing.value() == kUpdatedValue) + { + updated_value_received = true; + } + } + + if (!updated_value_received) + { + const auto total_wait_time = retry_backoff_time * num_retries; + std::unique_lock lock{sync_state.mutex}; + updated_value_received = sync_state.cv.wait_for(lock, total_wait_time, [&sync_state] { + return sync_state.value_received; + }); + } + + std::ignore = proxy.initial_only_field.UnsetReceiveHandler(); + proxy.initial_only_field.Unsubscribe(); + + if (!updated_value_received) + { + FailTest("Consumer: Did not receive expected updated value ", kUpdatedValue, " in notifier scenario"); + } +} + +void run_set_and_notifier_consumer(const std::size_t num_retries, const std::chrono::milliseconds retry_backoff_time) +{ + auto process_synchronizer_result = ProcessSynchronizer::Create(kInterprocessNotificationShmPath); + if (!process_synchronizer_result.has_value()) + { + FailTest("Consumer: Could not create ProcessSynchronizer"); + } + ExitFunctionGuard process_synchronizer_guard{[&process_synchronizer_result]() { + process_synchronizer_result->Notify(); + }}; + + // Step 1. Find service and create proxy + std::cout << "\nConsumer: Step 1" << std::endl; + ProxyContainer proxy_container{}; + proxy_container.CreateProxy(kInstanceSpecifier, "set_and_notifier"); + auto& proxy = proxy_container.GetProxy(); + + // Step 2. Subscribe to field and wait for subscription + std::cout << "\nConsumer: Step 2" << std::endl; + std::ignore = proxy.set_enabled_field.Subscribe(kMaxNumSamples); + if (!WaitForSubscription(proxy.set_enabled_field, num_retries, retry_backoff_time)) + { + FailTest("Consumer: Subscription failed in set scenario"); + } + + // Step 3. Verify initial value received + std::cout << "\nConsumer: Step 3" << std::endl; + const bool initial_value_received = + WaitForValue(proxy.set_enabled_field, kInitialValue, num_retries, retry_backoff_time); + if (!initial_value_received) + { + FailTest("Consumer: Did not receive initial value ", kInitialValue, " in set scenario"); + } + + // Step 4. Set new field value and verify accepted value matches expected transformed value + std::cout << "\nConsumer: Step 4" << std::endl; + const auto set_result = proxy.set_enabled_field.Set(kSetRequestValue); + if (!set_result.has_value()) + { + FailTest("Consumer: Set call failed: ", set_result.error()); + } + const std::int32_t accepted_value = *(set_result.value()); + + if (accepted_value != kSetTransformedValue) + { + FailTest("Consumer: Set accepted value mismatch. Expected ", kSetTransformedValue, " but got ", accepted_value); + } + + // Step 5. Verify transformed value received via field notification + std::cout << "\nConsumer: Step 5" << std::endl; + const bool transformed_value_received = + WaitForValue(proxy.set_enabled_field, kSetTransformedValue, num_retries, retry_backoff_time); + proxy.set_enabled_field.Unsubscribe(); + + if (!transformed_value_received) + { + FailTest("Consumer: Did not receive transformed value ", kSetTransformedValue, " after Set call"); + } +} + +void run_consumer(const std::size_t num_retries, + const std::chrono::milliseconds retry_backoff_time, + const std::string& mode) +{ + if (mode == "notifier") + { + run_notifier_consumer(num_retries, retry_backoff_time); + return; + } + if (mode == "set_and_notifier") + { + run_set_and_notifier_consumer(num_retries, retry_backoff_time); + return; + } + + // TODO: Add "get" mode consumer scenario coverage once getter-enabled field variant is introduced. + FailTest("Consumer: Unsupported mode: ", mode); +} +} // namespace score::mw::com::test diff --git a/score/mw/com/test/fields/set_and_notifier/fields_test_resources/field_consumer.h b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/field_consumer.h new file mode 100644 index 000000000..0aa22fd85 --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/field_consumer.h @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ + +#ifndef SCORE_MW_COM_TEST_FIELDS_SET_AND_NOTIFIER_FIELDS_TEST_RESOURCES_FIELD_CONSUMER_H +#define SCORE_MW_COM_TEST_FIELDS_SET_AND_NOTIFIER_FIELDS_TEST_RESOURCES_FIELD_CONSUMER_H + +#include +#include +#include + +namespace score::mw::com::test +{ + +void run_consumer(std::size_t num_retries, std::chrono::milliseconds retry_backoff_time, const std::string& mode); + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_FIELDS_SET_AND_NOTIFIER_FIELDS_TEST_RESOURCES_FIELD_CONSUMER_H diff --git a/score/mw/com/test/fields/set_and_notifier/fields_test_resources/field_provider.cpp b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/field_provider.cpp new file mode 100644 index 000000000..0b1d5efeb --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/field_provider.cpp @@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ + +#include "score/mw/com/test/fields/set_and_notifier/fields_test_resources/field_provider.h" + +#include "score/mw/com/test/common_test_resources/fail_test.h" +#include "score/mw/com/test/common_test_resources/process_synchronizer.h" +#include "score/mw/com/test/common_test_resources/skeleton_container.h" +#include "score/mw/com/test/fields/set_and_notifier/fields_test_resources/initial_only_field.h" +#include "score/mw/com/test/fields/set_and_notifier/fields_test_resources/set_enabled_field.h" +#include "score/mw/com/test/fields/set_and_notifier/fields_test_resources/test_constants.h" + +#include +#include +#include +#include + +namespace score::mw::com::test +{ +namespace +{ + +const std::string kInterprocessNotificationShmPath{"/fields_set_and_notifier_interprocess_notification"}; +const std::string kNotifierConsumerReadyShmPath{"/fields_notifier_consumer_ready"}; + +// InstanceSpecifier::Create can only fail if the provided string is invalid. +// Verified once here; all test functions reuse this constant. +const InstanceSpecifier kInstanceSpecifier = InstanceSpecifier::Create(std::string{kInstanceSpecifierString}).value(); + +void run_notifier_provider(const score::cpp::stop_token& stop_token) +{ + auto consumer_ready_synchronizer_result = ProcessSynchronizer::Create(kNotifierConsumerReadyShmPath); + if (!consumer_ready_synchronizer_result.has_value()) + { + FailTest("Provider: Could not create consumer ready ProcessSynchronizer"); + } + + auto done_synchronizer_result = ProcessSynchronizer::Create(kInterprocessNotificationShmPath); + if (!done_synchronizer_result.has_value()) + { + FailTest("Provider: Could not create done ProcessSynchronizer"); + } + + SkeletonContainer skeleton_container{}; + skeleton_container.CreateSkeleton(kInstanceSpecifier, "set_and_notifier"); + + auto& service = skeleton_container.GetSkeleton(); + + { + const auto update_result = service.initial_only_field.Update(kInitialValue); + if (!update_result.has_value()) + { + FailTest("Provider: Unable to update initial field value: ", update_result.error()); + } + } + skeleton_container.OfferService("set_and_notifier"); + + if (!consumer_ready_synchronizer_result->WaitWithAbort(stop_token)) + { + FailTest("Provider: WaitWithAbort (consumer ready) was stopped by stop_token instead of notification"); + } + + { + const auto update_result = service.initial_only_field.Update(kUpdatedValue); + if (!update_result.has_value()) + { + FailTest("Provider: Unable to update field with updated value: ", update_result.error()); + } + } + + if (!done_synchronizer_result->WaitWithAbort(stop_token)) + { + FailTest("Provider: WaitWithAbort (done) was stopped by stop_token instead of notification"); + } + + service.StopOfferService(); +} + +void run_set_and_notifier_provider(const score::cpp::stop_token& stop_token) +{ + auto process_synchronizer_result = ProcessSynchronizer::Create(kInterprocessNotificationShmPath); + if (!process_synchronizer_result.has_value()) + { + FailTest("Provider: Could not create ProcessSynchronizer"); + } + + SkeletonContainer skeleton_container{}; + skeleton_container.CreateSkeleton(kInstanceSpecifier, "set_and_notifier"); + + auto& service = skeleton_container.GetSkeleton(); + const auto register_handler_result = service.set_enabled_field.RegisterSetHandler([](std::int32_t& value) noexcept { + value = value * 2 + 1; + }); + if (!register_handler_result.has_value()) + { + FailTest("Provider: Unable to register set handler: ", register_handler_result.error()); + } + + const auto update_result = service.set_enabled_field.Update(kInitialValue); + if (!update_result.has_value()) + { + FailTest("Provider: Unable to update initial field value: ", update_result.error()); + } + + skeleton_container.OfferService("set_and_notifier"); + + if (!process_synchronizer_result->WaitWithAbort(stop_token)) + { + FailTest("Provider: WaitWithAbort was stopped by stop_token instead of notification"); + } + + service.StopOfferService(); +} + +} // namespace + +void run_provider(const score::cpp::stop_token& stop_token, const std::string& mode) +{ + if (mode == "notifier") + { + run_notifier_provider(stop_token); + return; + } + if (mode == "set_and_notifier") + { + run_set_and_notifier_provider(stop_token); + return; + } + + // TODO: Add "get" mode provider scenario coverage once getter-enabled field variant is introduced. + FailTest("Provider: Unsupported mode: ", mode); +} + +} // namespace score::mw::com::test diff --git a/score/mw/com/test/fields/set_and_notifier/fields_test_resources/field_provider.h b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/field_provider.h new file mode 100644 index 000000000..95a5dc32f --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/field_provider.h @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ + +#ifndef SCORE_MW_COM_TEST_FIELDS_SET_AND_NOTIFIER_FIELDS_TEST_RESOURCES_FIELD_PROVIDER_H +#define SCORE_MW_COM_TEST_FIELDS_SET_AND_NOTIFIER_FIELDS_TEST_RESOURCES_FIELD_PROVIDER_H + +#include + +#include + +namespace score::mw::com::test +{ + +void run_provider(const score::cpp::stop_token& stop_token, const std::string& mode); + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_FIELDS_SET_AND_NOTIFIER_FIELDS_TEST_RESOURCES_FIELD_PROVIDER_H diff --git a/score/mw/com/test/fields/set_and_notifier/fields_test_resources/initial_only_field.cpp b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/initial_only_field.cpp new file mode 100644 index 000000000..8f8241526 --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/initial_only_field.cpp @@ -0,0 +1,14 @@ +/******************************************************************************* + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ + +#include "score/mw/com/test/fields/set_and_notifier/fields_test_resources/initial_only_field.h" diff --git a/score/mw/com/test/fields/set_and_notifier/fields_test_resources/initial_only_field.h b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/initial_only_field.h new file mode 100644 index 000000000..764308381 --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/initial_only_field.h @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ + +#ifndef SCORE_MW_COM_TEST_FIELDS_SET_AND_NOTIFIER_FIELDS_TEST_RESOURCES_INITIAL_ONLY_FIELD_H +#define SCORE_MW_COM_TEST_FIELDS_SET_AND_NOTIFIER_FIELDS_TEST_RESOURCES_INITIAL_ONLY_FIELD_H + +#include "score/mw/com/types.h" +#include + +namespace score::mw::com::test +{ + +template +class InitialOnlyInterface : public T::Base +{ + public: + using T::Base::Base; + + typename T::template Field initial_only_field{*this, "initial_only_field"}; +}; + +using InitialOnlyProxy = score::mw::com::AsProxy; +using InitialOnlySkeleton = score::mw::com::AsSkeleton; + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_FIELDS_SET_AND_NOTIFIER_FIELDS_TEST_RESOURCES_INITIAL_ONLY_FIELD_H diff --git a/score/mw/com/test/fields/set_and_notifier/fields_test_resources/set_enabled_field.cpp b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/set_enabled_field.cpp new file mode 100644 index 000000000..f0757a93a --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/set_enabled_field.cpp @@ -0,0 +1,14 @@ +/******************************************************************************* + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ + +#include "score/mw/com/test/fields/set_and_notifier/fields_test_resources/set_enabled_field.h" diff --git a/score/mw/com/test/fields/set_and_notifier/fields_test_resources/set_enabled_field.h b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/set_enabled_field.h new file mode 100644 index 000000000..0ef5e6988 --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/set_enabled_field.h @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ + +#ifndef SCORE_MW_COM_TEST_FIELDS_SET_AND_NOTIFIER_FIELDS_TEST_RESOURCES_SET_ENABLED_FIELD_H +#define SCORE_MW_COM_TEST_FIELDS_SET_AND_NOTIFIER_FIELDS_TEST_RESOURCES_SET_ENABLED_FIELD_H + +#include "score/mw/com/types.h" +#include + +namespace score::mw::com::test +{ + +template +class SetEnabledInterface : public T::Base +{ + public: + using T::Base::Base; + + typename T::template Field set_enabled_field{*this, "set_enabled_field"}; +}; + +using SetEnabledProxy = score::mw::com::AsProxy; +using SetEnabledSkeleton = score::mw::com::AsSkeleton; + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_FIELDS_SET_AND_NOTIFIER_FIELDS_TEST_RESOURCES_SET_ENABLED_FIELD_H diff --git a/score/mw/com/test/fields/set_and_notifier/fields_test_resources/test_constants.cpp b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/test_constants.cpp new file mode 100644 index 000000000..4f33318f7 --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/test_constants.cpp @@ -0,0 +1,14 @@ +/******************************************************************************* + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ + +#include "score/mw/com/test/fields/set_and_notifier/fields_test_resources/test_constants.h" diff --git a/score/mw/com/test/fields/set_and_notifier/fields_test_resources/test_constants.h b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/test_constants.h new file mode 100644 index 000000000..bea27d2fc --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/fields_test_resources/test_constants.h @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ + +#ifndef SCORE_MW_COM_TEST_FIELDS_SET_AND_NOTIFIER_FIELDS_TEST_RESOURCES_TEST_CONSTANTS_H +#define SCORE_MW_COM_TEST_FIELDS_SET_AND_NOTIFIER_FIELDS_TEST_RESOURCES_TEST_CONSTANTS_H + +#include + +namespace score::mw::com::test +{ + +constexpr const char* const kInstanceSpecifierString = "test/fields/set_and_notifier"; + +constexpr std::int32_t kInitialValue = 18; +constexpr std::int32_t kSetRequestValue = 1234; +constexpr std::int32_t kSetTransformedValue = kSetRequestValue * 2 + 1; +constexpr std::int32_t kUpdatedValue = 19; + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_FIELDS_SET_AND_NOTIFIER_FIELDS_TEST_RESOURCES_TEST_CONSTANTS_H diff --git a/score/mw/com/test/fields/set_and_notifier/integration_test/BUILD b/score/mw/com/test/fields/set_and_notifier/integration_test/BUILD new file mode 100644 index 000000000..77cc6f923 --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/integration_test/BUILD @@ -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 +# ******************************************************************************* + +load("@rules_pkg//pkg:mappings.bzl", "pkg_filegroup") +load("//quality/integration_testing:integration_testing.bzl", "integration_test") + +pkg_filegroup( + name = "filesystem", + srcs = [ + "//score/mw/com/test/fields/set_and_notifier:consumer-pkg", + "//score/mw/com/test/fields/set_and_notifier:provider-pkg", + ], +) + +py_library( + name = "test_fixture", + srcs = ["test_fixture.py"], +) + +integration_test( + name = "test_notifier", + timeout = "moderate", + srcs = [ + "test_notifier.py", + ":test_fixture", + ], + filesystem = ":filesystem", +) + +integration_test( + name = "test_set_and_notifier", + timeout = "moderate", + srcs = [ + "test_set_and_notifier.py", + ":test_fixture", + ], + filesystem = ":filesystem", +) diff --git a/score/mw/com/test/fields/set_and_notifier/integration_test/test_fixture.py b/score/mw/com/test/fields/set_and_notifier/integration_test/test_fixture.py new file mode 100644 index 000000000..5f74fef2f --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/integration_test/test_fixture.py @@ -0,0 +1,23 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + + +def consumer(target, mode, config, **kwargs): + args = ["--num-retries", "20", "--backoff-time", "50", "--mode", mode, + "--service-instance-manifest", f"./etc/{config}"] + return target.wrap_exec("bin/consumer", args, cwd="/opt/MainConsumerApp", wait_on_exit=True, **kwargs) + + +def provider(target, mode, config, **kwargs): + args = ["--mode", mode, "--service-instance-manifest", f"./etc/{config}"] + return target.wrap_exec("bin/provider", args, cwd="/opt/MainProviderApp", **kwargs) diff --git a/score/mw/com/test/fields/set_and_notifier/integration_test/test_get.py b/score/mw/com/test/fields/set_and_notifier/integration_test/test_get.py new file mode 100644 index 000000000..af068eba8 --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/integration_test/test_get.py @@ -0,0 +1,18 @@ +# ******************************************************************************* +# 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 test_fixture import consumer, provider + +# TODO: Implement once get mode is supported by the provider and consumer binaries. +# Scenarios to cover: +# 1. calling Update / send -> calling get returns value set with send diff --git a/score/mw/com/test/fields/set_and_notifier/integration_test/test_get_and_notifier.py b/score/mw/com/test/fields/set_and_notifier/integration_test/test_get_and_notifier.py new file mode 100644 index 000000000..21648aabc --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/integration_test/test_get_and_notifier.py @@ -0,0 +1,19 @@ +# ******************************************************************************* +# 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 test_fixture import consumer, provider + +# TODO: Implement once get_and_notifier mode is supported by the provider and consumer binaries. +# Scenarios to cover (same as notifier and get, verifying result of both GetNewSamples and getter): +# 1. calling Update / send -> calling get returns value set with send +# 2. calling Update / send -> calling GetNewSamples returns value set with send diff --git a/score/mw/com/test/fields/set_and_notifier/integration_test/test_notifier.py b/score/mw/com/test/fields/set_and_notifier/integration_test/test_notifier.py new file mode 100644 index 000000000..2d399560b --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/integration_test/test_notifier.py @@ -0,0 +1,21 @@ +# ******************************************************************************* +# 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 test_fixture import consumer, provider + + +def test_field_notifier_initial_value(target): + """Test field initial value exchange between provider and consumer.""" + with provider(target, "notifier", "initial_only_field_mw_com_config.json"): + with consumer(target, "notifier", "initial_only_field_mw_com_config.json"): + pass diff --git a/score/mw/com/test/fields/set_and_notifier/integration_test/test_set_and_get.py b/score/mw/com/test/fields/set_and_notifier/integration_test/test_set_and_get.py new file mode 100644 index 000000000..ce184446b --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/integration_test/test_set_and_get.py @@ -0,0 +1,20 @@ +# ******************************************************************************* +# 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 test_fixture import consumer, provider + +# TODO: Implement once set_and_get mode is supported by the provider and consumer binaries. +# Scenarios to cover: +# 1. calling set with valid value -> calling get returns value set with setter +# 2. calling set with invalid value (set handler clamps the value) -> calling get returns clamped value +# 3. calling Update / send -> calling get returns value set with send diff --git a/score/mw/com/test/fields/set_and_notifier/integration_test/test_set_and_notifier.py b/score/mw/com/test/fields/set_and_notifier/integration_test/test_set_and_notifier.py new file mode 100644 index 000000000..c646bac23 --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/integration_test/test_set_and_notifier.py @@ -0,0 +1,24 @@ +# ******************************************************************************* +# 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 test_fixture import consumer, provider + + +def test_field_set_and_notifier_value(target): + """Test field set exchange and accepted value propagation between provider and consumer.""" + with provider(target, "set_and_notifier", "mw_com_config.json"): + with consumer(target, "set_and_notifier", "mw_com_config.json"): + pass + + +# TODO: Add a dedicated get scenario test once getter-enabled field mode is available. diff --git a/score/mw/com/test/fields/set_and_notifier/integration_test/test_set_get_and_notifier.py b/score/mw/com/test/fields/set_and_notifier/integration_test/test_set_get_and_notifier.py new file mode 100644 index 000000000..c7e39ac8a --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/integration_test/test_set_get_and_notifier.py @@ -0,0 +1,20 @@ +# ******************************************************************************* +# 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 test_fixture import consumer, provider + +# TODO: Implement once set_get_and_notifier mode is supported by the provider and consumer binaries. +# Scenarios to cover (same as set_and_notifier, but also verify result of getter): +# 1. calling set with valid value -> calling get and GetNewSamples both return value set with setter +# 2. calling set with invalid value (set handler clamps the value) -> calling get and GetNewSamples both return clamped value +# 3. calling Update / send -> calling get and GetNewSamples both return value set with send diff --git a/score/mw/com/test/fields/set_and_notifier/main_consumer.cpp b/score/mw/com/test/fields/set_and_notifier/main_consumer.cpp new file mode 100644 index 000000000..20ab84ed6 --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/main_consumer.cpp @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ + +#include "score/mw/com/runtime.h" +#include "score/mw/com/test/common_test_resources/command_line_parser.h" +#include "score/mw/com/test/common_test_resources/fail_test.h" +#include "score/mw/com/test/fields/set_and_notifier/fields_test_resources/field_consumer.h" + +#include +#include +#include +#include +#include +#include + +namespace +{ + +struct ConsumerConfig +{ + std::size_t num_retries; + std::chrono::milliseconds retry_backoff_time; + std::string mode; + std::string manifest; +}; + +ConsumerConfig ParseConfig(int argc, const char** argv) +{ + constexpr auto kNumRetriesArg = "num-retries"; + constexpr auto kBackoffTimeArg = "backoff-time"; + constexpr auto kModeArg = "mode"; + constexpr auto kServiceInstanceManifestArg = "service-instance-manifest"; + + const std::vector> parameter_description_pairs{ + {kNumRetriesArg, "Number of retries"}, + {kBackoffTimeArg, "Retry backoff time in milliseconds"}, + {kModeArg, "Consumer mode: notifier or set"}, + {kServiceInstanceManifestArg, "Path to the service instance manifest"}, + }; + + const auto args = score::mw::com::test::ParseCommandLineArguments(argc, argv, parameter_description_pairs); + + const auto num_retries_result = score::mw::com::test::GetValueIfProvided(args, kNumRetriesArg); + if (!num_retries_result.has_value()) + { + score::mw::com::test::FailTest("Consumer: missing or invalid --", kNumRetriesArg, " argument"); + } + + const auto backoff_time_result = score::mw::com::test::GetValueIfProvided(args, kBackoffTimeArg); + if (!backoff_time_result.has_value()) + { + score::mw::com::test::FailTest("Consumer: missing or invalid --", kBackoffTimeArg, " argument"); + } + + const auto mode_result = score::mw::com::test::GetValueIfProvided(args, kModeArg); + if (!mode_result.has_value()) + { + score::mw::com::test::FailTest("Consumer: missing or invalid --", kModeArg, " argument"); + } + + const auto manifest_result = + score::mw::com::test::GetValueIfProvided(args, kServiceInstanceManifestArg); + if (!manifest_result.has_value()) + { + score::mw::com::test::FailTest("Consumer: missing or invalid --", kServiceInstanceManifestArg, " argument"); + } + + return ConsumerConfig{num_retries_result.value(), + std::chrono::milliseconds{backoff_time_result.value()}, + mode_result.value(), + manifest_result.value()}; +} + +} // namespace + +int main(int argc, const char** argv) +{ + const auto config = ParseConfig(argc, argv); + + score::mw::com::runtime::InitializeRuntime(score::mw::com::runtime::RuntimeConfiguration{config.manifest}); + + score::mw::com::test::run_consumer(config.num_retries, config.retry_backoff_time, config.mode); + return EXIT_SUCCESS; +} diff --git a/score/mw/com/test/fields/set_and_notifier/main_provider.cpp b/score/mw/com/test/fields/set_and_notifier/main_provider.cpp new file mode 100644 index 000000000..d5bb0931b --- /dev/null +++ b/score/mw/com/test/fields/set_and_notifier/main_provider.cpp @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ + +#include "score/mw/com/runtime.h" +#include "score/mw/com/test/common_test_resources/command_line_parser.h" +#include "score/mw/com/test/common_test_resources/fail_test.h" +#include "score/mw/com/test/common_test_resources/stop_token_sig_term_handler.h" +#include "score/mw/com/test/fields/set_and_notifier/fields_test_resources/field_provider.h" + +#include +#include +#include +#include +#include + +namespace +{ + +struct ProviderConfig +{ + std::string mode; + std::string manifest; +}; + +ProviderConfig ParseConfig(int argc, const char** argv) +{ + constexpr auto kModeArg = "mode"; + constexpr auto kServiceInstanceManifestArg = "service-instance-manifest"; + + const std::vector> parameter_description_pairs{ + {kModeArg, "Provider mode: notifier or set"}, + {kServiceInstanceManifestArg, "Path to the service instance manifest"}, + }; + + const auto args = score::mw::com::test::ParseCommandLineArguments(argc, argv, parameter_description_pairs); + + const auto mode_result = score::mw::com::test::GetValueIfProvided(args, kModeArg); + if (!mode_result.has_value()) + { + score::mw::com::test::FailTest("Provider: missing or invalid --", kModeArg, " argument"); + } + + const auto manifest_result = + score::mw::com::test::GetValueIfProvided(args, kServiceInstanceManifestArg); + if (!manifest_result.has_value()) + { + score::mw::com::test::FailTest("Provider: missing or invalid --", kServiceInstanceManifestArg, " argument"); + } + + return ProviderConfig{mode_result.value(), manifest_result.value()}; +} + +} // namespace + +int main(int argc, const char** argv) +{ + const auto config = ParseConfig(argc, argv); + + score::mw::com::runtime::InitializeRuntime(score::mw::com::runtime::RuntimeConfiguration{config.manifest}); + + score::cpp::stop_source stop_source{}; + const bool sig_term_handler_setup_success = score::mw::com::SetupStopTokenSigTermHandler(stop_source); + if (!sig_term_handler_setup_success) + { + std::cerr << "Unable to set signal handler for SIGINT and/or SIGTERM, cautiously continuing\n"; + } + + score::mw::com::test::run_provider(stop_source.get_token(), config.mode); + return EXIT_SUCCESS; +} diff --git a/score/mw/com/test/methods/signature_variations/BUILD b/score/mw/com/test/methods/signature_variations/BUILD index 1d4007c90..19d2ec224 100644 --- a/score/mw/com/test/methods/signature_variations/BUILD +++ b/score/mw/com/test/methods/signature_variations/BUILD @@ -41,8 +41,8 @@ cc_library( implementation_deps = [ ":test_method_datatype", "//score/mw/com", + "//score/mw/com/test/common_test_resources:process_synchronizer", "//score/mw/com/test/methods/methods_test_resources:method_consumer", - "//score/mw/com/test/methods/methods_test_resources:process_synchronizer", ], deps = [ "@score_baselibs//score/language/futurecpp", @@ -57,8 +57,8 @@ cc_library( deps = [ ":test_method_datatype", "//score/mw/com", + "//score/mw/com/test/common_test_resources:process_synchronizer", "//score/mw/com/test/methods/methods_test_resources:method_provider", - "//score/mw/com/test/methods/methods_test_resources:process_synchronizer", ], ) diff --git a/score/mw/com/test/methods/signature_variations/consumer.cpp b/score/mw/com/test/methods/signature_variations/consumer.cpp index 2d6123f00..e820c0b85 100644 --- a/score/mw/com/test/methods/signature_variations/consumer.cpp +++ b/score/mw/com/test/methods/signature_variations/consumer.cpp @@ -12,8 +12,8 @@ ********************************************************************************/ #include "score/mw/com/test/methods/signature_variations/consumer.h" +#include "score/mw/com/test/common_test_resources/process_synchronizer.h" #include "score/mw/com/test/methods/methods_test_resources/method_consumer.h" -#include "score/mw/com/test/methods/methods_test_resources/process_synchronizer.h" #include "score/mw/com/test/methods/signature_variations/test_method_datatype.h" #include "score/mw/com/types.h" diff --git a/score/mw/com/test/methods/signature_variations/provider.cpp b/score/mw/com/test/methods/signature_variations/provider.cpp index ffd5ec0f1..aaf7bb9f0 100644 --- a/score/mw/com/test/methods/signature_variations/provider.cpp +++ b/score/mw/com/test/methods/signature_variations/provider.cpp @@ -12,8 +12,8 @@ ********************************************************************************/ #include "score/mw/com/test/methods/signature_variations/provider.h" +#include "score/mw/com/test/common_test_resources/process_synchronizer.h" #include "score/mw/com/test/methods/methods_test_resources/method_provider.h" -#include "score/mw/com/test/methods/methods_test_resources/process_synchronizer.h" #include "score/mw/com/test/methods/signature_variations/test_method_datatype.h" #include "score/mw/com/types.h"