diff --git a/score/mw/com/test/common_test_resources/proxy_container.h b/score/mw/com/test/common_test_resources/proxy_container.h index cf6103e7da..549819578f 100644 --- a/score/mw/com/test/common_test_resources/proxy_container.h +++ b/score/mw/com/test/common_test_resources/proxy_container.h @@ -39,6 +39,13 @@ class ProxyContainer return *proxy_; } + Proxy&& Extract() + { + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(proxy_ != nullptr, + "Proxy was not successfully created! Cannot extract it!"); + return std::move(*proxy_); + } + private: std::unique_ptr handle_{nullptr}; std::mutex proxy_creation_mutex_{}; diff --git a/score/mw/com/test/move_semantics/proxy_method/BUILD b/score/mw/com/test/move_semantics/proxy_method/BUILD new file mode 100644 index 0000000000..72bb1e6cc2 --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/BUILD @@ -0,0 +1,167 @@ +# ******************************************************************************* +# 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", "cc_library") +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_config_schema", + json = "config/mw_com_config.json", + schema = "//score/mw/com:config_schema", + tags = ["lint"], +) + +cc_library( + name = "test_method_datatype", + srcs = ["test_method_datatype.cpp"], + hdrs = ["test_method_datatype.h"], + features = COMPILER_WARNING_FEATURES, + deps = [ + "//score/mw/com", + ], +) + +cc_library( + name = "test_parameters", + srcs = ["test_parameters.cpp"], + hdrs = ["test_parameters.h"], + features = COMPILER_WARNING_FEATURES, + deps = [ + "//score/mw/com", + "//score/mw/com/test/common_test_resources:command_line_parser", + "//score/mw/com/test/common_test_resources:fail_test", + ], +) + +cc_library( + name = "provider", + srcs = ["provider.cpp"], + hdrs = ["provider.h"], + features = COMPILER_WARNING_FEATURES, + deps = [ + ":test_method_datatype", + ":test_parameters", + "//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", + ], +) + +cc_library( + name = "consumer", + srcs = ["consumer.cpp"], + hdrs = ["consumer.h"], + features = COMPILER_WARNING_FEATURES, + deps = [ + ":test_method_datatype", + ":test_parameters", + "//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_binary( + name = "main_provider", + srcs = ["main_provider.cpp"], + data = ["config/mw_com_config.json"], + features = COMPILER_WARNING_FEATURES + [ + "aborts_upon_exception", + ], + deps = [ + ":provider", + ":test_parameters", + "//score/mw/com", + "//score/mw/com/test/common_test_resources:assert_handler", + "//score/mw/com/test/common_test_resources:fail_test", + "//score/mw/com/test/common_test_resources:stop_token_sig_term_handler", + ], +) + +cc_binary( + name = "main_consumer", + srcs = ["main_consumer.cpp"], + data = ["config/mw_com_config.json"], + features = COMPILER_WARNING_FEATURES + [ + "aborts_upon_exception", + ], + deps = [ + ":consumer", + ":test_parameters", + "//score/mw/com", + "//score/mw/com/test/common_test_resources:assert_handler", + "//score/mw/com/test/common_test_resources:fail_test", + "//score/mw/com/test/common_test_resources:stop_token_sig_term_handler", + ], +) + +cc_binary( + name = "main_consumer_and_provider", + srcs = ["main_consumer_and_provider.cpp"], + data = ["config/mw_com_config.json"], + features = COMPILER_WARNING_FEATURES + [ + "aborts_upon_exception", + ], + deps = [ + ":consumer", + ":provider", + ":test_parameters", + "//score/mw/com", + "//score/mw/com/test/common_test_resources:assert_handler", + "//score/mw/com/test/common_test_resources:fail_test", + "//score/mw/com/test/common_test_resources:stop_token_sig_term_handler", + ], +) + +pkg_application( + name = "main_provider-pkg", + app_name = "MainProviderApp", + bin = [":main_provider"], + etc = [ + "config/mw_com_config.json", + "config/logging.json", + ], + visibility = [ + "//score/mw/com/test/move_semantics/proxy_method:__subpackages__", + ], +) + +pkg_application( + name = "main_consumer-pkg", + app_name = "MainConsumerApp", + bin = [":main_consumer"], + etc = [ + "config/mw_com_config.json", + "config/logging.json", + ], + visibility = [ + "//score/mw/com/test/move_semantics/proxy_method:__subpackages__", + ], +) + +pkg_application( + name = "main_consumer_and_provider-pkg", + app_name = "MainConsumerAndProviderApp", + bin = [":main_consumer_and_provider"], + etc = [ + "config/mw_com_config.json", + "config/logging.json", + ], + visibility = [ + "//score/mw/com/test/move_semantics/proxy_method:__subpackages__", + ], +) diff --git a/score/mw/com/test/move_semantics/proxy_method/config/logging.json b/score/mw/com/test/move_semantics/proxy_method/config/logging.json new file mode 100644 index 0000000000..e449c42b9b --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/config/logging.json @@ -0,0 +1,7 @@ +{ + "appId": "PMMS", + "appDesc": "proxy_method_move_semantics", + "logLevel": "kDebug", + "logLevelThresholdConsole": "kDebug", + "logMode": "kRemote|kConsole" +} diff --git a/score/mw/com/test/move_semantics/proxy_method/config/mw_com_config.json b/score/mw/com/test/move_semantics/proxy_method/config/mw_com_config.json new file mode 100644 index 0000000000..9211fbb3b9 --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/config/mw_com_config.json @@ -0,0 +1,103 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/proxy_method_move_semantics/MoveMethodInterface", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 2200, + "methods": [ + { + "methodName": "with_in_args_and_return", + "methodId": 1 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "test/proxy_method_move_semantics/MoveMethodInterfaceMovedTo", + "serviceTypeName": "/test/proxy_method_move_semantics/MoveMethodInterface", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "QM", + "binding": "SHM", + "methods": [ + { + "methodName": "with_in_args_and_return", + "queueSize": 1 + } + ], + "allowedConsumer": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + }, + "allowedProvider": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + } + } + ] + }, + { + "instanceSpecifier": "test/proxy_method_move_semantics/MoveMethodInterfaceMovedFrom", + "serviceTypeName": "/test/proxy_method_move_semantics/MoveMethodInterface", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 2, + "asil-level": "QM", + "binding": "SHM", + "methods": [ + { + "methodName": "with_in_args_and_return", + "queueSize": 1 + } + ], + "allowedConsumer": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + }, + "allowedProvider": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + } + } + ] + } + ], + "global": { + "asil-level": "QM", + "applicationID": 4002 + } +} diff --git a/score/mw/com/test/move_semantics/proxy_method/consumer.cpp b/score/mw/com/test/move_semantics/proxy_method/consumer.cpp new file mode 100644 index 0000000000..29c39e8bb2 --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/consumer.cpp @@ -0,0 +1,176 @@ +/******************************************************************************** + * 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/move_semantics/proxy_method/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/move_semantics/proxy_method/test_method_datatype.h" + +#include +#include +#include + +namespace score::mw::com::test +{ +namespace +{ + +const std::string kInterprocessNotificationShmPath{"/proxy_method_move_semantics_interprocess_notification"}; + +/// \brief Calls the method via the copy path and verifies the returned value. +void CallMethodWithCopy(ProxyMethodMoveSemanticsProxy& proxy, const std::int32_t expected_return_value) +{ + std::cout << "\nConsumer: Calling method (copy), expecting " << expected_return_value << std::endl; + auto method_return_result = proxy.with_in_args_and_return(kTestValueA, kTestValueB); + if (!method_return_result.has_value()) + { + FailTest("Consumer: with_in_args_and_return copy call failed: ", method_return_result.error()); + } + const auto actual_return_value = *(method_return_result.value()); + if (actual_return_value != expected_return_value) + { + FailTest("Consumer: with_in_args_and_return copy call expected ", + expected_return_value, + " but got ", + actual_return_value); + } +} + +/// \brief Calls the method via the zero-copy path and verifies the returned value. +void CallMethodZeroCopy(ProxyMethodMoveSemanticsProxy& proxy, const std::int32_t expected_return_value) +{ + std::cout << "\nConsumer: Calling method (zero-copy), expecting " << expected_return_value << std::endl; + auto allocated_args_result = proxy.with_in_args_and_return.Allocate(); + if (!allocated_args_result.has_value()) + { + FailTest("Consumer: Could not allocate method args: ", allocated_args_result.error()); + } + + auto& [arg1_ptr, arg2_ptr] = allocated_args_result.value(); + *arg1_ptr = kTestValueA; + *arg2_ptr = kTestValueB; + + auto method_return_result = proxy.with_in_args_and_return(std::move(arg1_ptr), std::move(arg2_ptr)); + if (!method_return_result.has_value()) + { + FailTest("Consumer: with_in_args_and_return zero-copy call failed: ", method_return_result.error()); + } + const auto actual_return_value = *(method_return_result.value()); + if (actual_return_value != expected_return_value) + { + FailTest("Consumer: with_in_args_and_return zero-copy call expected ", + expected_return_value, + " but got ", + actual_return_value); + } +} + +/// \brief Exercises both the copy and zero-copy call paths and verifies the returned value. +void CallAndVerify(ProxyMethodMoveSemanticsProxy& proxy, const std::int32_t expected_return_value) +{ + CallMethodWithCopy(proxy, expected_return_value); + CallMethodZeroCopy(proxy, expected_return_value); +} + +void RunConsumerMoveConstruct(const std::string& failure_message_prefix) +{ + // Step 1. Find service and create proxy A + std::cout << "\nConsumer: Step 1 - Find service and create proxy A" << std::endl; + ProxyContainer proxy_a_container{}; + proxy_a_container.CreateProxy(kInstanceSpecifierMovedTo, failure_message_prefix); + auto proxy_a = proxy_a_container.Extract(); + + // Step 2. Call method via proxy A (iteration 0: proxy works before the move) + std::cout << "\nConsumer: Step 2 - Call method via proxy A" << std::endl; + CallAndVerify(proxy_a, kTestValueA + kTestValueB); + + // Step 3. Move construct proxy B from proxy A + std::cout << "\nConsumer: Step 3 - Move construct proxy B from proxy A" << std::endl; + auto proxy_b = std::move(proxy_a); + + // Step 4. Call method via proxy B (iteration 1: proxy works after the move) + std::cout << "\nConsumer: Step 4 - Call method via proxy B" << std::endl; + CallAndVerify(proxy_b, kTestValueA + kTestValueB); +} + +void RunConsumerMoveAssign(const std::string& failure_message_prefix) +{ + // Step 1. Find service and create proxy A (connected to the moved-from instance, which answers with a + b) + std::cout << "\nConsumer: Step 1 - Find service and create proxy A" << std::endl; + ProxyContainer proxy_a_container{}; + proxy_a_container.CreateProxy(kInstanceSpecifierMovedFrom, failure_message_prefix); + auto proxy_a = proxy_a_container.Extract(); + + // Step 2. Find service and create proxy B (connected to the moved-to instance, which answers with a - b) + std::cout << "\nConsumer: Step 2 - Find service and create proxy B" << std::endl; + ProxyContainer proxy_b_container{}; + proxy_b_container.CreateProxy(kInstanceSpecifierMovedTo, failure_message_prefix); + auto proxy_b = proxy_b_container.Extract(); + + // Step 3. Call method via proxy A (iteration 0: proxy A works before the move, returns a + b) + std::cout << "\nConsumer: Step 3 - Call method via proxy A" << std::endl; + CallAndVerify(proxy_a, kTestValueA + kTestValueB); + + // Step 4. Call method via proxy B (iteration 0: proxy B works before the move, returns a - b) + std::cout << "\nConsumer: Step 4 - Call method via proxy B" << std::endl; + CallAndVerify(proxy_b, kTestValueA - kTestValueB); + + // Step 5. Move assign proxy A into proxy B. Proxy B now holds the moved-from proxy. + std::cout << "\nConsumer: Step 5 - Move assign proxy A into proxy B" << std::endl; + proxy_b = std::move(proxy_a); + + // Step 6. Call method via proxy B (iteration 1: proxy B now reaches the moved-from method handler, returns a + b) + std::cout << "\nConsumer: Step 6 - Call method via proxy B" << std::endl; + CallAndVerify(proxy_b, kTestValueA + kTestValueB); +} + +} // namespace + +void RunConsumer(const ProxyMoveScenario& scenario, const score::cpp::stop_token& /*stop_token*/) +{ + const std::string failure_message_prefix{"proxy_method_move_semantics"}; + + auto consumer_done_synchronizer_result = ProcessSynchronizer::Create(kInterprocessNotificationShmPath); + if (!consumer_done_synchronizer_result.has_value()) + { + FailTest("proxy_method_move_semantics consumer failed: could not create consumer done synchronizer"); + } + + // Notify the provider when the consumer is done (or fails) so that it does not wait indefinitely. + ExitFunctionGuard done_guard{[&consumer_done_synchronizer_result]() { + consumer_done_synchronizer_result->Notify(); + }}; + + switch (scenario) + { + case ProxyMoveScenario::kMoveConstructAfterMethodCall: + { + RunConsumerMoveConstruct(failure_message_prefix); + break; + } + case ProxyMoveScenario::kMoveAssignAfterMethodCall: + { + RunConsumerMoveAssign(failure_message_prefix); + break; + } + case ProxyMoveScenario::kNumberOfScenarios: + [[fallthrough]]; + default: + FailTest("proxy_method_move_semantics consumer failed: unknown scenario"); + } + + std::cout << "Consumer: Done with all method calls, exiting" << std::endl; +} + +} // namespace score::mw::com::test diff --git a/score/mw/com/test/move_semantics/proxy_method/consumer.h b/score/mw/com/test/move_semantics/proxy_method/consumer.h new file mode 100644 index 0000000000..e9e8def24d --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/consumer.h @@ -0,0 +1,27 @@ +/******************************************************************************** + * 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_PROXY_METHOD_MOVE_SEMANTICS_CONSUMER_H +#define SCORE_MW_COM_TEST_PROXY_METHOD_MOVE_SEMANTICS_CONSUMER_H + +#include "score/mw/com/test/move_semantics/proxy_method/test_parameters.h" + +#include + +namespace score::mw::com::test +{ + +void RunConsumer(const ProxyMoveScenario& scenario, const score::cpp::stop_token& stop_token); + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_PROXY_METHOD_MOVE_SEMANTICS_CONSUMER_H diff --git a/score/mw/com/test/move_semantics/proxy_method/integration_test/BUILD b/score/mw/com/test/move_semantics/proxy_method/integration_test/BUILD new file mode 100644 index 0000000000..08eb4ca0dc --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/integration_test/BUILD @@ -0,0 +1,59 @@ +# ******************************************************************************* +# 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") + +integration_test( + name = "move_construct_after_method_call_same_process_test", + srcs = [ + "move_construct_after_method_call_same_process_test.py", + "test_fixture.py", + ], + filesystem = "//score/mw/com/test/move_semantics/proxy_method:main_consumer_and_provider-pkg", +) + +integration_test( + name = "move_assign_after_method_call_same_process_test", + srcs = [ + "move_assign_after_method_call_same_process_test.py", + "test_fixture.py", + ], + filesystem = "//score/mw/com/test/move_semantics/proxy_method:main_consumer_and_provider-pkg", +) + +pkg_filegroup( + name = "different_processes_filesystem", + srcs = [ + "//score/mw/com/test/move_semantics/proxy_method:main_consumer-pkg", + "//score/mw/com/test/move_semantics/proxy_method:main_provider-pkg", + ], +) + +integration_test( + name = "move_construct_after_method_call_different_process_test", + srcs = [ + "move_construct_after_method_call_different_process_test.py", + "test_fixture.py", + ], + filesystem = ":different_processes_filesystem", +) + +integration_test( + name = "move_assign_after_method_call_different_process_test", + srcs = [ + "move_assign_after_method_call_different_process_test.py", + "test_fixture.py", + ], + filesystem = ":different_processes_filesystem", +) diff --git a/score/mw/com/test/move_semantics/proxy_method/integration_test/move_assign_after_method_call_different_process_test.py b/score/mw/com/test/move_semantics/proxy_method/integration_test/move_assign_after_method_call_different_process_test.py new file mode 100644 index 0000000000..f5af1a75d4 --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/integration_test/move_assign_after_method_call_different_process_test.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, ProxyMoveScenario + + +def test_move_assign_after_method_call_different_process(target): + with provider(target, ProxyMoveScenario.MOVE_ASSIGN_AFTER_METHOD_CALL): + with consumer(target, ProxyMoveScenario.MOVE_ASSIGN_AFTER_METHOD_CALL): + pass diff --git a/score/mw/com/test/move_semantics/proxy_method/integration_test/move_assign_after_method_call_same_process_test.py b/score/mw/com/test/move_semantics/proxy_method/integration_test/move_assign_after_method_call_same_process_test.py new file mode 100644 index 0000000000..e11cab27ed --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/integration_test/move_assign_after_method_call_same_process_test.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_and_provider, ProxyMoveScenario + + +def test_move_assign_after_method_call_same_process(target): + with consumer_and_provider(target, ProxyMoveScenario.MOVE_ASSIGN_AFTER_METHOD_CALL): + pass diff --git a/score/mw/com/test/move_semantics/proxy_method/integration_test/move_construct_after_method_call_different_process_test.py b/score/mw/com/test/move_semantics/proxy_method/integration_test/move_construct_after_method_call_different_process_test.py new file mode 100644 index 0000000000..ebb6d692b8 --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/integration_test/move_construct_after_method_call_different_process_test.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, ProxyMoveScenario + + +def test_move_construct_after_method_call_different_process(target): + with provider(target, ProxyMoveScenario.MOVE_CONSTRUCT_AFTER_METHOD_CALL): + with consumer(target, ProxyMoveScenario.MOVE_CONSTRUCT_AFTER_METHOD_CALL): + pass diff --git a/score/mw/com/test/move_semantics/proxy_method/integration_test/move_construct_after_method_call_same_process_test.py b/score/mw/com/test/move_semantics/proxy_method/integration_test/move_construct_after_method_call_same_process_test.py new file mode 100644 index 0000000000..ea6e486eb0 --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/integration_test/move_construct_after_method_call_same_process_test.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_and_provider, ProxyMoveScenario + + +def test_move_construct_after_method_call_same_process(target): + with consumer_and_provider(target, ProxyMoveScenario.MOVE_CONSTRUCT_AFTER_METHOD_CALL): + pass diff --git a/score/mw/com/test/move_semantics/proxy_method/integration_test/test_fixture.py b/score/mw/com/test/move_semantics/proxy_method/integration_test/test_fixture.py new file mode 100644 index 0000000000..1d8f5c5371 --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/integration_test/test_fixture.py @@ -0,0 +1,35 @@ +# ******************************************************************************* +# 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 enum import IntEnum + + +class ProxyMoveScenario(IntEnum): + MOVE_CONSTRUCT_AFTER_METHOD_CALL = 0 + MOVE_ASSIGN_AFTER_METHOD_CALL = 1 + + +def consumer_and_provider(target, scenario, **kwargs): + args = ["--scenario", str(int(scenario)), "--service-instance-manifest", f"./etc/mw_com_config.json"] + return target.wrap_exec( + "bin/main_consumer_and_provider", args, cwd="/opt/MainConsumerAndProviderApp", wait_on_exit=True, **kwargs + ) + + +def consumer(target, scenario, **kwargs): + args = ["--scenario", str(int(scenario)), "--service-instance-manifest", f"./etc/mw_com_config.json"] + return target.wrap_exec("bin/main_consumer", args, cwd="/opt/MainConsumerApp", wait_on_exit=True, **kwargs) + + +def provider(target, scenario, **kwargs): + args = ["--scenario", str(int(scenario)), "--service-instance-manifest", f"./etc/mw_com_config.json"] + return target.wrap_exec("bin/main_provider", args, cwd="/opt/MainProviderApp", wait_on_exit=True, **kwargs) diff --git a/score/mw/com/test/move_semantics/proxy_method/main_consumer.cpp b/score/mw/com/test/move_semantics/proxy_method/main_consumer.cpp new file mode 100644 index 0000000000..c04b05de8a --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/main_consumer.cpp @@ -0,0 +1,40 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/com/runtime.h" + +#include "score/mw/com/test/common_test_resources/assert_handler.h" +#include "score/mw/com/test/common_test_resources/stop_token_sig_term_handler.h" +#include "score/mw/com/test/move_semantics/proxy_method/consumer.h" +#include "score/mw/com/test/move_semantics/proxy_method/test_parameters.h" + +int main(int argc, const char** argv) +{ + auto test_configuration{score::mw::com::test::ReadCommandLineArguments(argc, argv)}; + + score::mw::com::test::SetupAssertHandler(); + 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" << std::endl; + } + + std::cout << "Starting consumer with scenario " << static_cast(test_configuration.scenario) + << std::endl; + + score::mw::com::test::RunConsumer(test_configuration.scenario, stop_source.get_token()); + + return EXIT_SUCCESS; +} diff --git a/score/mw/com/test/move_semantics/proxy_method/main_consumer_and_provider.cpp b/score/mw/com/test/move_semantics/proxy_method/main_consumer_and_provider.cpp new file mode 100644 index 0000000000..b22bbd16fd --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/main_consumer_and_provider.cpp @@ -0,0 +1,49 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/com/runtime.h" + +#include "score/mw/com/test/common_test_resources/assert_handler.h" +#include "score/mw/com/test/common_test_resources/stop_token_sig_term_handler.h" +#include "score/mw/com/test/move_semantics/proxy_method/consumer.h" +#include "score/mw/com/test/move_semantics/proxy_method/provider.h" +#include "score/mw/com/test/move_semantics/proxy_method/test_parameters.h" + +#include + +int main(int argc, const char** argv) +{ + auto test_configuration{score::mw::com::test::ReadCommandLineArguments(argc, argv)}; + + score::mw::com::test::SetupAssertHandler(); + 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" << std::endl; + } + + std::cout << "Starting provider and consumer with scenario " + << static_cast(test_configuration.scenario) << std::endl; + + auto provider_future = + std::async(score::mw::com::test::RunProvider, test_configuration.scenario, stop_source.get_token()); + auto consumer_future = + std::async(score::mw::com::test::RunConsumer, test_configuration.scenario, stop_source.get_token()); + + provider_future.get(); + consumer_future.get(); + + return EXIT_SUCCESS; +} diff --git a/score/mw/com/test/move_semantics/proxy_method/main_provider.cpp b/score/mw/com/test/move_semantics/proxy_method/main_provider.cpp new file mode 100644 index 0000000000..b716512c13 --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/main_provider.cpp @@ -0,0 +1,40 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/com/runtime.h" + +#include "score/mw/com/test/common_test_resources/assert_handler.h" +#include "score/mw/com/test/common_test_resources/stop_token_sig_term_handler.h" +#include "score/mw/com/test/move_semantics/proxy_method/provider.h" +#include "score/mw/com/test/move_semantics/proxy_method/test_parameters.h" + +int main(int argc, const char** argv) +{ + auto test_configuration{score::mw::com::test::ReadCommandLineArguments(argc, argv)}; + + score::mw::com::test::SetupAssertHandler(); + 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" << std::endl; + } + + std::cout << "Starting provider with scenario " << static_cast(test_configuration.scenario) + << std::endl; + + score::mw::com::test::RunProvider(test_configuration.scenario, stop_source.get_token()); + + return EXIT_SUCCESS; +} diff --git a/score/mw/com/test/move_semantics/proxy_method/provider.cpp b/score/mw/com/test/move_semantics/proxy_method/provider.cpp new file mode 100644 index 0000000000..2013df8daf --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/provider.cpp @@ -0,0 +1,142 @@ +/******************************************************************************** + * 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/move_semantics/proxy_method/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/move_semantics/proxy_method/test_method_datatype.h" + +#include +#include +#include + +namespace score::mw::com::test +{ +namespace +{ + +const std::string kInterprocessNotificationShmPath{"/proxy_method_move_semantics_interprocess_notification"}; + +/// \brief Registers the addition handler (return_value = a + b) on the given skeleton. +void RegisterAdditionHandler(ProxyMethodMoveSemanticsSkeleton& skeleton) +{ + auto handler = [](std::int32_t& return_value, const std::int32_t& a, const std::int32_t& b) { + std::cout << "Provider: addition handler called with " << a << " + " << b << std::endl; + return_value = a + b; + }; + const auto register_result = skeleton.with_in_args_and_return.RegisterHandler(std::move(handler)); + if (!register_result.has_value()) + { + FailTest("Provider: Failed to register addition handler: ", register_result.error()); + } +} + +/// \brief Registers the subtraction handler (return_value = a - b) on the given skeleton. +void RegisterSubtractionHandler(ProxyMethodMoveSemanticsSkeleton& skeleton) +{ + auto handler = [](std::int32_t& return_value, const std::int32_t& a, const std::int32_t& b) { + std::cout << "Provider: subtraction handler called with " << a << " - " << b << std::endl; + return_value = a - b; + }; + const auto register_result = skeleton.with_in_args_and_return.RegisterHandler(std::move(handler)); + if (!register_result.has_value()) + { + FailTest("Provider: Failed to register subtraction handler: ", register_result.error()); + } +} + +void RunProviderMoveConstruct(const score::cpp::stop_token& stop_token, ProcessSynchronizer& consumer_done_synchronizer) +{ + // Step 1. Create skeleton for the moved-to instance + std::cout << "\nProvider: Step 1 - Create skeleton" << std::endl; + SkeletonContainer skeleton_container{}; + skeleton_container.CreateSkeleton(kInstanceSpecifierMovedTo, "proxy_method_move_semantics"); + auto& skeleton = skeleton_container.GetSkeleton(); + + // Step 2. Register the addition handler + std::cout << "\nProvider: Step 2 - Register method handler" << std::endl; + RegisterAdditionHandler(skeleton); + + // Step 3. Offer service + std::cout << "\nProvider: Step 3 - Offer service" << std::endl; + skeleton_container.OfferService("proxy_method_move_semantics"); + + // Step 4. Wait for the consumer to finish all method calls + std::cout << "\nProvider: Step 4 - Ready for method calls, waiting for consumer to finish" << std::endl; + if (!consumer_done_synchronizer.WaitWithAbort(stop_token)) + { + FailTest("proxy_method_move_semantics provider failed: waiting for consumer done was aborted"); + } + + std::cout << "Provider: Shutting down" << std::endl; +} + +void RunProviderMoveAssign(const score::cpp::stop_token& stop_token, ProcessSynchronizer& consumer_done_synchronizer) +{ + // Step 1. Create two skeletons. The moved-from instance answers with a + b, the moved-to instance with a - b, so + // that the consumer can prove that a moved-to proxy reaches the moved-from method channel after a move assignment. + std::cout << "\nProvider: Step 1 - Create two skeletons" << std::endl; + SkeletonContainer moved_from_skeleton_container{}; + moved_from_skeleton_container.CreateSkeleton(kInstanceSpecifierMovedFrom, "proxy_method_move_semantics"); + RegisterAdditionHandler(moved_from_skeleton_container.GetSkeleton()); + + SkeletonContainer moved_to_skeleton_container{}; + moved_to_skeleton_container.CreateSkeleton(kInstanceSpecifierMovedTo, "proxy_method_move_semantics"); + RegisterSubtractionHandler(moved_to_skeleton_container.GetSkeleton()); + + // Step 2. Offer both services + std::cout << "\nProvider: Step 2 - Offer both services" << std::endl; + moved_from_skeleton_container.OfferService("proxy_method_move_semantics"); + moved_to_skeleton_container.OfferService("proxy_method_move_semantics"); + + // Step 3. Wait for the consumer to finish all method calls + std::cout << "\nProvider: Step 3 - Ready for method calls, waiting for consumer to finish" << std::endl; + if (!consumer_done_synchronizer.WaitWithAbort(stop_token)) + { + FailTest("proxy_method_move_semantics provider failed: waiting for consumer done was aborted"); + } + + std::cout << "Provider: Shutting down" << std::endl; +} + +} // namespace + +void RunProvider(const ProxyMoveScenario& scenario, const score::cpp::stop_token& stop_token) +{ + auto consumer_done_synchronizer_result = ProcessSynchronizer::Create(kInterprocessNotificationShmPath); + if (!consumer_done_synchronizer_result.has_value()) + { + FailTest("proxy_method_move_semantics provider failed: could not create consumer done synchronizer"); + } + + switch (scenario) + { + case ProxyMoveScenario::kMoveConstructAfterMethodCall: + { + RunProviderMoveConstruct(stop_token, consumer_done_synchronizer_result.value()); + break; + } + case ProxyMoveScenario::kMoveAssignAfterMethodCall: + { + RunProviderMoveAssign(stop_token, consumer_done_synchronizer_result.value()); + break; + } + case ProxyMoveScenario::kNumberOfScenarios: + [[fallthrough]]; + default: + FailTest("proxy_method_move_semantics provider failed: unknown scenario"); + } +} + +} // namespace score::mw::com::test diff --git a/score/mw/com/test/move_semantics/proxy_method/provider.h b/score/mw/com/test/move_semantics/proxy_method/provider.h new file mode 100644 index 0000000000..39e43785a9 --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/provider.h @@ -0,0 +1,27 @@ +/******************************************************************************** + * 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_PROXY_METHOD_MOVE_SEMANTICS_PROVIDER_H +#define SCORE_MW_COM_TEST_PROXY_METHOD_MOVE_SEMANTICS_PROVIDER_H + +#include "score/mw/com/test/move_semantics/proxy_method/test_parameters.h" + +#include + +namespace score::mw::com::test +{ + +void RunProvider(const ProxyMoveScenario& scenario, const score::cpp::stop_token& stop_token); + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_PROXY_METHOD_MOVE_SEMANTICS_PROVIDER_H diff --git a/score/mw/com/test/move_semantics/proxy_method/test_method_datatype.cpp b/score/mw/com/test/move_semantics/proxy_method/test_method_datatype.cpp new file mode 100644 index 0000000000..2d8cf497a3 --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/test_method_datatype.cpp @@ -0,0 +1,13 @@ +/******************************************************************************** + * 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/move_semantics/proxy_method/test_method_datatype.h" diff --git a/score/mw/com/test/move_semantics/proxy_method/test_method_datatype.h b/score/mw/com/test/move_semantics/proxy_method/test_method_datatype.h new file mode 100644 index 0000000000..d9173e2aa3 --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/test_method_datatype.h @@ -0,0 +1,39 @@ +/******************************************************************************** + * 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_PROXY_METHOD_MOVE_SEMANTICS_TEST_METHOD_DATATYPE_H +#define SCORE_MW_COM_TEST_PROXY_METHOD_MOVE_SEMANTICS_TEST_METHOD_DATATYPE_H + +#include "score/mw/com/types.h" + +#include + +namespace score::mw::com::test +{ + +template +class ProxyMethodMoveSemanticsInterface : public T::Base +{ + public: + using T::Base::Base; + + typename T::template Method with_in_args_and_return{ + *this, + "with_in_args_and_return"}; +}; + +using ProxyMethodMoveSemanticsProxy = score::mw::com::AsProxy; +using ProxyMethodMoveSemanticsSkeleton = score::mw::com::AsSkeleton; + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_PROXY_METHOD_MOVE_SEMANTICS_TEST_METHOD_DATATYPE_H diff --git a/score/mw/com/test/move_semantics/proxy_method/test_parameters.cpp b/score/mw/com/test/move_semantics/proxy_method/test_parameters.cpp new file mode 100644 index 0000000000..38d92592cd --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/test_parameters.cpp @@ -0,0 +1,43 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/com/test/move_semantics/proxy_method/test_parameters.h" + +#include "score/mw/com/test/common_test_resources/command_line_parser.h" +#include "score/mw/com/test/common_test_resources/fail_test.h" + +namespace score::mw::com::test +{ + +CombinedTestConfiguration ReadCommandLineArguments(int argc, const char** argv) +{ + auto args = ParseCommandLineArguments(argc, argv, {{kScenario, ""}, {kServiceInstanceManifest, ""}}); + + const auto scenario_index = GetValue(args, kScenario); + if (scenario_index >= static_cast(ProxyMoveScenario::kNumberOfScenarios)) + { + FailTest("Consumer: ", + kScenario, + " value ", + scenario_index, + " is out of range. Valid values are between 0 and ", + static_cast(ProxyMoveScenario::kNumberOfScenarios) - 1, + "."); + } + const auto scenario = static_cast(scenario_index); + + auto service_instance_manifest = GetValue(args, kServiceInstanceManifest); + + return {scenario, service_instance_manifest}; +} + +} // namespace score::mw::com::test diff --git a/score/mw/com/test/move_semantics/proxy_method/test_parameters.h b/score/mw/com/test/move_semantics/proxy_method/test_parameters.h new file mode 100644 index 0000000000..932490560f --- /dev/null +++ b/score/mw/com/test/move_semantics/proxy_method/test_parameters.h @@ -0,0 +1,52 @@ +/******************************************************************************** + * 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_PROXY_METHOD_MOVE_SEMANTICS_TEST_PARAMETERS_H +#define SCORE_MW_COM_TEST_PROXY_METHOD_MOVE_SEMANTICS_TEST_PARAMETERS_H + +#include "score/mw/com/types.h" + +#include +#include + +namespace score::mw::com::test +{ + +const std::string kScenario{"scenario"}; +const std::string kServiceInstanceManifest{"service-instance-manifest"}; + +constexpr std::int32_t kTestValueA{42}; +constexpr std::int32_t kTestValueB{17}; + +const InstanceSpecifier kInstanceSpecifierMovedTo = + InstanceSpecifier::Create(std::string{"test/proxy_method_move_semantics/MoveMethodInterfaceMovedTo"}).value(); +const InstanceSpecifier kInstanceSpecifierMovedFrom = + InstanceSpecifier::Create(std::string{"test/proxy_method_move_semantics/MoveMethodInterfaceMovedFrom"}).value(); + +enum class ProxyMoveScenario : std::uint8_t +{ + kMoveConstructAfterMethodCall, + kMoveAssignAfterMethodCall, + kNumberOfScenarios +}; + +struct CombinedTestConfiguration +{ + ProxyMoveScenario scenario; + std::string service_instance_manifest; +}; + +CombinedTestConfiguration ReadCommandLineArguments(int argc, const char** argv); + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_PROXY_METHOD_MOVE_SEMANTICS_TEST_PARAMETERS_H