diff --git a/score/mw/com/test/methods/edge_cases_test/BUILD b/score/mw/com/test/methods/edge_cases_test/BUILD new file mode 100644 index 000000000..1c05e7191 --- /dev/null +++ b/score/mw/com/test/methods/edge_cases_test/BUILD @@ -0,0 +1,100 @@ +# ******************************************************************************* +# 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("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") +load("//score/mw/com/test:pkg_application.bzl", "pkg_application") + +cc_binary( + name = "disabled_method_test", + srcs = ["disabled_method_test.cpp"], + data = ["config/disabled_methods_mw_com_config.json"], + features = COMPILER_WARNING_FEATURES + [ + "aborts_upon_exception", + ], + deps = [ + "//score/mw/com", + "//score/mw/com/test/common_test_resources:assert_handler", + "//score/mw/com/test/common_test_resources:command_line_parser", + "//score/mw/com/test/common_test_resources:fail_test", + "//score/mw/com/test/methods/methods_test_resources:proxy_container", + "//score/mw/com/test/methods/methods_test_resources/all_signatures_datatype", + "//score/mw/com/test/methods/methods_test_resources/all_signatures_datatype:all_signatures_method_provider", + "@score_baselibs//score/language/futurecpp", + ], +) + +cc_binary( + name = "incomplete_handlers_test", + srcs = ["incomplete_handlers_test.cpp"], + data = ["config/mw_com_config.json"], + features = COMPILER_WARNING_FEATURES + [ + "aborts_upon_exception", + ], + deps = [ + "//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/methods/methods_test_resources/all_signatures_datatype:all_signatures_method_provider", + "@score_baselibs//score/language/futurecpp", + ], +) + +cc_binary( + name = "proxy_recreation_test", + srcs = ["proxy_recreation_test.cpp"], + data = ["config/mw_com_config.json"], + features = COMPILER_WARNING_FEATURES + [ + "aborts_upon_exception", + ], + deps = [ + "//score/mw/com", + "//score/mw/com/test/common_test_resources:assert_handler", + "//score/mw/com/test/methods/methods_test_resources/all_signatures_datatype:all_signatures_method_consumer", + "//score/mw/com/test/methods/methods_test_resources/all_signatures_datatype:all_signatures_method_provider", + "@score_baselibs//score/language/futurecpp", + ], +) + +cc_binary( + name = "skeleton_recreation_test", + srcs = ["skeleton_recreation_test.cpp"], + data = ["config/mw_com_config.json"], + features = COMPILER_WARNING_FEATURES + [ + "aborts_upon_exception", + ], + deps = [ + "//score/mw/com", + "//score/mw/com/test/common_test_resources:assert_handler", + "//score/mw/com/test/methods/methods_test_resources/all_signatures_datatype:all_signatures_method_consumer", + "//score/mw/com/test/methods/methods_test_resources/all_signatures_datatype:all_signatures_method_provider", + "@score_baselibs//score/language/futurecpp", + ], +) + +pkg_application( + name = "edge-case-test-pkg", + app_name = "EdgeCasesTestApp", + bin = [ + ":disabled_method_test", + ":incomplete_handlers_test", + ":proxy_recreation_test", + ":skeleton_recreation_test", + ], + etc = [ + "config/disabled_methods_mw_com_config.json", + "config/mw_com_config.json", + ], + visibility = [ + "//score/mw/com/test/methods/edge_cases_test/integration_test:__subpackages__", + ], +) diff --git a/score/mw/com/test/methods/edge_cases_test/config/disabled_methods_mw_com_config.json b/score/mw/com/test/methods/edge_cases_test/config/disabled_methods_mw_com_config.json new file mode 100644 index 000000000..ebf74ae0e --- /dev/null +++ b/score/mw/com/test/methods/edge_cases_test/config/disabled_methods_mw_com_config.json @@ -0,0 +1,94 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/methods/edge_cases_test/MethodSignature", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 2000, + "methods": [ + { + "methodName": "with_in_args_and_return", + "methodId": 1 + }, + { + "methodName": "with_in_args_only", + "methodId": 2 + }, + { + "methodName": "with_return_only", + "methodId": 3 + }, + { + "methodName": "without_args_or_return", + "methodId": 4 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "test/methods/edge_cases_test/MethodSignature", + "serviceTypeName": "/test/methods/edge_cases_test/MethodSignature", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "with_in_args_and_return", + "queueSize": 1, + "use": false + }, + { + "methodName": "with_in_args_only", + "queueSize": 1, + "use": false + }, + { + "methodName": "with_return_only", + "queueSize": 1, + "use": false + }, + { + "methodName": "without_args_or_return", + "queueSize": 1, + "use": false + } + ], + "allowedConsumer": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + }, + "allowedProvider": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + } + + } + ] + } + ], + "global": { + "asil-level": "B" + } +} diff --git a/score/mw/com/test/methods/edge_cases_test/config/mw_com_config.json b/score/mw/com/test/methods/edge_cases_test/config/mw_com_config.json new file mode 100644 index 000000000..0753f7f2e --- /dev/null +++ b/score/mw/com/test/methods/edge_cases_test/config/mw_com_config.json @@ -0,0 +1,90 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/methods/edge_cases_test/MethodSignature", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 2000, + "methods": [ + { + "methodName": "with_in_args_and_return", + "methodId": 1 + }, + { + "methodName": "with_in_args_only", + "methodId": 2 + }, + { + "methodName": "with_return_only", + "methodId": 3 + }, + { + "methodName": "without_args_or_return", + "methodId": 4 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "test/methods/edge_cases_test/MethodSignature", + "serviceTypeName": "/test/methods/edge_cases_test/MethodSignature", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "with_in_args_and_return", + "queueSize": 1 + }, + { + "methodName": "with_in_args_only", + "queueSize": 1 + }, + { + "methodName": "with_return_only", + "queueSize": 1 + }, + { + "methodName": "without_args_or_return", + "queueSize": 1 + } + ], + "allowedConsumer": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + }, + "allowedProvider": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + } + + } + ] + } + ], + "global": { + "asil-level": "B" + } +} diff --git a/score/mw/com/test/methods/edge_cases_test/disabled_method_test.cpp b/score/mw/com/test/methods/edge_cases_test/disabled_method_test.cpp new file mode 100644 index 000000000..6991ef16e --- /dev/null +++ b/score/mw/com/test/methods/edge_cases_test/disabled_method_test.cpp @@ -0,0 +1,107 @@ +/******************************************************************************** + * 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/command_line_parser.h" +#include "score/mw/com/test/common_test_resources/fail_test.h" +#include "score/mw/com/test/methods/methods_test_resources/all_signatures_datatype/all_signatures_datatype.h" +#include "score/mw/com/test/methods/methods_test_resources/all_signatures_datatype/all_signatures_method_provider.h" +#include "score/mw/com/test/methods/methods_test_resources/proxy_container.h" + +#include +#include +#include +#include + +namespace score::mw::com::test +{ +namespace +{ + +const InstanceSpecifier kInstanceSpecifier = + InstanceSpecifier::Create(std::string{"test/methods/edge_cases_test/MethodSignature"}).value(); +const std::string kFailureMessagePrefix{"disabled_method_test"}; + +const std::string kServiceInstanceManifest{"service-instance-manifest"}; + +constexpr std::int32_t kReturnOnlyMethodReturnValue{15}; +constexpr std::int32_t kInArgOnlyMethodTestValueA{17}; +constexpr std::int32_t kInArgOnlyMethodTestValueB{18}; + +constexpr std::int32_t kTestValueA{10}; +constexpr std::int32_t kTestValueB{20}; + +std::string ParseServiceInstanceManifest(int argc, const char** argv) +{ + auto args = ParseCommandLineArguments(argc, argv, {{kServiceInstanceManifest, ""}}); + return {GetValue(args, kServiceInstanceManifest)}; +} + +template +void FailIfMethodCallDidNotFail(const std::string& method_name, const ResultType& call_result) +{ + if (call_result.has_value()) + { + FailTest(kFailureMessagePrefix, " Consumer: ", method_name, " unexpectedly succeeded."); + return; + } + + std::cout << "Consumer: " << method_name << " failed as expected: " << call_result.error() << std::endl; +} + +void RunDisabledMethodTest() +{ + std::cout << "\n=== Disabled methods return errors ===" << std::endl; + + std::cout << "\nProvider: Step 1: Create skeleton" << std::endl; + AllSignaturesMethodProvider provider{}; + provider.CreateSkeleton(kInstanceSpecifier, kFailureMessagePrefix); + + std::cout << "\nProvider: Step 2: Register method handlers" << std::endl; + provider.RegisterMethodHandlerWithInArgsAndReturn(kFailureMessagePrefix); + provider.RegisterMethodHandlerWithInArgsOnly( + kInArgOnlyMethodTestValueA, kInArgOnlyMethodTestValueB, kFailureMessagePrefix); + provider.RegisterMethodHandlerWithReturnOnly(kReturnOnlyMethodReturnValue, kFailureMessagePrefix); + provider.RegisterWithoutInArgsOrReturn(kFailureMessagePrefix); + + std::cout << "\nProvider: Step 3: Offer Service" << std::endl; + provider.OfferService(kFailureMessagePrefix); + + std::cout << "\nConsumer: Step 4: Find service and create proxy" << std::endl; + ProxyContainer proxy_container{}; + proxy_container.CreateProxy(kInstanceSpecifier, kFailureMessagePrefix); + auto& proxy = proxy_container.GetProxy(); + + std::cout << "\nConsumer: Step 5: Call methods which have been disabled in the C++ interface" << std::endl; + FailIfMethodCallDidNotFail("with_in_args_and_return", proxy.with_in_args_and_return(kTestValueA, kTestValueB)); + FailIfMethodCallDidNotFail("with_in_args_only", proxy.with_in_args_only(kTestValueA, kTestValueB)); + FailIfMethodCallDidNotFail("with_return_only", proxy.with_return_only()); + FailIfMethodCallDidNotFail("without_args_or_return", proxy.without_args_or_return()); + + std::cout << "=== Disabled method test: PASSED ===" << std::endl; +} + +} // namespace +} // namespace score::mw::com::test + +int main(int argc, const char** argv) +{ + const auto service_instance_manifest = score::mw::com::test::ParseServiceInstanceManifest(argc, argv); + + score::mw::com::test::SetupAssertHandler(); + score::mw::com::runtime::InitializeRuntime( + score::mw::com::runtime::RuntimeConfiguration{service_instance_manifest}); + + score::mw::com::test::RunDisabledMethodTest(); + return EXIT_SUCCESS; +} diff --git a/score/mw/com/test/methods/edge_cases_test/incomplete_handlers_test.cpp b/score/mw/com/test/methods/edge_cases_test/incomplete_handlers_test.cpp new file mode 100644 index 000000000..684d21864 --- /dev/null +++ b/score/mw/com/test/methods/edge_cases_test/incomplete_handlers_test.cpp @@ -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 + ********************************************************************************/ +#include "score/mw/com/test/common_test_resources/assert_handler.h" +#include "score/mw/com/test/common_test_resources/fail_test.h" +#include "score/mw/com/test/methods/methods_test_resources/all_signatures_datatype/all_signatures_method_provider.h" + +#include "score/mw/com/runtime.h" + +#include +#include +#include + +namespace score::mw::com::test +{ +namespace +{ + +const InstanceSpecifier kInstanceSpecifier = + InstanceSpecifier::Create(std::string{"test/methods/edge_cases_test/MethodSignature"}).value(); +const std::string kFailureMessagePrefix{"incomplete_handlers_test"}; + +constexpr std::int32_t kInArgOnlyMethodTestValueA{17}; +constexpr std::int32_t kInArgOnlyMethodTestValueB{18}; + +void RunIncompleteHandlersTest() +{ + std::cout << "\n=== Incomplete handlers fail OfferService ===" << std::endl; + + std::cout << "\nProvider: Step 1: Create skeleton" << std::endl; + AllSignaturesMethodProvider provider{}; + provider.CreateSkeleton(kInstanceSpecifier, kFailureMessagePrefix); + + std::cout << "\nProvider: Register only 2 of 4 handlers" << std::endl; + provider.RegisterMethodHandlerWithInArgsAndReturn(kFailureMessagePrefix); + provider.RegisterMethodHandlerWithInArgsOnly( + kInArgOnlyMethodTestValueA, kInArgOnlyMethodTestValueB, kFailureMessagePrefix); + + // Step 3. Try to offer service - should fail + std::cout << "\nProvider: Try to offer service - should fail" << std::endl; + const auto was_successfully_offered = provider.GetSkeleton().OfferService(); + if (was_successfully_offered) + { + FailTest("ERROR: OfferService succeeded when it should have failed!"); + return; + } + + std::cout << "=== Incomplete handlers test: PASSED ===" << std::endl; +} + +} // namespace +} // namespace score::mw::com::test + +int main(int argc, const char** argv) +{ + score::mw::com::test::SetupAssertHandler(); + score::mw::com::runtime::InitializeRuntime(argc, argv); + + score::mw::com::test::RunIncompleteHandlersTest(); + return EXIT_SUCCESS; +} diff --git a/score/mw/com/test/methods/edge_cases_test/integration_test/BUILD b/score/mw/com/test/methods/edge_cases_test/integration_test/BUILD new file mode 100644 index 000000000..bd96ea870 --- /dev/null +++ b/score/mw/com/test/methods/edge_cases_test/integration_test/BUILD @@ -0,0 +1,53 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +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/methods/edge_cases_test:edge-case-test-pkg", + ], +) + +integration_test( + name = "disabled_method_test", + srcs = [ + "disabled_method_test.py", + ], + filesystem = ":filesystem", +) + +integration_test( + name = "incomplete_handlers_test", + srcs = [ + "incomplete_handlers_test.py", + ], + filesystem = ":filesystem", +) + +integration_test( + name = "proxy_recreation_test", + srcs = [ + "proxy_recreation_test.py", + ], + filesystem = ":filesystem", +) + +integration_test( + name = "skeleton_recreation_test", + srcs = [ + "skeleton_recreation_test.py", + ], + filesystem = ":filesystem", +) diff --git a/score/mw/com/test/methods/edge_cases_test/integration_test/disabled_method_test.py b/score/mw/com/test/methods/edge_cases_test/integration_test/disabled_method_test.py new file mode 100644 index 000000000..01bfd848f --- /dev/null +++ b/score/mw/com/test/methods/edge_cases_test/integration_test/disabled_method_test.py @@ -0,0 +1,16 @@ +# ******************************************************************************* +# 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 test_edge_cases(target): + args = ["--service-instance-manifest", "./etc/disabled_methods_mw_com_config.json"] + with target.wrap_exec("bin/disabled_method_test", args, cwd="/opt/EdgeCasesTestApp", wait_on_exit=True) as process: + pass diff --git a/score/mw/com/test/methods/edge_cases_test/integration_test/incomplete_handlers_test.py b/score/mw/com/test/methods/edge_cases_test/integration_test/incomplete_handlers_test.py new file mode 100644 index 000000000..ad7aa7f50 --- /dev/null +++ b/score/mw/com/test/methods/edge_cases_test/integration_test/incomplete_handlers_test.py @@ -0,0 +1,15 @@ +# ******************************************************************************* +# 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 test_edge_cases(target): + with target.wrap_exec("bin/incomplete_handlers_test", cwd="/opt/EdgeCasesTestApp", wait_on_exit=True) as process: + pass diff --git a/score/mw/com/test/methods/edge_cases_test/integration_test/proxy_recreation_test.py b/score/mw/com/test/methods/edge_cases_test/integration_test/proxy_recreation_test.py new file mode 100644 index 000000000..eb645aee2 --- /dev/null +++ b/score/mw/com/test/methods/edge_cases_test/integration_test/proxy_recreation_test.py @@ -0,0 +1,15 @@ +# ******************************************************************************* +# 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 test_edge_cases(target): + with target.wrap_exec("bin/proxy_recreation_test", cwd="/opt/EdgeCasesTestApp", wait_on_exit=True) as process: + pass diff --git a/score/mw/com/test/methods/edge_cases_test/integration_test/skeleton_recreation_test.py b/score/mw/com/test/methods/edge_cases_test/integration_test/skeleton_recreation_test.py new file mode 100644 index 000000000..103a9fb47 --- /dev/null +++ b/score/mw/com/test/methods/edge_cases_test/integration_test/skeleton_recreation_test.py @@ -0,0 +1,15 @@ +# ******************************************************************************* +# 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 test_edge_cases(target): + with target.wrap_exec("bin/skeleton_recreation_test", cwd="/opt/EdgeCasesTestApp", wait_on_exit=True) as process: + pass diff --git a/score/mw/com/test/methods/edge_cases_test/proxy_recreation_test.cpp b/score/mw/com/test/methods/edge_cases_test/proxy_recreation_test.cpp new file mode 100644 index 000000000..71bee7bf1 --- /dev/null +++ b/score/mw/com/test/methods/edge_cases_test/proxy_recreation_test.cpp @@ -0,0 +1,92 @@ +/******************************************************************************** + * 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/assert_handler.h" +#include "score/mw/com/test/methods/methods_test_resources/all_signatures_datatype/all_signatures_method_consumer.h" +#include "score/mw/com/test/methods/methods_test_resources/all_signatures_datatype/all_signatures_method_provider.h" + +#include "score/mw/com/runtime.h" + +#include +#include +#include + +namespace score::mw::com::test +{ +namespace +{ + +const InstanceSpecifier kInstanceSpecifier = + InstanceSpecifier::Create(std::string{"test/methods/edge_cases_test/MethodSignature"}).value(); +const std::string kFailureMessagePrefix{"proxy_recreation_test"}; + +constexpr std::int32_t kReturnOnlyMethodReturnValue{15}; +constexpr std::int32_t kInArgOnlyMethodTestValueA{17}; +constexpr std::int32_t kInArgOnlyMethodTestValueB{18}; + +constexpr std::int32_t kTestValueA{10}; +constexpr std::int32_t kTestValueB{20}; + +void RunProxyRecreationTest() +{ + std::cout << "\n=== Proxy recreation ===" << std::endl; + + AllSignaturesMethodProvider provider{}; + + std::cout << "\nProvider: Step 1: Create skeleton" << std::endl; + provider.CreateSkeleton(kInstanceSpecifier, kFailureMessagePrefix); + + std::cout << "\nProvider: Step 2: Register method handlers" << std::endl; + provider.RegisterMethodHandlerWithInArgsAndReturn(kFailureMessagePrefix); + provider.RegisterMethodHandlerWithInArgsOnly( + kInArgOnlyMethodTestValueA, kInArgOnlyMethodTestValueB, kFailureMessagePrefix); + provider.RegisterMethodHandlerWithReturnOnly(kReturnOnlyMethodReturnValue, kFailureMessagePrefix); + provider.RegisterWithoutInArgsOrReturn(kFailureMessagePrefix); + + std::cout << "\nProvider: Step 3: Offer Service" << std::endl; + provider.OfferService(kFailureMessagePrefix); + + { + std::cout << "\nConsumer: Step 4: Find service and create proxy" << std::endl; + AllSignaturesMethodConsumer consumer{}; + consumer.CreateProxy(kInstanceSpecifier, kFailureMessagePrefix); + + std::cout << "\nConsumer: Step 5: Call method" << std::endl; + consumer.CallMethodWithInArgsAndReturn( + kTestValueA, kTestValueB, AllSignaturesMethodConsumer::CopyMode::WITH_COPY, kFailureMessagePrefix); + + std::cout << "\nConsumer: Step 6: Destroy proxy" << std::endl; + } + + { + std::cout << "\nConsumer: Step 7: Find service and create second proxy" << std::endl; + AllSignaturesMethodConsumer consumer{}; + consumer.CreateProxy(kInstanceSpecifier, kFailureMessagePrefix); + + std::cout << "\nConsumer: Step 8: Call method" << std::endl; + consumer.CallMethodWithInArgsAndReturn( + kTestValueA, kTestValueB, AllSignaturesMethodConsumer::CopyMode::WITH_COPY, kFailureMessagePrefix); + } + std::cout << "=== Proxy recreation test: PASSED ===" << std::endl; +} + +} // namespace +} // namespace score::mw::com::test + +int main(int argc, const char** argv) +{ + score::mw::com::test::SetupAssertHandler(); + score::mw::com::runtime::InitializeRuntime(argc, argv); + + score::mw::com::test::RunProxyRecreationTest(); + return EXIT_SUCCESS; +} diff --git a/score/mw/com/test/methods/edge_cases_test/skeleton_recreation_test.cpp b/score/mw/com/test/methods/edge_cases_test/skeleton_recreation_test.cpp new file mode 100644 index 000000000..58d31c2af --- /dev/null +++ b/score/mw/com/test/methods/edge_cases_test/skeleton_recreation_test.cpp @@ -0,0 +1,166 @@ +/******************************************************************************** + * 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/assert_handler.h" +#include "score/mw/com/test/methods/methods_test_resources/all_signatures_datatype/all_signatures_method_consumer.h" +#include "score/mw/com/test/methods/methods_test_resources/all_signatures_datatype/all_signatures_method_provider.h" + +#include "score/mw/com/runtime.h" + +#include +#include +#include + +namespace score::mw::com::test +{ +namespace +{ + +const InstanceSpecifier kInstanceSpecifier = + InstanceSpecifier::Create(std::string{"test/methods/edge_cases_test/MethodSignature"}).value(); +const std::string kFailureMessagePrefix{"skeleton_recreation_test"}; + +constexpr std::int32_t kReturnOnlyMethodReturnValue{15}; +constexpr std::int32_t kInArgOnlyMethodTestValueA{17}; +constexpr std::int32_t kInArgOnlyMethodTestValueB{18}; + +constexpr std::int32_t kTestValueA{10}; +constexpr std::int32_t kTestValueB{20}; + +void RunSkeletonRecreationWithNewProxyTest() +{ + std::cout << "\n=== Skeleton recreation with new proxy ===" << std::endl; + + { + std::cout << "\nProvider: Step 1: Create skeleton" << std::endl; + AllSignaturesMethodProvider provider{}; + provider.CreateSkeleton(kInstanceSpecifier, kFailureMessagePrefix); + + std::cout << "\nProvider: Step 2: Register method handlers" << std::endl; + provider.RegisterMethodHandlerWithInArgsAndReturn(kFailureMessagePrefix); + provider.RegisterMethodHandlerWithInArgsOnly( + kInArgOnlyMethodTestValueA, kInArgOnlyMethodTestValueB, kFailureMessagePrefix); + provider.RegisterMethodHandlerWithReturnOnly(kReturnOnlyMethodReturnValue, kFailureMessagePrefix); + provider.RegisterWithoutInArgsOrReturn(kFailureMessagePrefix); + + std::cout << "\nProvider: Step 3: Offer Service" << std::endl; + provider.OfferService(kFailureMessagePrefix); + + std::cout << "\nConsumer: Step 4: Find service and create proxy" << std::endl; + AllSignaturesMethodConsumer consumer{}; + consumer.CreateProxy(kInstanceSpecifier, kFailureMessagePrefix); + + std::cout << "\nConsumer: Step 5: Call method" << std::endl; + consumer.CallMethodWithInArgsAndReturn( + kTestValueA, kTestValueB, AllSignaturesMethodConsumer::CopyMode::WITH_COPY, kFailureMessagePrefix); + + std::cout << "\nConsumer: Step 6: Destroy proxy and skeleton" << std::endl; + } + + { + std::cout << "\nProvider: Step 7: Create second skeleton" << std::endl; + AllSignaturesMethodProvider provider{}; + provider.CreateSkeleton(kInstanceSpecifier, kFailureMessagePrefix); + + std::cout << "\nProvider: Step 8: Register method handlers" << std::endl; + provider.RegisterMethodHandlerWithInArgsAndReturn(kFailureMessagePrefix); + provider.RegisterMethodHandlerWithInArgsOnly( + kInArgOnlyMethodTestValueA, kInArgOnlyMethodTestValueB, kFailureMessagePrefix); + provider.RegisterMethodHandlerWithReturnOnly(kReturnOnlyMethodReturnValue, kFailureMessagePrefix); + provider.RegisterWithoutInArgsOrReturn(kFailureMessagePrefix); + + std::cout << "\nProvider: Step 9: Offer second service" << std::endl; + provider.OfferService(kFailureMessagePrefix); + + std::cout << "\nConsumer: Step 10: Find service and create second proxy" << std::endl; + AllSignaturesMethodConsumer consumer{}; + consumer.CreateProxy(kInstanceSpecifier, kFailureMessagePrefix); + + std::cout << "\nConsumer: Step 11: Call the same method again" << std::endl; + consumer.CallMethodWithInArgsAndReturn( + kTestValueA, kTestValueB, AllSignaturesMethodConsumer::CopyMode::WITH_COPY, kFailureMessagePrefix); + } +} + +void RunSkeletonRecreationWithSameProxyTest() +{ + std::cout << "\n=== Skeleton recreation with same proxy ===" << std::endl; + + AllSignaturesMethodConsumer consumer{}; + { + std::cout << "\nProvider: Step 1: Create skeleton" << std::endl; + AllSignaturesMethodProvider provider{}; + provider.CreateSkeleton(kInstanceSpecifier, kFailureMessagePrefix); + + std::cout << "\nProvider: Step 2: Register method handlers" << std::endl; + provider.RegisterMethodHandlerWithInArgsAndReturn(kFailureMessagePrefix); + provider.RegisterMethodHandlerWithInArgsOnly( + kInArgOnlyMethodTestValueA, kInArgOnlyMethodTestValueB, kFailureMessagePrefix); + provider.RegisterMethodHandlerWithReturnOnly(kReturnOnlyMethodReturnValue, kFailureMessagePrefix); + provider.RegisterWithoutInArgsOrReturn(kFailureMessagePrefix); + + std::cout << "\nProvider: Step 3: Offer Service" << std::endl; + provider.OfferService(kFailureMessagePrefix); + + std::cout << "\nConsumer: Step 4: Find service and create proxy" << std::endl; + consumer.CreateProxy(kInstanceSpecifier, kFailureMessagePrefix); + + std::cout << "\nConsumer: Step 5: Call method" << std::endl; + consumer.CallMethodWithInArgsAndReturn( + kTestValueA, kTestValueB, AllSignaturesMethodConsumer::CopyMode::WITH_COPY, kFailureMessagePrefix); + + std::cout << "\nConsumer: Step 6: Destroy skeleton only" << std::endl; + } + + { + std::cout << "\nProvider: Step 7: Create second skeleton" << std::endl; + AllSignaturesMethodProvider provider{}; + provider.CreateSkeleton(kInstanceSpecifier, kFailureMessagePrefix); + + std::cout << "\nProvider: Step 8: Register method handlers" << std::endl; + provider.RegisterMethodHandlerWithInArgsAndReturn(kFailureMessagePrefix); + provider.RegisterMethodHandlerWithInArgsOnly( + kInArgOnlyMethodTestValueA, kInArgOnlyMethodTestValueB, kFailureMessagePrefix); + provider.RegisterMethodHandlerWithReturnOnly(kReturnOnlyMethodReturnValue, kFailureMessagePrefix); + provider.RegisterWithoutInArgsOrReturn(kFailureMessagePrefix); + + std::cout << "\nProvider: Step 9: Offer second service" << std::endl; + provider.OfferService(kFailureMessagePrefix); + + std::cout << "\nConsumer: Step 10: Call method again on old proxy" << std::endl; + + std::cout << "\nConsumer: Step 10.1: Wait until proxy has reconnected to recreated skeleton" << std::endl; + // Since we have no way of being notified of the proxy being reconnected to the new skeleton, we keep calling a + // method in a loop until it succeeds. If it succeeded then it indicates that the proxy reconnected. + while (!consumer.GetProxy().without_args_or_return().has_value()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(30)); + } + + std::cout << "\nConsumer: Step 10.2: Call the same method again" << std::endl; + consumer.CallMethodWithInArgsAndReturn( + kTestValueA, kTestValueB, AllSignaturesMethodConsumer::CopyMode::WITH_COPY, kFailureMessagePrefix); + } +} + +} // namespace +} // namespace score::mw::com::test + +int main(int argc, const char** argv) +{ + score::mw::com::test::SetupAssertHandler(); + score::mw::com::runtime::InitializeRuntime(argc, argv); + + score::mw::com::test::RunSkeletonRecreationWithNewProxyTest(); + score::mw::com::test::RunSkeletonRecreationWithSameProxyTest(); + return EXIT_SUCCESS; +} diff --git a/score/mw/com/test/methods/mixed_criticality/BUILD b/score/mw/com/test/methods/mixed_criticality/BUILD new file mode 100644 index 000000000..6f2c52a04 --- /dev/null +++ b/score/mw/com/test/methods/mixed_criticality/BUILD @@ -0,0 +1,152 @@ +# ******************************************************************************* +# 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("@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_asil_b_to_asil_b", + json = "config/consumer_asil_b_to_asil_b_mw_com_config.json", + schema = "//score/mw/com:config_schema", + tags = ["lint"], +) + +validate_json_schema_test( + name = "validate_config_schema_asil_b_to_qm", + json = "config/consumer_asil_b_to_qm_mw_com_config.json", + schema = "//score/mw/com:config_schema", + tags = ["lint"], +) + +validate_json_schema_test( + name = "validate_config_schema_qm_to_qm", + json = "config/consumer_qm_to_qm_mw_com_config.json", + schema = "//score/mw/com:config_schema", + tags = ["lint"], +) + +validate_json_schema_test( + name = "validate_config_schema_provider_asil_b", + json = "config/provider_asil_b_mw_com_config.json", + schema = "//score/mw/com:config_schema", + tags = ["lint"], +) + +validate_json_schema_test( + name = "validate_config_schema", + json = "config/provider_qm_mw_com_config.json", + schema = "//score/mw/com:config_schema", + tags = ["lint"], +) + +cc_library( + name = "common_resources", + srcs = ["common_resources.cpp"], + hdrs = ["common_resources.h"], + features = COMPILER_WARNING_FEATURES, + visibility = ["//score/mw/com/test/methods:__subpackages__"], + deps = [ + "//score/mw/com/test/common_test_resources:command_line_parser", + "//score/mw/com/test/common_test_resources:fail_test", + ], +) + +cc_library( + name = "consumer", + srcs = ["consumer.cpp"], + hdrs = ["consumer.h"], + features = COMPILER_WARNING_FEATURES, + implementation_deps = [ + "//score/mw/com", + "//score/mw/com/test/methods/methods_test_resources:process_synchronizer", + "//score/mw/com/test/methods/methods_test_resources/all_signatures_datatype", + "//score/mw/com/test/methods/methods_test_resources/all_signatures_datatype:all_signatures_method_consumer", + ], + deps = [ + "@score_baselibs//score/language/futurecpp", + ], +) + +cc_library( + name = "provider", + srcs = ["provider.cpp"], + hdrs = ["provider.h"], + features = COMPILER_WARNING_FEATURES, + deps = [ + "//score/mw/com", + "//score/mw/com/test/methods/methods_test_resources:process_synchronizer", + "//score/mw/com/test/methods/methods_test_resources/all_signatures_datatype", + "//score/mw/com/test/methods/methods_test_resources/all_signatures_datatype:all_signatures_method_provider", + ], +) + +cc_binary( + name = "provider_main", + srcs = ["provider_main.cpp"], + features = COMPILER_WARNING_FEATURES + [ + "aborts_upon_exception", + ], + deps = [ + ":common_resources", + ":provider", + "//score/mw/com/test/common_test_resources:assert_handler", + ], +) + +cc_binary( + name = "consumer_main", + srcs = ["consumer_main.cpp"], + features = COMPILER_WARNING_FEATURES + [ + "aborts_upon_exception", + ], + deps = [ + ":common_resources", + ":consumer", + "//score/mw/com/test/common_test_resources:assert_handler", + ], +) + +# --- Packages --- + +pkg_application( + name = "provider-pkg", + app_name = "ProviderApp", + bin = [":provider_main"], + etc = [ + "config/consumer_asil_b_to_asil_b_mw_com_config.json", + "config/consumer_asil_b_to_qm_mw_com_config.json", + "config/consumer_qm_to_qm_mw_com_config.json", + "config/provider_asil_b_mw_com_config.json", + "config/provider_qm_mw_com_config.json", + ], + visibility = [ + "//score/mw/com/test/methods/mixed_criticality/integration_test:__pkg__", + ], +) + +pkg_application( + name = "consumer-pkg", + app_name = "ConsumerApp", + bin = [":consumer_main"], + etc = [ + "config/consumer_asil_b_to_asil_b_mw_com_config.json", + "config/consumer_asil_b_to_qm_mw_com_config.json", + "config/consumer_qm_to_qm_mw_com_config.json", + "config/provider_asil_b_mw_com_config.json", + "config/provider_qm_mw_com_config.json", + ], + visibility = [ + "//score/mw/com/test/methods/mixed_criticality/integration_test:__pkg__", + ], +) diff --git a/score/mw/com/test/methods/mixed_criticality/common_resources.cpp b/score/mw/com/test/methods/mixed_criticality/common_resources.cpp new file mode 100644 index 000000000..e185c41cb --- /dev/null +++ b/score/mw/com/test/methods/mixed_criticality/common_resources.cpp @@ -0,0 +1,29 @@ +/******************************************************************************** + * 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/methods/mixed_criticality/common_resources.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 +{ + +auto ParseServiceInstanceManifest(int argc, const char** argv) -> std::string +{ + const std::string service_instance_manifest_name = "service_instance_manifest"; + + auto args = ParseCommandLineArguments(argc, argv, {{service_instance_manifest_name, ""}}); + return GetValue(args, service_instance_manifest_name); +} + +} // namespace score::mw::com::test diff --git a/score/mw/com/test/methods/mixed_criticality/common_resources.h b/score/mw/com/test/methods/mixed_criticality/common_resources.h new file mode 100644 index 000000000..cb9cfc0db --- /dev/null +++ b/score/mw/com/test/methods/mixed_criticality/common_resources.h @@ -0,0 +1,32 @@ +/******************************************************************************** + * 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_METHODS_MIXED_CRITICALITY_COMMON_RESOURCES_H +#define SCORE_MW_COM_TEST_METHODS_MIXED_CRITICALITY_COMMON_RESOURCES_H + +#include + +/// This file contains information shared between the provider and consumer. + +namespace score::mw::com::test +{ + +/// \brief Parses the command line arguments for the service instance manifest path and returns it. +/// +/// Terminates if the argument is not provided. We use this function instead of providing argc / argv directly to +/// InitializeRuntime so that we detect if the config file was not provided instead of silently falling back to the +/// default config. It also makes it easier to extend the command line arguments in the future if needed. +std::string ParseServiceInstanceManifest(int argc, const char** argv); + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_METHODS_MIXED_CRITICALITY_COMMON_RESOURCES_H diff --git a/score/mw/com/test/methods/mixed_criticality/config/consumer_asil_b_to_asil_b_mw_com_config.json b/score/mw/com/test/methods/mixed_criticality/config/consumer_asil_b_to_asil_b_mw_com_config.json new file mode 100644 index 000000000..2208a2f13 --- /dev/null +++ b/score/mw/com/test/methods/mixed_criticality/config/consumer_asil_b_to_asil_b_mw_com_config.json @@ -0,0 +1,90 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/methods/mixed_criticality/MethodSignature", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 2000, + "methods": [ + { + "methodName": "with_in_args_and_return", + "methodId": 1 + }, + { + "methodName": "with_in_args_only", + "methodId": 2 + }, + { + "methodName": "with_return_only", + "methodId": 3 + }, + { + "methodName": "without_args_or_return", + "methodId": 4 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "test/methods/mixed_criticality/MethodSignature", + "serviceTypeName": "/test/methods/mixed_criticality/MethodSignature", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "with_in_args_and_return", + "queueSize": 1 + }, + { + "methodName": "with_in_args_only", + "queueSize": 1 + }, + { + "methodName": "with_return_only", + "queueSize": 1 + }, + { + "methodName": "without_args_or_return", + "queueSize": 1 + } + ], + "allowedConsumer": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + }, + "allowedProvider": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + } + + } + ] + } + ], + "global": { + "asil-level": "B" + } +} diff --git a/score/mw/com/test/methods/mixed_criticality/config/consumer_asil_b_to_qm_mw_com_config.json b/score/mw/com/test/methods/mixed_criticality/config/consumer_asil_b_to_qm_mw_com_config.json new file mode 100644 index 000000000..0940ce01f --- /dev/null +++ b/score/mw/com/test/methods/mixed_criticality/config/consumer_asil_b_to_qm_mw_com_config.json @@ -0,0 +1,90 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/methods/mixed_criticality/MethodSignature", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 2000, + "methods": [ + { + "methodName": "with_in_args_and_return", + "methodId": 1 + }, + { + "methodName": "with_in_args_only", + "methodId": 2 + }, + { + "methodName": "with_return_only", + "methodId": 3 + }, + { + "methodName": "without_args_or_return", + "methodId": 4 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "test/methods/mixed_criticality/MethodSignature", + "serviceTypeName": "/test/methods/mixed_criticality/MethodSignature", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "QM", + "binding": "SHM", + "methods": [ + { + "methodName": "with_in_args_and_return", + "queueSize": 1 + }, + { + "methodName": "with_in_args_only", + "queueSize": 1 + }, + { + "methodName": "with_return_only", + "queueSize": 1 + }, + { + "methodName": "without_args_or_return", + "queueSize": 1 + } + ], + "allowedConsumer": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + }, + "allowedProvider": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + } + + } + ] + } + ], + "global": { + "asil-level": "B" + } +} diff --git a/score/mw/com/test/methods/signature_variations/config/mw_com_config.json b/score/mw/com/test/methods/mixed_criticality/config/consumer_qm_to_qm_mw_com_config.json similarity index 87% rename from score/mw/com/test/methods/signature_variations/config/mw_com_config.json rename to score/mw/com/test/methods/mixed_criticality/config/consumer_qm_to_qm_mw_com_config.json index 6bd0c1c14..f5df3e073 100644 --- a/score/mw/com/test/methods/signature_variations/config/mw_com_config.json +++ b/score/mw/com/test/methods/mixed_criticality/config/consumer_qm_to_qm_mw_com_config.json @@ -1,7 +1,7 @@ { "serviceTypes": [ { - "serviceTypeName": "/test/methods/signature_variations/MethodSignature", + "serviceTypeName": "/test/methods/mixed_criticality/MethodSignature", "version": { "major": 1, "minor": 0 @@ -34,8 +34,8 @@ ], "serviceInstances": [ { - "instanceSpecifier": "test/methods/signature_variations/MethodSignature", - "serviceTypeName": "/test/methods/signature_variations/MethodSignature", + "instanceSpecifier": "test/methods/mixed_criticality/MethodSignature", + "serviceTypeName": "/test/methods/mixed_criticality/MethodSignature", "version": { "major": 1, "minor": 0 diff --git a/score/mw/com/test/methods/mixed_criticality/config/provider_asil_b_mw_com_config.json b/score/mw/com/test/methods/mixed_criticality/config/provider_asil_b_mw_com_config.json new file mode 100644 index 000000000..2208a2f13 --- /dev/null +++ b/score/mw/com/test/methods/mixed_criticality/config/provider_asil_b_mw_com_config.json @@ -0,0 +1,90 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/methods/mixed_criticality/MethodSignature", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 2000, + "methods": [ + { + "methodName": "with_in_args_and_return", + "methodId": 1 + }, + { + "methodName": "with_in_args_only", + "methodId": 2 + }, + { + "methodName": "with_return_only", + "methodId": 3 + }, + { + "methodName": "without_args_or_return", + "methodId": 4 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "test/methods/mixed_criticality/MethodSignature", + "serviceTypeName": "/test/methods/mixed_criticality/MethodSignature", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "with_in_args_and_return", + "queueSize": 1 + }, + { + "methodName": "with_in_args_only", + "queueSize": 1 + }, + { + "methodName": "with_return_only", + "queueSize": 1 + }, + { + "methodName": "without_args_or_return", + "queueSize": 1 + } + ], + "allowedConsumer": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + }, + "allowedProvider": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + } + + } + ] + } + ], + "global": { + "asil-level": "B" + } +} diff --git a/score/mw/com/test/methods/mixed_criticality/config/provider_qm_mw_com_config.json b/score/mw/com/test/methods/mixed_criticality/config/provider_qm_mw_com_config.json new file mode 100644 index 000000000..f5df3e073 --- /dev/null +++ b/score/mw/com/test/methods/mixed_criticality/config/provider_qm_mw_com_config.json @@ -0,0 +1,90 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/methods/mixed_criticality/MethodSignature", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 2000, + "methods": [ + { + "methodName": "with_in_args_and_return", + "methodId": 1 + }, + { + "methodName": "with_in_args_only", + "methodId": 2 + }, + { + "methodName": "with_return_only", + "methodId": 3 + }, + { + "methodName": "without_args_or_return", + "methodId": 4 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "test/methods/mixed_criticality/MethodSignature", + "serviceTypeName": "/test/methods/mixed_criticality/MethodSignature", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "QM", + "binding": "SHM", + "methods": [ + { + "methodName": "with_in_args_and_return", + "queueSize": 1 + }, + { + "methodName": "with_in_args_only", + "queueSize": 1 + }, + { + "methodName": "with_return_only", + "queueSize": 1 + }, + { + "methodName": "without_args_or_return", + "queueSize": 1 + } + ], + "allowedConsumer": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + }, + "allowedProvider": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + } + + } + ] + } + ], + "global": { + "asil-level": "QM" + } +} diff --git a/score/mw/com/test/methods/mixed_criticality/consumer.cpp b/score/mw/com/test/methods/mixed_criticality/consumer.cpp new file mode 100644 index 000000000..1e0ae4e98 --- /dev/null +++ b/score/mw/com/test/methods/mixed_criticality/consumer.cpp @@ -0,0 +1,103 @@ +/******************************************************************************** + * 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/methods/mixed_criticality/consumer.h" + +#include "score/mw/com/test/common_test_resources/fail_test.h" +#include "score/mw/com/test/methods/methods_test_resources/all_signatures_datatype/all_signatures_method_consumer.h" +#include "score/mw/com/test/methods/methods_test_resources/process_synchronizer.h" +#include "score/mw/com/types.h" + +#include + +#include +#include + +namespace score::mw::com::test +{ +namespace +{ + +const std::string kInterprocessNotificationShmPath{"/mixed_criticality_test_interprocess_notification"}; +const std::string kFailureMessagePrefix{"mixed_criticality"}; + +// Test values for methods with InArgs and Return +constexpr std::int32_t kTestValueA = 42; +constexpr std::int32_t kTestValueB = 23; + +// Test values for method with Return only +const std::int32_t kReturnOnlyMethodReturnValue{15}; + +// Test values for method with InArgs only +const std::int32_t kInArgOnlyMethodTestValueA{17}; +const std::int32_t kInArgOnlyMethodTestValueB{18}; + +const InstanceSpecifier kInstanceSpecifier = + InstanceSpecifier::Create(std::string{"test/methods/mixed_criticality/MethodSignature"}).value(); + +} // namespace + +void run_consumer() +{ + AllSignaturesMethodConsumer consumer{}; + auto process_synchronizer_result = ProcessSynchronizer::Create(kInterprocessNotificationShmPath); + if (!(process_synchronizer_result.has_value())) + { + FailTest("Methods mixed_criticality consumer failed: Could not create ProcessSynchronizer"); + return; + } + + // Set an exit function to notify the provider in case of failure in calls to FailTest, so that it does not wait + // indefinitely for the consumer to finish. Will also be called when the guard goes out of scope at the end of + // this function. + 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; + consumer.CreateProxy(kInstanceSpecifier, kFailureMessagePrefix); + + // Step 2. Call method with InArgs and Return with copy + std::cout << "\nConsumer: Step 2" << std::endl; + consumer.CallMethodWithInArgsAndReturn( + kTestValueA, kTestValueB, AllSignaturesMethodConsumer::CopyMode::WITH_COPY, kFailureMessagePrefix); + + // Step 3. Call zero-copy method with InArgs and Return + std::cout << "\nConsumer: Step 3" << std::endl; + consumer.CallMethodWithInArgsAndReturn( + kTestValueA, kTestValueB, AllSignaturesMethodConsumer::CopyMode::ZERO_COPY, kFailureMessagePrefix); + + // Step 4. Call method with InArgs only with copy + std::cout << "\nConsumer: Step 4" << std::endl; + consumer.CallMethodWithInArgsOnly(kInArgOnlyMethodTestValueA, + kInArgOnlyMethodTestValueB, + AllSignaturesMethodConsumer::CopyMode::WITH_COPY, + kFailureMessagePrefix); + + // Step 5. Call zero-copy method with InArgs only + std::cout << "\nConsumer: Step 5" << std::endl; + consumer.CallMethodWithInArgsOnly(kInArgOnlyMethodTestValueA, + kInArgOnlyMethodTestValueB, + AllSignaturesMethodConsumer::CopyMode::ZERO_COPY, + kFailureMessagePrefix); + + // Step 6. Call method with return only with copy + std::cout << "\nConsumer: Step 6" << std::endl; + consumer.CallMethodWithReturnOnly(kReturnOnlyMethodReturnValue, kFailureMessagePrefix); + + // Step 7. Call method without InArgs or return + std::cout << "\nConsumer: Step 7" << std::endl; + consumer.CallMethodWithoutInArgsOrReturn(kFailureMessagePrefix); +} + +} // namespace score::mw::com::test diff --git a/score/mw/com/test/methods/signature_variations/consumer.h b/score/mw/com/test/methods/mixed_criticality/consumer.h similarity index 74% rename from score/mw/com/test/methods/signature_variations/consumer.h rename to score/mw/com/test/methods/mixed_criticality/consumer.h index 3275b77ff..1a454b4c6 100644 --- a/score/mw/com/test/methods/signature_variations/consumer.h +++ b/score/mw/com/test/methods/mixed_criticality/consumer.h @@ -10,14 +10,14 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef SCORE_MW_COM_TEST_METHODS_SIGNATURE_VARIATIONS_CONSUMER_H -#define SCORE_MW_COM_TEST_METHODS_SIGNATURE_VARIATIONS_CONSUMER_H +#ifndef SCORE_MW_COM_TEST_METHODS_MIXED_CRITICALITY_CONSUMER_H +#define SCORE_MW_COM_TEST_METHODS_MIXED_CRITICALITY_CONSUMER_H namespace score::mw::com::test { -int run_consumer(); +void run_consumer(); } // namespace score::mw::com::test -#endif // SCORE_MW_COM_TEST_METHODS_SIGNATURE_VARIATIONS_CONSUMER_H +#endif // SCORE_MW_COM_TEST_METHODS_MIXED_CRITICALITY_CONSUMER_H diff --git a/score/mw/com/test/methods/mixed_criticality/consumer_main.cpp b/score/mw/com/test/methods/mixed_criticality/consumer_main.cpp new file mode 100644 index 000000000..39ec7552b --- /dev/null +++ b/score/mw/com/test/methods/mixed_criticality/consumer_main.cpp @@ -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 + ********************************************************************************/ + +/// \brief QM consumer main entry point for mixed criticality method tests. + +#include "score/mw/com/runtime.h" +#include "score/mw/com/test/common_test_resources/assert_handler.h" +#include "score/mw/com/test/methods/mixed_criticality/common_resources.h" +#include "score/mw/com/test/methods/mixed_criticality/consumer.h" + +int main(int argc, const char** argv) +{ + auto service_instance_manifest_path = score::mw::com::test::ParseServiceInstanceManifest(argc, argv); + + score::mw::com::test::SetupAssertHandler(); + score::mw::com::runtime::InitializeRuntime( + score::mw::com::runtime::RuntimeConfiguration{service_instance_manifest_path}); + + score::mw::com::test::run_consumer(); + return EXIT_SUCCESS; +} diff --git a/score/mw/com/test/methods/mixed_criticality/integration_test/BUILD b/score/mw/com/test/methods/mixed_criticality/integration_test/BUILD new file mode 100644 index 000000000..b123fd1bb --- /dev/null +++ b/score/mw/com/test/methods/mixed_criticality/integration_test/BUILD @@ -0,0 +1,58 @@ +# ******************************************************************************* +# 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/methods/mixed_criticality:consumer-pkg", + "//score/mw/com/test/methods/mixed_criticality:provider-pkg", + ], +) + +integration_test( + name = "qm_consumer_qm_provider_test", + srcs = [ + "qm_consumer_qm_provider_test.py", + "test_fixture.py", + ], + filesystem = ":filesystem", +) + +integration_test( + name = "qm_consumer_asilb_provider_test", + srcs = [ + "qm_consumer_asilb_provider_test.py", + "test_fixture.py", + ], + filesystem = ":filesystem", +) + +integration_test( + name = "asilb_consumer_qm_provider_test", + srcs = [ + "asilb_consumer_qm_provider_test.py", + "test_fixture.py", + ], + filesystem = ":filesystem", +) + +integration_test( + name = "asilb_consumer_asilb_provider_test", + srcs = [ + "asilb_consumer_asilb_provider_test.py", + "test_fixture.py", + ], + filesystem = ":filesystem", +) diff --git a/score/mw/com/test/methods/mixed_criticality/integration_test/asilb_consumer_asilb_provider_test.py b/score/mw/com/test/methods/mixed_criticality/integration_test/asilb_consumer_asilb_provider_test.py new file mode 100644 index 000000000..93cdec26c --- /dev/null +++ b/score/mw/com/test/methods/mixed_criticality/integration_test/asilb_consumer_asilb_provider_test.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 Criticality, call_consumer_and_provider + + +""" +Test all four method types (InArg and Return, InArg only, Return only, and no InArg or Return) with ASIL B criticality for both consumer and provider. +Verifies that method calls are sent from consumer to provider with the correct values. +Verifies that the provider executes the correct handler on the provided values. +Verifies that the consumer receives the correct return values. +""" +def test_mixed_criticality_consumer_provider(target): + call_consumer_and_provider( + target, Criticality.ASILB, Criticality.ASILB, Criticality.ASILB) diff --git a/score/mw/com/test/methods/mixed_criticality/integration_test/asilb_consumer_qm_provider_test.py b/score/mw/com/test/methods/mixed_criticality/integration_test/asilb_consumer_qm_provider_test.py new file mode 100644 index 000000000..2543600b0 --- /dev/null +++ b/score/mw/com/test/methods/mixed_criticality/integration_test/asilb_consumer_qm_provider_test.py @@ -0,0 +1,25 @@ +# ******************************************************************************* +# 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 Criticality, call_consumer_and_provider + + +""" +Test all four method types (InArg and Return, InArg only, Return only, and no InArg or Return) with ASIL B criticality for the consumer and QM criticality for the provider. +Verifies that method calls are sent from consumer to provider with the correct values. +Verifies that the provider executes the correct handler on the provided values. +Verifies that the consumer receives the correct return values. +""" +def test_mixed_criticality_consumer_provider(target): + print("Starting asilb consumer and qm provider...") + call_consumer_and_provider( + target, Criticality.ASILB, Criticality.QM, Criticality.QM, timeout_sec=120) diff --git a/score/mw/com/test/methods/mixed_criticality/integration_test/qm_consumer_asilb_provider_test.py b/score/mw/com/test/methods/mixed_criticality/integration_test/qm_consumer_asilb_provider_test.py new file mode 100644 index 000000000..e07edf8eb --- /dev/null +++ b/score/mw/com/test/methods/mixed_criticality/integration_test/qm_consumer_asilb_provider_test.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 +# ******************************************************************************* +from test_fixture import Criticality, call_consumer_and_provider + +""" +Test all four method types (InArg and Return, InArg only, Return only, and no InArg or Return) with QM criticality for the consumer and ASIL B criticality for the provider. +Verifies that method calls are sent from consumer to provider with the correct values. +Verifies that the provider executes the correct handler on the provided values. +Verifies that the consumer receives the correct return values. +""" +def test_mixed_criticality_consumer_provider(target): + call_consumer_and_provider( + target, Criticality.QM, Criticality.QM, Criticality.ASILB) diff --git a/score/mw/com/test/methods/mixed_criticality/integration_test/qm_consumer_qm_provider_test.py b/score/mw/com/test/methods/mixed_criticality/integration_test/qm_consumer_qm_provider_test.py new file mode 100644 index 000000000..64f8dad62 --- /dev/null +++ b/score/mw/com/test/methods/mixed_criticality/integration_test/qm_consumer_qm_provider_test.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 Criticality, call_consumer_and_provider + + +""" +Test all four method types (InArg and Return, InArg only, Return only, and no InArg or Return) with QM criticality for both consumer and provider. +Verifies that method calls are sent from consumer to provider with the correct values. +Verifies that the provider executes the correct handler on the provided values. +Verifies that the consumer receives the correct return values. +""" +def test_mixed_criticality_consumer_provider(target): + call_consumer_and_provider( + target, Criticality.QM, Criticality.QM, Criticality.QM) diff --git a/score/mw/com/test/methods/mixed_criticality/integration_test/test_fixture.py b/score/mw/com/test/methods/mixed_criticality/integration_test/test_fixture.py new file mode 100644 index 000000000..729a99522 --- /dev/null +++ b/score/mw/com/test/methods/mixed_criticality/integration_test/test_fixture.py @@ -0,0 +1,62 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +from enum import Enum + + +class Criticality(Enum): + ASILB = "asil_b" + QM = "qm" + + +def consumer(target, consumer_criticality: Criticality, consumer_expects_criticality: Criticality, **kwargs): + # consumer app may be asil b but consume asil qm provider thus two separate + # Criticality values need to be specified here + consumer_config_name = ( + f"consumer_{consumer_criticality.value}_to_{consumer_expects_criticality.value}_mw_com_config.json") + + args = ["--service_instance_manifest", f"./etc/{consumer_config_name}"] + return target.wrap_exec("bin/consumer_main", args, cwd="/opt/ConsumerApp", wait_on_exit=True, **kwargs) + + +def provider(target, provider_criticality: Criticality, **kwargs): + # For a provider the global app criticality and the + # offered criticality is always the same + provider_config_name = ( + f"provider_{provider_criticality.value}_mw_com_config.json") + + args = ["--service_instance_manifest", f"./etc/{provider_config_name}"] + return target.wrap_exec("bin/provider_main", args, cwd="/opt/ProviderApp", wait_on_exit=True, **kwargs) + + +def call_consumer_and_provider(target, + consumer_criticality: Criticality, + consumer_expects_criticality: Criticality, + provider_criticality: Criticality, + timeout_sec=60): + """ + Validates: + - Method registration on a skeleton with a given provider_criticality + - Method invocation from a proxy with a given consumer_criticality + - Correct parameter passing and result return + - Both copy and zero-copy call semantics are tested + Note: every even numbered method call is zero-copy, thus more + than two methods need to be registered, in the used mw_com_config.json + """ + print(f"Starting consumer and provider with criticality { + consumer_criticality.value} and {provider_criticality.value}") + + with provider(target, provider_criticality): + with consumer(target, consumer_criticality, consumer_expects_criticality): + pass + diff --git a/score/mw/com/test/methods/mixed_criticality/provider.cpp b/score/mw/com/test/methods/mixed_criticality/provider.cpp new file mode 100644 index 000000000..ac1f85d63 --- /dev/null +++ b/score/mw/com/test/methods/mixed_criticality/provider.cpp @@ -0,0 +1,84 @@ +/******************************************************************************** + * 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/methods/mixed_criticality/provider.h" + +#include "score/mw/com/test/common_test_resources/fail_test.h" +#include "score/mw/com/test/methods/methods_test_resources/all_signatures_datatype/all_signatures_method_provider.h" +#include "score/mw/com/test/methods/methods_test_resources/process_synchronizer.h" +#include "score/mw/com/types.h" + +#include "score/result/result.h" + +#include + +#include +#include + +namespace score::mw::com::test +{ +namespace +{ +const std::string kInterprocessNotificationShmPath{"/mixed_criticality_test_interprocess_notification"}; +const std::string kFailureMessagePrefix{"mixed_criticality"}; + +const InstanceSpecifier kInstanceSpecifier = + InstanceSpecifier::Create(std::string{"test/methods/mixed_criticality/MethodSignature"}).value(); + +const std::int32_t kReturnOnlyMethodReturnValue{15}; +const std::int32_t kInArgOnlyMethodTestValueA{17}; +const std::int32_t kInArgOnlyMethodTestValueB{18}; + +} // namespace + +void run_provider(const score::cpp::stop_token& stop_token) +{ + AllSignaturesMethodProvider provider{}; + auto process_synchronizer_result = ProcessSynchronizer::Create(kInterprocessNotificationShmPath); + if (!(process_synchronizer_result.has_value())) + { + FailTest("Methods mixed_criticality provider failed: Could not create ProcessSynchronizer"); + return; + } + + // Step 1. Create skeleton + std::cout << "\nProvider: Step 1" << std::endl; + provider.CreateSkeleton(kInstanceSpecifier, kFailureMessagePrefix); + + std::cout << "Provider: Ready for method calls from multiple proxies\n"; + + // Step 2. Register method handlers + std::cout << "\nProvider: Step 2" << std::endl; + provider.RegisterMethodHandlerWithInArgsAndReturn(kFailureMessagePrefix); + provider.RegisterMethodHandlerWithInArgsOnly( + kInArgOnlyMethodTestValueA, kInArgOnlyMethodTestValueB, kFailureMessagePrefix); + provider.RegisterMethodHandlerWithReturnOnly(kReturnOnlyMethodReturnValue, kFailureMessagePrefix); + provider.RegisterWithoutInArgsOrReturn(kFailureMessagePrefix); + + // Step 3. Offer service + std::cout << "\nProvider: Step 3" << std::endl; + provider.OfferService(kFailureMessagePrefix); + + // Step 4. Wait for proxy test to finish + std::cout << "Provider: Ready for method calls" << std::endl; + if (!process_synchronizer_result->WaitWithAbort(stop_token)) + { + FailTest( + "Methods mixed_criticality provider failed: WaitForProxyTestToFinish was stopped by " + "stop_token instead of notification"); + return; + } + + std::cout << "Provider: Shutting down.\n"; +} + +} // namespace score::mw::com::test diff --git a/score/mw/com/test/methods/signature_variations/provider.h b/score/mw/com/test/methods/mixed_criticality/provider.h similarity index 72% rename from score/mw/com/test/methods/signature_variations/provider.h rename to score/mw/com/test/methods/mixed_criticality/provider.h index e1bf6608f..ed7329ce9 100644 --- a/score/mw/com/test/methods/signature_variations/provider.h +++ b/score/mw/com/test/methods/mixed_criticality/provider.h @@ -10,16 +10,16 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef SCORE_MW_COM_TEST_METHODS_SIGNATURE_VARIATIONS_PROVIDER_H -#define SCORE_MW_COM_TEST_METHODS_SIGNATURE_VARIATIONS_PROVIDER_H +#ifndef SCORE_MW_COM_TEST_METHODS_MIXED_CRITICALITY_PROVIDER_H +#define SCORE_MW_COM_TEST_METHODS_MIXED_CRITICALITY_PROVIDER_H #include namespace score::mw::com::test { -bool run_provider(const score::cpp::stop_token& stop_token); +void run_provider(const score::cpp::stop_token& stop_token); } // namespace score::mw::com::test -#endif // SCORE_MW_COM_TEST_METHODS_SIGNATURE_VARIATIONS_PROVIDER_H +#endif // SCORE_MW_COM_TEST_METHODS_MIXED_CRITICALITY_PROVIDER_H diff --git a/score/mw/com/test/methods/signature_variations/main_provider.cpp b/score/mw/com/test/methods/mixed_criticality/provider_main.cpp similarity index 74% rename from score/mw/com/test/methods/signature_variations/main_provider.cpp rename to score/mw/com/test/methods/mixed_criticality/provider_main.cpp index c03878019..eee7eab36 100644 --- a/score/mw/com/test/methods/signature_variations/main_provider.cpp +++ b/score/mw/com/test/methods/mixed_criticality/provider_main.cpp @@ -10,15 +10,21 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ + +/// \brief ASIL-B provider main entry point for mixed criticality method tests. + #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/methods/signature_variations/provider.h" +#include "score/mw/com/test/methods/mixed_criticality/common_resources.h" +#include "score/mw/com/test/methods/mixed_criticality/provider.h" #include int main(int argc, const char** argv) { + auto service_instance_manifest_path = score::mw::com::test::ParseServiceInstanceManifest(argc, argv); + score::mw::com::test::SetupAssertHandler(); score::mw::com::runtime::InitializeRuntime(argc, argv); @@ -29,5 +35,6 @@ int main(int argc, const char** argv) std::cerr << "Unable to set signal handler for SIGINT and/or SIGTERM, cautiously continuing\n"; } - return score::mw::com::test::run_provider(stop_source.get_token()); + score::mw::com::test::run_provider(stop_source.get_token()); + return EXIT_SUCCESS; } diff --git a/score/mw/com/test/methods/multiple_proxies/BUILD b/score/mw/com/test/methods/multiple_proxies/BUILD new file mode 100644 index 000000000..29da9c7e4 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/BUILD @@ -0,0 +1,192 @@ +# ******************************************************************************* +# 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("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") +load("//score/mw/com/test:pkg_application.bzl", "pkg_application") + +cc_library( + name = "common_resources", + srcs = ["common_resources.cpp"], + hdrs = ["common_resources.h"], + features = COMPILER_WARNING_FEATURES, + visibility = ["//score/mw/com/test/methods:__subpackages__"], + deps = [ + "//score/mw/com", + "//score/mw/com/test/common_test_resources:fail_test", + ], +) + +# gtodo: fix deps +cc_library( + name = "consumer", + srcs = ["consumer.cpp"], + hdrs = ["consumer.h"], + features = COMPILER_WARNING_FEATURES, + implementation_deps = [ + ":common_resources", + ":duplicate_signatures_datatype", + "//score/mw/com", + "//score/mw/com/test/common_test_resources:fail_test", + "//score/mw/com/test/methods/methods_test_resources:process_synchronizer", + "//score/mw/com/test/methods/methods_test_resources:proxy_container", + ], +) + +cc_library( + name = "provider", + srcs = ["provider.cpp"], + hdrs = ["provider.h"], + features = COMPILER_WARNING_FEATURES, + deps = [ + ":common_resources", + ":duplicate_signatures_datatype", + "//score/mw/com", + "//score/mw/com/test/common_test_resources:fail_test", + "//score/mw/com/test/methods/methods_test_resources:process_synchronizer", + "//score/mw/com/test/methods/methods_test_resources:skeleton_container", + ], +) + +cc_binary( + name = "main_provider", + srcs = ["main_provider.cpp"], + features = COMPILER_WARNING_FEATURES + [ + "aborts_upon_exception", + ], + deps = [ + ":common_resources", + ":provider", + "//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", + ], +) + +cc_binary( + name = "main_consumer", + srcs = ["main_consumer.cpp"], + features = COMPILER_WARNING_FEATURES + [ + "aborts_upon_exception", + ], + deps = [ + ":common_resources", + ":consumer", + "//score/mw/com", + "//score/mw/com/test/common_test_resources:command_line_parser", + "//score/mw/com/test/common_test_resources:fail_test", + ], +) + +cc_binary( + name = "main_combined_consumer_provider", + srcs = ["main_combined_consumer_provider.cpp"], + features = COMPILER_WARNING_FEATURES + [ + "aborts_upon_exception", + ], + deps = [ + ":common_resources", + ":consumer", + ":provider", + "//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", + ], +) + +cc_library( + name = "duplicate_signatures_datatype", + srcs = ["duplicate_signatures_datatype.cpp"], + hdrs = ["duplicate_signatures_datatype.h"], + features = COMPILER_WARNING_FEATURES, + visibility = ["//score/mw/com/test/methods:__subpackages__"], + deps = [ + "//score/mw/com", + ], +) + +pkg_application( + name = "provider-pkg", + app_name = "MainProviderApp", + bin = [":main_provider"], + etc = [ + "config/provider_mw_com_config.json", + ], + visibility = [ + "//score/mw/com/test/methods/multiple_proxies/integration_test:__pkg__", + ], +) + +pkg_application( + name = "consumer-pkg", + app_name = "MainConsumerApp", + bin = [":main_consumer"], + etc = [ + "config/consumer0_mw_com_config.json", + "config/consumer1_mw_com_config.json", + "config/consumer2_mw_com_config.json", + "config/consumer3_mw_com_config.json", + ], + visibility = [ + "//score/mw/com/test/methods/multiple_proxies/integration_test:__pkg__", + ], +) + +# pkg_application( +# name = "consumer1-pkg", +# app_name = "Consumer1App", +# bin = [":main_consumer"], +# etc = [ +# ":consumer1_mw_com_config", +# ], +# visibility = [ +# "//score/mw/com/test/methods/multiple_proxies/integration_test:__pkg__", +# ], +# ) +# +# pkg_application( +# name = "consumer2-pkg", +# app_name = "Consumer2App", +# bin = [":main_consumer"], +# etc = [ +# ":consumer2_mw_com_config", +# ], +# visibility = [ +# "//score/mw/com/test/methods/multiple_proxies/integration_test:__pkg__", +# ], +# ) +# +# pkg_application( +# name = "consumer3-pkg", +# app_name = "Consumer3App", +# bin = [":main_consumer"], +# etc = [ +# ":consumer3_mw_com_config", +# ], +# visibility = [ +# "//score/mw/com/test/methods/multiple_proxies/integration_test:__pkg__", +# ], +# ) + +pkg_application( + name = "combined-provider-consumer-pkg", + app_name = "MainCombinedConsumerProviderApp", + bin = [":main_combined_consumer_provider"], + etc = [ + "config/combined_mw_com_config.json", + ], + visibility = [ + "//score/mw/com/test/methods/multiple_proxies/integration_test:__pkg__", + ], +) diff --git a/score/mw/com/test/methods/multiple_proxies/common_resources.cpp b/score/mw/com/test/methods/multiple_proxies/common_resources.cpp new file mode 100644 index 000000000..0e7373610 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/common_resources.cpp @@ -0,0 +1,50 @@ +/******************************************************************************** + * 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/methods/multiple_proxies/common_resources.h" + +#include + +namespace score::mw::com::test +{ + +std::string CreateInterprocessNotificationShmPath(size_t path_id) +{ + std::string path{"test_methods_interprocess_shm_notification_path"}; + path.append(std::to_string(path_id)); + path.append("_notification"); + return path; +} + +/// \brief Calculate the total number of times each method is expected to be called, based on the number of calls per +/// method, the number of proxies and the enabled methods for each consumer. +/// \return A vector where the value at each index corresponds to the expected number of calls for the corresponding +/// method in DuplicateSignatureInterface. +std::vector CalculateExpectedMethodCallCounts(std::size_t num_consumers, + std::size_t num_proxies_per_process, + std::size_t num_method_calls_per_proxy, + const EnabledMethodIdsPerConsumer& enabled_method_ids) +{ + std::vector expected_method_call_count(kNumRegisteredMethods, 0U); + + for (std::size_t consumer_id{0}; consumer_id < num_consumers; ++consumer_id) + { + const auto enabled_methods_for_consumer = enabled_method_ids.at(consumer_id); + for (const auto method_id : enabled_methods_for_consumer) + { + expected_method_call_count.at(method_id) += num_proxies_per_process * num_method_calls_per_proxy; + } + } + return expected_method_call_count; +} + +} // namespace score::mw::com::test diff --git a/score/mw/com/test/methods/multiple_proxies/common_resources.h b/score/mw/com/test/methods/multiple_proxies/common_resources.h new file mode 100644 index 000000000..eefd5ee4b --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/common_resources.h @@ -0,0 +1,46 @@ +/******************************************************************************** + * 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_METHODS_MULTIPLE_PROXIES_COMMON_RESOURCES_H +#define SCORE_MW_COM_TEST_METHODS_MULTIPLE_PROXIES_COMMON_RESOURCES_H + +#include +#include +#include +#include + +namespace score::mw::com::test +{ + +// Number of methods which exist within DuplicateSignatureInterface. +inline constexpr std::size_t kNumRegisteredMethods{5U}; + +inline constexpr std::int32_t kMethodResultMultiplierBase{13}; + +// The method index for each consumer which is enabled. E.g. the first vector contains the method indices enabled for +// consumer 0 etc. The size of this array must match num_consumers defined in the python test runner. +using EnabledMethodIdsPerConsumer = std::array, 4U>; +const EnabledMethodIdsPerConsumer kEnabledMethodsPerProxy{std::vector{0U, 1U}, + std::vector{1U, 2U}, + std::vector{3U, 4U}, + std::vector{0U, 2U, 3U}}; + +std::string CreateInterprocessNotificationShmPath(size_t path_id); + +std::vector CalculateExpectedMethodCallCounts(std::size_t num_consumers, + std::size_t num_proxies_per_process, + std::size_t num_method_calls_per_proxy, + const EnabledMethodIdsPerConsumer& enabled_method_ids); + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_METHODS_MULTIPLE_PROXIES_COMMON_RESOURCES_H diff --git a/score/mw/com/test/methods/multiple_proxies/config/combined_mw_com_config.json b/score/mw/com/test/methods/multiple_proxies/config/combined_mw_com_config.json new file mode 100644 index 000000000..a24b77ad4 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/config/combined_mw_com_config.json @@ -0,0 +1,98 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethodProvider", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 3000, + "methods": [ + { + "methodName": "method0", + "methodId": 1 + }, + { + "methodName": "method1", + "methodId": 2 + }, + { + "methodName": "method2", + "methodId": 3 + }, + { + "methodName": "method3", + "methodId": 4 + }, + { + "methodName": "method4", + "methodId": 5 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "multiple_proxies/MultiMethodProvider", + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethodProvider", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "method0", + "queueSize": 5 + }, + { + "methodName": "method1", + "queueSize": 5 + }, + { + "methodName": "method2", + "queueSize": 5 + }, + { + "methodName": "method3", + "queueSize": 5 + }, + { + "methodName": "method4", + "queueSize": 5 + } + ], + "allowedConsumer": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + }, + "allowedProvider": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + } + } + ] + } + ], + "global": { + "asil-level": "B", + "applicationID": 12 + } +} diff --git a/score/mw/com/test/methods/multiple_proxies/config/consumer0_mw_com_config.json b/score/mw/com/test/methods/multiple_proxies/config/consumer0_mw_com_config.json new file mode 100644 index 000000000..213c52052 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/config/consumer0_mw_com_config.json @@ -0,0 +1,98 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethodProvider", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 3000, + "methods": [ + { + "methodName": "method0", + "methodId": 1 + }, + { + "methodName": "method1", + "methodId": 2 + }, + { + "methodName": "method2", + "methodId": 3 + }, + { + "methodName": "method3", + "methodId": 4 + }, + { + "methodName": "method4", + "methodId": 5 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "multiple_proxies/MultiMethodProvider", + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethodProvider", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "method0", + "queueSize": 5 + }, + { + "methodName": "method1", + "queueSize": 5 + }, + { + "methodName": "method2", + "queueSize": 5 + }, + { + "methodName": "method3", + "queueSize": 5 + }, + { + "methodName": "method4", + "queueSize": 5 + } + ], + "allowedConsumer": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + }, + "allowedProvider": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + } + } + ] + } + ], + "global": { + "asil-level": "B", + "applicationID": 100 + } +} diff --git a/score/mw/com/test/methods/multiple_proxies/config/consumer1_mw_com_config.json b/score/mw/com/test/methods/multiple_proxies/config/consumer1_mw_com_config.json new file mode 100644 index 000000000..30c083a55 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/config/consumer1_mw_com_config.json @@ -0,0 +1,98 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethodProvider", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 3000, + "methods": [ + { + "methodName": "method0", + "methodId": 1 + }, + { + "methodName": "method1", + "methodId": 2 + }, + { + "methodName": "method2", + "methodId": 3 + }, + { + "methodName": "method3", + "methodId": 4 + }, + { + "methodName": "method4", + "methodId": 5 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "multiple_proxies/MultiMethodProvider", + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethodProvider", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "method0", + "queueSize": 5 + }, + { + "methodName": "method1", + "queueSize": 5 + }, + { + "methodName": "method2", + "queueSize": 5 + }, + { + "methodName": "method3", + "queueSize": 5 + }, + { + "methodName": "method4", + "queueSize": 5 + } + ], + "allowedConsumer": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + }, + "allowedProvider": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + } + } + ] + } + ], + "global": { + "asil-level": "B", + "applicationID": 101 + } +} diff --git a/score/mw/com/test/methods/multiple_proxies/config/consumer2_mw_com_config.json b/score/mw/com/test/methods/multiple_proxies/config/consumer2_mw_com_config.json new file mode 100644 index 000000000..074a126e7 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/config/consumer2_mw_com_config.json @@ -0,0 +1,98 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethodProvider", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 3000, + "methods": [ + { + "methodName": "method0", + "methodId": 1 + }, + { + "methodName": "method1", + "methodId": 2 + }, + { + "methodName": "method2", + "methodId": 3 + }, + { + "methodName": "method3", + "methodId": 4 + }, + { + "methodName": "method4", + "methodId": 5 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "multiple_proxies/MultiMethodProvider", + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethodProvider", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "method0", + "queueSize": 5 + }, + { + "methodName": "method1", + "queueSize": 5 + }, + { + "methodName": "method2", + "queueSize": 5 + }, + { + "methodName": "method3", + "queueSize": 5 + }, + { + "methodName": "method4", + "queueSize": 5 + } + ], + "allowedConsumer": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + }, + "allowedProvider": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + } + } + ] + } + ], + "global": { + "asil-level": "B", + "applicationID": 102 + } +} diff --git a/score/mw/com/test/methods/multiple_proxies/config/consumer3_mw_com_config.json b/score/mw/com/test/methods/multiple_proxies/config/consumer3_mw_com_config.json new file mode 100644 index 000000000..368cc45ef --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/config/consumer3_mw_com_config.json @@ -0,0 +1,98 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethodProvider", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 3000, + "methods": [ + { + "methodName": "method0", + "methodId": 1 + }, + { + "methodName": "method1", + "methodId": 2 + }, + { + "methodName": "method2", + "methodId": 3 + }, + { + "methodName": "method3", + "methodId": 4 + }, + { + "methodName": "method4", + "methodId": 5 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "multiple_proxies/MultiMethodProvider", + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethodProvider", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "method0", + "queueSize": 5 + }, + { + "methodName": "method1", + "queueSize": 5 + }, + { + "methodName": "method2", + "queueSize": 5 + }, + { + "methodName": "method3", + "queueSize": 5 + }, + { + "methodName": "method4", + "queueSize": 5 + } + ], + "allowedConsumer": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + }, + "allowedProvider": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + } + } + ] + } + ], + "global": { + "asil-level": "B", + "applicationID": 103 + } +} diff --git a/score/mw/com/test/methods/multiple_proxies/config/provider_mw_com_config.json b/score/mw/com/test/methods/multiple_proxies/config/provider_mw_com_config.json new file mode 100644 index 000000000..a24b77ad4 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/config/provider_mw_com_config.json @@ -0,0 +1,98 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethodProvider", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 3000, + "methods": [ + { + "methodName": "method0", + "methodId": 1 + }, + { + "methodName": "method1", + "methodId": 2 + }, + { + "methodName": "method2", + "methodId": 3 + }, + { + "methodName": "method3", + "methodId": 4 + }, + { + "methodName": "method4", + "methodId": 5 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "multiple_proxies/MultiMethodProvider", + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethodProvider", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "method0", + "queueSize": 5 + }, + { + "methodName": "method1", + "queueSize": 5 + }, + { + "methodName": "method2", + "queueSize": 5 + }, + { + "methodName": "method3", + "queueSize": 5 + }, + { + "methodName": "method4", + "queueSize": 5 + } + ], + "allowedConsumer": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + }, + "allowedProvider": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + } + } + ] + } + ], + "global": { + "asil-level": "B", + "applicationID": 12 + } +} diff --git a/score/mw/com/test/methods/multiple_proxies/consumer.cpp b/score/mw/com/test/methods/multiple_proxies/consumer.cpp new file mode 100644 index 000000000..94ec7ca51 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/consumer.cpp @@ -0,0 +1,182 @@ +/******************************************************************************** + * 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/methods/multiple_proxies/consumer.h" + +#include "score/mw/com/test/common_test_resources/fail_test.h" +#include "score/mw/com/test/methods/methods_test_resources/process_synchronizer.h" +#include "score/mw/com/test/methods/methods_test_resources/proxy_container.h" +#include "score/mw/com/test/methods/multiple_proxies/common_resources.h" +#include "score/mw/com/test/methods/multiple_proxies/duplicate_signatures_datatype.h" +#include "score/mw/com/types.h" + +#include + +#include +#include +#include +#include + +namespace score::mw::com::test +{ +namespace +{ + +const std::string kInterprocessNotificationShmPath{"/multiple_proxies_test_interprocess_notification"}; + +const InstanceSpecifier kInstanceSpecifier = + InstanceSpecifier::Create(std::string{"multiple_proxies/MultiMethodProvider"}).value(); + +enum class CopyMode +{ + ZERO_COPY, + WITH_COPY +}; + +void ValidateMethodCallResult(score::Result>& result, + std::size_t consumer_id, + std::size_t method_id, + std::int32_t input_value) +{ + if (!result.has_value()) + { + FailTest("Consumer", consumer_id, ": method", method_id, " call failed: ", result.error()); + } + + const auto actual = *(result.value()); + const auto expected = input_value * (kMethodResultMultiplierBase + static_cast(method_id)); + + // clang-format off + if (actual != expected) + { + FailTest("Consumer" , consumer_id , ": method" , method_id , " returned " , actual , " but expected " , expected); + } + // clang-format on + + std::cout << "Consumer" << consumer_id << ": method" << method_id << " returned correct value: " << actual << '\n'; +} + +void CallMethod(DuplicateSignatureProxy& proxy, + CopyMode copy_mode, + std::size_t method_id, + std::size_t consumer_id, + std::int32_t input) +{ + std::cout << "Consumer" << consumer_id << ": Calling a method" << method_id << " with the value " << input << '\n'; + + auto& method = [&proxy, consumer_id, method_id]() -> auto& { + if (method_id == 0U) + { + return proxy.method0; + } + if (method_id == 1U) + { + return proxy.method1; + } + if (method_id == 2U) + { + return proxy.method2; + } + if (method_id == 3U) + { + return proxy.method3; + } + if (method_id == 4U) + { + return proxy.method4; + } + // clang-format off + FailTest("Consumer", consumer_id, ": Invalid method ID ", method_id, " cannot be larger than ", kNumRegisteredMethods); + // clang-format on + + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(false); + }(); + + auto result = + [&method, copy_mode, input]() -> score::Result> { + if (copy_mode == CopyMode::ZERO_COPY) + { + auto allocated_args_result = method.Allocate(); + if (!allocated_args_result.has_value()) + { + std::cerr << "Consumer: Failure during zero-copy call. " << allocated_args_result.error() << '\n'; + return score::Unexpected(allocated_args_result.error()); + } + + auto& [arg_ptr] = allocated_args_result.value(); + *arg_ptr = input; + + return method(std::move(arg_ptr)); + } + + return method(input); + }(); + + ValidateMethodCallResult(result, consumer_id, method_id, input); +} + +} // namespace + +void run_consumer(const std::size_t consumer_id, + const std::size_t num_proxies_per_process, + const std::size_t num_method_calls_per_proxy, + const std::vector& enabled_method_ids) +{ + const auto notification_path = CreateInterprocessNotificationShmPath(consumer_id); + + auto process_synchronizer_result = ProcessSynchronizer::Create(notification_path); + if (!(process_synchronizer_result.has_value())) + { + FailTest("Methods signature_variations consumer failed: Could not create ProcessSynchronizer"); + } + + // Set an exit function to notify the provider in case of failure in calls to FailTest, so that it does not wait + // indefinitely for the consumer to finish. Will also be called when the guard goes out of scope at the end of + // this function. + ExitFunctionGuard process_synchronizer_guard{[&process_synchronizer_result]() { + process_synchronizer_result->Notify(); + }}; + + auto proxy_runner = [&enabled_method_ids, consumer_id, num_method_calls_per_proxy](std::size_t proxy_id) -> void { + ProxyContainer proxy_container{}; + + // Step 1. Find service and create proxy + std::cout << "\nConsumer: Step 1 - Proxy " << proxy_id << std::endl; + proxy_container.CreateProxy(kInstanceSpecifier, "multiple_proxies"); + auto& proxy = proxy_container.GetProxy(); + + // Step 2. Call method with InArgs and Return with copy + std::cout << "\nConsumer: Step 2 - Proxy " << proxy_id << std::endl; + for (std::size_t i = 0U; i < num_method_calls_per_proxy; ++i) + { + for (const auto& method_id : enabled_method_ids) + { + const auto some_random_input_value = static_cast(proxy_id + method_id); + CopyMode copy_mode = (method_id % 2 == 0) ? CopyMode::ZERO_COPY : CopyMode::WITH_COPY; + CallMethod(proxy, copy_mode, method_id, consumer_id, some_random_input_value); + } + } + }; + + { + std::vector threads{}; + threads.reserve(num_proxies_per_process); + for (std::size_t proxy_id = 0U; proxy_id < num_proxies_per_process; ++proxy_id) + { + threads.emplace_back(proxy_runner, proxy_id); + } + } + + std::cout << "Consumer" << consumer_id << ": All threads have completed. Exiting." << std::endl; +} + +} // namespace score::mw::com::test diff --git a/score/mw/com/test/methods/multiple_proxies/consumer.h b/score/mw/com/test/methods/multiple_proxies/consumer.h new file mode 100644 index 000000000..9f8b8704d --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/consumer.h @@ -0,0 +1,29 @@ +/******************************************************************************** + * 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_METHODS_MULTIPLE_PROXIES_CONSUMER_H +#define SCORE_MW_COM_TEST_METHODS_MULTIPLE_PROXIES_CONSUMER_H + +#include +#include + +namespace score::mw::com::test +{ + +void run_consumer(const std::size_t consumer_id, + const std::size_t num_proxies_per_process, + const std::size_t num_method_calls_per_proxy, + const std::vector& enabled_method_ids); + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_METHODS_MULTIPLE_PROXIES_CONSUMER_H diff --git a/score/mw/com/test/methods/signature_variations/main_consumer.cpp b/score/mw/com/test/methods/multiple_proxies/duplicate_signatures_datatype.cpp similarity index 60% rename from score/mw/com/test/methods/signature_variations/main_consumer.cpp rename to score/mw/com/test/methods/multiple_proxies/duplicate_signatures_datatype.cpp index 69fe81cc3..c684c7abb 100644 --- a/score/mw/com/test/methods/signature_variations/main_consumer.cpp +++ b/score/mw/com/test/methods/multiple_proxies/duplicate_signatures_datatype.cpp @@ -10,13 +10,4 @@ * * 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/methods/signature_variations/consumer.h" - -int main(int argc, const char** argv) -{ - score::mw::com::test::SetupAssertHandler(); - score::mw::com::runtime::InitializeRuntime(argc, argv); - return score::mw::com::test::run_consumer(); -} +#include "score/mw/com/test/methods/multiple_proxies/duplicate_signatures_datatype.h" diff --git a/score/mw/com/test/methods/multiple_proxies/duplicate_signatures_datatype.h b/score/mw/com/test/methods/multiple_proxies/duplicate_signatures_datatype.h new file mode 100644 index 000000000..46f8815bf --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/duplicate_signatures_datatype.h @@ -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 + ********************************************************************************/ +#ifndef SCORE_MW_COM_TEST_METHODS_MULTIPLE_PROXIES_DUPLICATE_SIGNATURES_DATATYPE_H +#define SCORE_MW_COM_TEST_METHODS_MULTIPLE_PROXIES_DUPLICATE_SIGNATURES_DATATYPE_H + +#include "score/mw/com/types.h" + +#include + +namespace score::mw::com::test +{ + +/// \brief Test interface with multiple methods for selective enabling by different proxies +template +class DuplicateSignatureInterface : public T::Base +{ + public: + using T::Base::Base; + using ReturnType = std::int32_t; + using InpArgType = std::int32_t; + using MethodType = ReturnType(InpArgType); + + typename T::template Method method0{*this, "method0"}; + typename T::template Method method1{*this, "method1"}; + typename T::template Method method2{*this, "method2"}; + typename T::template Method method3{*this, "method3"}; + typename T::template Method method4{*this, "method4"}; +}; + +/// \brief Proxy side of the test service +using DuplicateSignatureProxy = score::mw::com::AsProxy; + +/// \brief Skeleton side of the test service +using DuplicateSignatureSkeleton = score::mw::com::AsSkeleton; + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_METHODS_MULTIPLE_PROXIES_DUPLICATE_SIGNATURES_DATATYPE_H diff --git a/score/mw/com/test/methods/multiple_proxies/integration_test/BUILD b/score/mw/com/test/methods/multiple_proxies/integration_test/BUILD new file mode 100644 index 000000000..5d25c64f4 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/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_different_process", + srcs = [ + "//score/mw/com/test/methods/multiple_proxies:consumer-pkg", + "//score/mw/com/test/methods/multiple_proxies:provider-pkg", + ], +) + +pkg_filegroup( + name = "filesystem_same_process", + srcs = [ + "//score/mw/com/test/methods/multiple_proxies:combined-provider-consumer-pkg", + ], +) + +integration_test( + name = "different_process_provider_and_consumers", + timeout = "moderate", + srcs = [ + "test_different_process_provider_and_consumers.py", + ], + filesystem = ":filesystem_different_process", +) + +integration_test( + name = "same_process_provider_and_consumers", + timeout = "moderate", + srcs = [ + "test_same_process_provider_and_consumers.py", + ], + filesystem = ":filesystem_same_process", +) diff --git a/score/mw/com/test/methods/multiple_proxies/integration_test/test_different_process_provider_and_consumers.py b/score/mw/com/test/methods/multiple_proxies/integration_test/test_different_process_provider_and_consumers.py new file mode 100644 index 000000000..bc91a058b --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/integration_test/test_different_process_provider_and_consumers.py @@ -0,0 +1,54 @@ +# ******************************************************************************* +# 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 contextlib import ExitStack + + +# Number of consumer processes. Each consumer will spawn multiple identical proxies which +# enable a different combination of methods. There must be at least as many configuration files as NUM_CONSUMERS +# which are named consumer0_mw_com_config.json, consumer1_mw_com_config.json, etc. kEnabledMethodsPerProxy +# must be at least as large as NUM_CONSUMERS. +NUM_CONSUMERS = 4 + + +# Number of proxies that are created per consumer process. Each proxy will be spawned in a separate +# thread. This can be freely configured. +NUM_PROXIES_PER_CONSUMER = 5 + + +# Number of calls to each enabled method per proxy. This can be freely configured. +NUM_METHOD_CALLS_PER_PROXY = 10 + + +def provider(target, num_consumers, num_proxies_per_consumer, num_method_calls_per_proxy, config_name, **kwargs): + args = ["--num-consumers", str(num_consumers), + "--num-proxies-per-consumer", str(num_proxies_per_consumer), + "--num-method-calls-per-proxy", str(num_method_calls_per_proxy), + "--service-instance-manifest", f"./etc/{config_name}"] + return target.wrap_exec("bin/main_provider", args, cwd="/opt/MainProviderApp", wait_on_exit=True, wait_timeout=60, **kwargs) + + +def consumer(target, consumer_id, num_proxies_per_consumer, num_method_calls_per_proxy, config_name, **kwargs): + args = ["--consumer-id", str(consumer_id), + "--num-proxies-per-consumer", str(num_proxies_per_consumer), + "--num-method-calls-per-proxy", str(num_method_calls_per_proxy), + "--service-instance-manifest", f"./etc/{config_name}"] + return target.wrap_exec("bin/main_consumer", args, cwd="/opt/MainConsumerApp", wait_on_exit=True, wait_timeout=60, **kwargs) + + +def test_multiple_proxies_different_processes(target): + with provider(target, NUM_CONSUMERS, NUM_PROXIES_PER_CONSUMER, NUM_METHOD_CALLS_PER_PROXY, "provider_mw_com_config.json"): + # Launch NUM_CONSUMERS consumers using the python config manager (i.e. `with` keyword). We use ExitStack to ensure that the __exit__ function of each launched consumer (returned from consumer()) is called. + with ExitStack() as stack: + for i in range(NUM_CONSUMERS): + stack.enter_context(consumer(target, i, NUM_PROXIES_PER_CONSUMER, NUM_METHOD_CALLS_PER_PROXY, f"consumer{i}_mw_com_config.json")) + diff --git a/score/mw/com/test/methods/multiple_proxies/integration_test/test_same_process_provider_and_consumers.py b/score/mw/com/test/methods/multiple_proxies/integration_test/test_same_process_provider_and_consumers.py new file mode 100644 index 000000000..7468ca12b --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/integration_test/test_same_process_provider_and_consumers.py @@ -0,0 +1,41 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + + +# Number of consumers. Each consumer will spawn multiple identical proxies which enable +# a different combination of methods. These consumers will be in the same process. There +# must be at least as many configuration files as NUM_CONSUMERS which are named +# consumer0_mw_com_config.json, consumer1_mw_com_config.json, etc. kEnabledMethodsPerProxy +# must be at least as large as NUM_CONSUMERS. +NUM_CONSUMERS = 4 + + +# Number of proxies that are created in the consumer process. Each proxy will be spawned in a separate +# thread. This can be freely configured. +NUM_PROXIES_PER_CONSUMER = 5 + + +# Number of calls to each enabled method per proxy. This can be freely configured. +NUM_CALLS_PER_METHOD = 10 + + +def provider_and_consumer(target, num_consumers, num_proxies_per_consumer, NUM_CALLS_PER_METHOD, config_name, **kwargs): + args = ["--num-consumers", str(num_consumers), + "--num-proxies-per-consumer", str(num_proxies_per_consumer), + "--num-method-calls-per-proxy", str(NUM_CALLS_PER_METHOD), + "--service-instance-manifest", f"./etc/{config_name}"] + return target.wrap_exec("bin/main_combined_consumer_provider", args, cwd="/opt/MainCombinedConsumerProviderApp", wait_on_exit=True, **kwargs) + +def test_multiple_proxies_same_process(target): + with provider_and_consumer(target, NUM_CONSUMERS, NUM_PROXIES_PER_CONSUMER, NUM_CALLS_PER_METHOD, "combined_mw_com_config.json") as provider_and_consumer_process: + pass diff --git a/score/mw/com/test/methods/multiple_proxies/main_combined_consumer_provider.cpp b/score/mw/com/test/methods/multiple_proxies/main_combined_consumer_provider.cpp new file mode 100644 index 000000000..afe5f4aaa --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/main_combined_consumer_provider.cpp @@ -0,0 +1,126 @@ +/******************************************************************************** + * 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/methods/multiple_proxies/common_resources.h" +#include "score/mw/com/test/methods/multiple_proxies/consumer.h" +#include "score/mw/com/test/methods/multiple_proxies/provider.h" + +#include +#include +#include + +namespace score::mw::com::test +{ +namespace +{ + +const std::string kNumConsumers{"num-consumers"}; +const std::string kNumProxiesPerConsumerKey{"num-proxies-per-consumer"}; +const std::string kNumMethodCallsPerProxyKey{"num-method-calls-per-proxy"}; +const std::string kServiceInstanceManifest{"service-instance-manifest"}; + +struct CombinedTestConfiguration +{ + std::size_t num_consumers; + std::size_t num_proxies_per_process; + std::size_t num_method_calls_per_proxy; + std::string service_instance_manifest; +}; + +CombinedTestConfiguration ReadCommandLineArguments(int argc, const char** argv) +{ + auto args = ParseCommandLineArguments(argc, + argv, + {{kNumConsumers, ""}, + {kNumProxiesPerConsumerKey, ""}, + {kNumMethodCallsPerProxyKey, ""}, + {kServiceInstanceManifest, ""}}); + + const auto num_consumers = GetValue(args, kNumConsumers); + if (num_consumers <= 0) + { + FailTest("Consumer: ", kNumConsumers, " value ", num_consumers, " must be greater than 0."); + } + + const auto num_proxies_per_process = GetValue(args, kNumProxiesPerConsumerKey); + if (num_proxies_per_process <= 0) + { + FailTest( + "Consumer: ", kNumProxiesPerConsumerKey, " value ", num_proxies_per_process, " must be greater than 0."); + } + + const auto num_method_calls_per_proxy = GetValue(args, kNumMethodCallsPerProxyKey); + if (num_method_calls_per_proxy <= 0) + { + FailTest("Consumer: ", + kNumMethodCallsPerProxyKey, + " value ", + num_method_calls_per_proxy, + " must be greater than 0."); + } + + auto service_instance_manifest = GetValue(args, kServiceInstanceManifest); + + return {num_consumers, num_proxies_per_process, num_method_calls_per_proxy, std::move(service_instance_manifest)}; +} + +} // namespace +} // namespace score::mw::com::test + +int main(int argc, const char** argv) +{ + auto test_configuration{score::mw::com::test::ReadCommandLineArguments(argc, argv)}; + + auto expected_method_call_count = + score::mw::com::test::CalculateExpectedMethodCallCounts(test_configuration.num_consumers, + test_configuration.num_proxies_per_process, + test_configuration.num_method_calls_per_proxy, + score::mw::com::test::kEnabledMethodsPerProxy); + score::mw::com::runtime::InitializeRuntime( + score::mw::com::runtime::RuntimeConfiguration{test_configuration.service_instance_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"; + } + + // ------ Start the Consumer Threads + std::vector> consumer_futures{}; + + for (std::size_t consumer_id{0}; consumer_id < test_configuration.num_consumers; ++consumer_id) + { + const auto& enabled_method_ids = score::mw::com::test::kEnabledMethodsPerProxy.at(consumer_id); + auto consumer_future = std::async(std::launch::async, + score::mw::com::test::run_consumer, + consumer_id, + test_configuration.num_proxies_per_process, + test_configuration.num_method_calls_per_proxy, + enabled_method_ids); + consumer_futures.push_back(std::move(consumer_future)); + } + + // ------ Start the provider + score::mw::com::test::run_provider( + stop_source.get_token(), expected_method_call_count, test_configuration.num_consumers); + + for (auto& consumer_future : consumer_futures) + { + consumer_future.get(); + } + return EXIT_SUCCESS; +} diff --git a/score/mw/com/test/methods/multiple_proxies/main_consumer.cpp b/score/mw/com/test/methods/multiple_proxies/main_consumer.cpp new file mode 100644 index 000000000..473fd223d --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/main_consumer.cpp @@ -0,0 +1,82 @@ +/******************************************************************************** + * 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/methods/multiple_proxies/common_resources.h" +#include "score/mw/com/test/methods/multiple_proxies/consumer.h" + +namespace score::mw::com::test +{ + +const std::string kConsumerIdKey{"consumer-id"}; +const std::string kNumProxiesPerConsumerKey{"num-proxies-per-consumer"}; +const std::string kNumMethodCallsPerProxyKey{"num-method-calls-per-proxy"}; +const std::string kServiceInstanceManifest{"service-instance-manifest"}; + +struct ConsumerTestConfiguration +{ + std::size_t consumer_id; + std::size_t num_proxies_per_process; + std::size_t num_method_calls_per_proxy; + std::string service_instance_manifest; +}; + +ConsumerTestConfiguration ReadCommandLineArguments(int argc, const char** argv) +{ + auto args = ParseCommandLineArguments(argc, + argv, + {{kConsumerIdKey, ""}, + {kNumProxiesPerConsumerKey, ""}, + {kNumMethodCallsPerProxyKey, ""}, + {kServiceInstanceManifest, ""}}); + + const auto consumer_id = GetValue(args, kConsumerIdKey); + + const auto num_proxies_per_process = GetValue(args, kNumProxiesPerConsumerKey); + if (num_proxies_per_process <= 0) + { + FailTest( + "Consumer: ", kNumProxiesPerConsumerKey, " value ", num_proxies_per_process, " must be greater than 0."); + } + + const auto num_method_calls_per_proxy = GetValue(args, kNumMethodCallsPerProxyKey); + if (num_method_calls_per_proxy <= 0) + { + FailTest("Consumer: ", + kNumMethodCallsPerProxyKey, + " value ", + num_method_calls_per_proxy, + " must be greater than 0."); + } + + auto service_instance_manifest = GetValue(args, kServiceInstanceManifest); + + return {consumer_id, num_proxies_per_process, num_method_calls_per_proxy, std::move(service_instance_manifest)}; +} + +} // namespace score::mw::com::test + +int main(int argc, const char** argv) +{ + const auto test_configuration = score::mw::com::test::ReadCommandLineArguments(argc, argv); + + const auto& enabled_method_ids = score::mw::com::test::kEnabledMethodsPerProxy.at(test_configuration.consumer_id); + score::mw::com::runtime::InitializeRuntime( + score::mw::com::runtime::RuntimeConfiguration{test_configuration.service_instance_manifest}); + score::mw::com::test::run_consumer(test_configuration.consumer_id, + test_configuration.num_proxies_per_process, + test_configuration.num_method_calls_per_proxy, + enabled_method_ids); + return EXIT_SUCCESS; +} diff --git a/score/mw/com/test/methods/multiple_proxies/main_provider.cpp b/score/mw/com/test/methods/multiple_proxies/main_provider.cpp new file mode 100644 index 000000000..ad4563511 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/main_provider.cpp @@ -0,0 +1,107 @@ +/******************************************************************************** + * 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/methods/multiple_proxies/common_resources.h" +#include "score/mw/com/test/methods/multiple_proxies/provider.h" + +#include + +#include +#include + +namespace score::mw::com::test +{ + +const std::string kNumConsumers{"num-consumers"}; +const std::string kNumProxiesPerConsumerKey{"num-proxies-per-consumer"}; +const std::string kNumMethodCallsPerProxyKey{"num-method-calls-per-proxy"}; +const std::string kServiceInstanceManifest{"service-instance-manifest"}; + +struct ProviderTestConfiguration +{ + std::size_t num_consumers; + std::size_t num_proxies_per_process; + std::size_t num_method_calls_per_proxy; + std::string service_instance_manifest; +}; + +ProviderTestConfiguration ReadCommandLineArguments(int argc, const char** argv) +{ + auto args = ParseCommandLineArguments(argc, + argv, + {{kNumConsumers, ""}, + {kNumProxiesPerConsumerKey, ""}, + {kNumMethodCallsPerProxyKey, ""}, + {kServiceInstanceManifest, ""}}); + + const auto num_consumers = GetValue(args, kNumConsumers); + if (num_consumers <= 0) + { + FailTest("Consumer: ", kNumConsumers, " value ", num_consumers, " must be greater than 0."); + } + + const auto num_proxies_per_process = GetValue(args, kNumProxiesPerConsumerKey); + if (num_proxies_per_process <= 0) + { + FailTest( + "Consumer: ", kNumProxiesPerConsumerKey, " value ", num_proxies_per_process, " must be greater than 0."); + } + + const auto num_method_calls_per_proxy = GetValue(args, kNumMethodCallsPerProxyKey); + if (num_proxies_per_process <= 0) + { + FailTest( + "Consumer: ", kNumMethodCallsPerProxyKey, " value ", num_proxies_per_process, " must be greater than 0."); + } + + auto service_instance_manifest = GetValue(args, kServiceInstanceManifest); + + return {num_consumers, num_proxies_per_process, num_method_calls_per_proxy, std::move(service_instance_manifest)}; +} + +} // namespace score::mw::com::test + +int main(int argc, const char** argv) +{ + const auto test_configuration = score::mw::com::test::ReadCommandLineArguments(argc, argv); + const auto expected_num_consumers = score::mw::com::test::kEnabledMethodsPerProxy.size(); + if (test_configuration.num_consumers > expected_num_consumers) + { + score::mw::com::test::FailTest("Provider: num_consumers ", + test_configuration.num_consumers, + " must be smaller than the size of kEnabledMethodsPerProxy which is currently ", + expected_num_consumers); + } + + auto expected_method_call_count = + score::mw::com::test::CalculateExpectedMethodCallCounts(test_configuration.num_consumers, + test_configuration.num_proxies_per_process, + test_configuration.num_method_calls_per_proxy, + score::mw::com::test::kEnabledMethodsPerProxy); + score::mw::com::runtime::InitializeRuntime( + score::mw::com::runtime::RuntimeConfiguration{test_configuration.service_instance_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(), expected_method_call_count, test_configuration.num_consumers); + return EXIT_SUCCESS; +} diff --git a/score/mw/com/test/methods/multiple_proxies/provider.cpp b/score/mw/com/test/methods/multiple_proxies/provider.cpp new file mode 100644 index 000000000..022ff1158 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/provider.cpp @@ -0,0 +1,136 @@ +/******************************************************************************** + * 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/methods/multiple_proxies/provider.h" + +#include "score/mw/com/test/common_test_resources/fail_test.h" +#include "score/mw/com/test/methods/methods_test_resources/process_synchronizer.h" +#include "score/mw/com/test/methods/methods_test_resources/skeleton_container.h" +#include "score/mw/com/test/methods/multiple_proxies/common_resources.h" +#include "score/mw/com/test/methods/multiple_proxies/duplicate_signatures_datatype.h" +#include "score/mw/com/types.h" + +#include "score/result/result.h" + +#include + +#include +#include + +namespace score::mw::com::test +{ +namespace +{ +const std::string kInterprocessNotificationShmPath{"/multiple_proxies_test_interprocess_notification"}; + +const InstanceSpecifier kInstanceSpecifier = + InstanceSpecifier::Create(std::string{"multiple_proxies/MultiMethodProvider"}).value(); + +std::array, kNumRegisteredMethods> gMethodCallCounts{0U, 0U, 0U, 0U, 0U}; +std::array, kNumRegisteredMethods> gConcurrentCalls{0U, 0U, 0U, 0U, 0U}; +std::array, kNumRegisteredMethods> gOccuranceOfConcurrentCalls{0U, 0U, 0U, 0U, 0U}; + +using InpArgType = DuplicateSignatureSkeleton::InpArgType; +using ReturnType = DuplicateSignatureSkeleton::ReturnType; + +auto HandlerMaker(InpArgType idx) +{ + return [idx](InpArgType val) -> ReturnType { + const auto count = gMethodCallCounts.at(idx).fetch_add(1) + 1; + auto current_call_numner = gConcurrentCalls.at(idx).fetch_add(1); + if (current_call_numner > 0) + { + gOccuranceOfConcurrentCalls.at(idx).fetch_add(1); + } + + // Simulate some processing work to increase the time window for concurrent execution + // This makes it more likely that multiple handlers will be executing at the same time + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + + std::cout << "Provider: method" << idx << " called (count=" << count << ") with val=" << val << '\n'; + gConcurrentCalls.at(idx).fetch_add(-1); + return val * (kMethodResultMultiplierBase + idx); + }; +} + +} // namespace + +void run_provider(const score::cpp::stop_token& stop_token, + const std::vector& expected_method_call_counts, + std::size_t num_consumers) +{ + std::vector> process_synchronizers; + for (std::size_t i{0}; i < num_consumers; ++i) + { + process_synchronizers.emplace_back( + ProcessSynchronizer::CreateUniquePtr(CreateInterprocessNotificationShmPath(i))); + + std::cout << "provider sync: " << CreateInterprocessNotificationShmPath(i) << std::endl; + } + + SkeletonContainer skeleton_container{}; + + std::cout << "\nProvider: Step 1. Create skeleton" << std::endl; + skeleton_container.CreateSkeleton(kInstanceSpecifier, "multiple_proxies"); + auto& skeleton = skeleton_container.GetSkeleton(); + + std::cout << "Provider: Ready for method calls from multiple proxies\n"; + + std::cout << "\nProvider: Step 2. Register method handlers" << std::endl; + auto register_handler_or_fail = [](auto& method, std::size_t method_idx) { + if (!method.RegisterHandler(HandlerMaker(method_idx))) + { + FailTest("Methods multiple_proxies provider failed: Could not register handler for method ", method_idx); + } + }; + register_handler_or_fail(skeleton.method0, 0U); + register_handler_or_fail(skeleton.method1, 1U); + register_handler_or_fail(skeleton.method2, 2U); + register_handler_or_fail(skeleton.method3, 3U); + register_handler_or_fail(skeleton.method4, 4U); + + std::cout << "\nProvider: Step 3. Offer service" << std::endl; + skeleton_container.OfferService("multiple_proxies"); + + std::cout << "\nProvider: Step 4. Wait for consumers" << std::endl; + for (std::size_t i{0}; i < process_synchronizers.size(); ++i) + { + std::cout << "Provider: Waiting for Consumer" << i << " to finish...\n"; + if (!process_synchronizers.at(i)->WaitWithAbort(stop_token)) + { + FailTest("Provider: WaitWithAbort for Consumer", i, " was stopped by stop_token\n"); + } + } + std::cout << "Provider: All consumers have finished...\n\n"; + + std::cout << "\nProvider: Step 5. Verify the number of method calls are correct" << std::endl; + std::size_t idx{0}; + for (const auto& method_call_count : gMethodCallCounts) + { + const auto actual_method_call_count = method_call_count.load(); + const auto expected_method_call_count = expected_method_call_counts.at(idx); + + std::cout << "Recorded number of concurrent calls for idx: " << idx << "\n"; + std::cout << "Expected: " << expected_method_call_count << ". Actual: " << actual_method_call_count << "\n"; + if (expected_method_call_count != actual_method_call_count) + { + std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"; + std::cout << "Provider: method called unexpected amount of times.\n"; + std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"; + FailTest(); + } + idx++; + } + std::cout << "Provider: Shutting down.\n"; +} + +} // namespace score::mw::com::test diff --git a/score/mw/com/test/methods/multiple_proxies/provider.h b/score/mw/com/test/methods/multiple_proxies/provider.h new file mode 100644 index 000000000..f32450aa6 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/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_METHODS_MULTIPLE_PROXIES_PROVIDER_H +#define SCORE_MW_COM_TEST_METHODS_MULTIPLE_PROXIES_PROVIDER_H + +#include + +namespace score::mw::com::test +{ + +void run_provider(const score::cpp::stop_token& stop_token, + const std::vector& expected_method_call_count, + std::size_t num_consumers); + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_METHODS_MULTIPLE_PROXIES_PROVIDER_H diff --git a/score/mw/com/test/methods/signature_variations/BUILD b/score/mw/com/test/methods/signature_variations/BUILD deleted file mode 100644 index 1d4007c90..000000000 --- a/score/mw/com/test/methods/signature_variations/BUILD +++ /dev/null @@ -1,124 +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 -# ******************************************************************************* - -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 = "consumer", - srcs = ["consumer.cpp"], - hdrs = ["consumer.h"], - features = COMPILER_WARNING_FEATURES, - implementation_deps = [ - ":test_method_datatype", - "//score/mw/com", - "//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", - ], -) - -cc_library( - name = "provider", - srcs = ["provider.cpp"], - hdrs = ["provider.h"], - features = COMPILER_WARNING_FEATURES, - deps = [ - ":test_method_datatype", - "//score/mw/com", - "//score/mw/com/test/methods/methods_test_resources:method_provider", - "//score/mw/com/test/methods/methods_test_resources:process_synchronizer", - ], -) - -cc_binary( - name = "main_provider", - srcs = ["main_provider.cpp"], - data = ["config/mw_com_config.json"], - features = COMPILER_WARNING_FEATURES + [ - "aborts_upon_exception", - ], - deps = [ - ":provider", - "//score/mw/com", - "//score/mw/com/test/common_test_resources:assert_handler", - "//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", - "//score/mw/com", - "//score/mw/com/test/common_test_resources:assert_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/methods/signature_variations:__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/methods/signature_variations:__subpackages__", - ], -) - -test_suite( - name = "component_tests", - tests = [], - visibility = ["//score/mw/com/test/methods/signature_variations:__pkg__"], -) diff --git a/score/mw/com/test/methods/signature_variations/config/logging.json b/score/mw/com/test/methods/signature_variations/config/logging.json deleted file mode 100644 index 1ad3ef164..000000000 --- a/score/mw/com/test/methods/signature_variations/config/logging.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "appId": "SV", - "appDesc": "signature_variations", - "logLevel": "kDebug", - "logLevelThresholdConsole": "kDebug", - "logMode": "kRemote|kConsole" -} diff --git a/score/mw/com/test/methods/signature_variations/consumer.cpp b/score/mw/com/test/methods/signature_variations/consumer.cpp deleted file mode 100644 index 2d6123f00..000000000 --- a/score/mw/com/test/methods/signature_variations/consumer.cpp +++ /dev/null @@ -1,128 +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/methods/signature_variations/consumer.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" - -#include - -#include -#include -#include - -namespace score::mw::com::test -{ -namespace -{ - -const std::string kInterprocessNotificationShmPath{"/signature_variations_test_interprocess_notification"}; - -// Test values for methods with InArgs and Return -constexpr std::int32_t kTestValueA = 42; -constexpr std::int32_t kTestValueB = 23; - -// Test values for method with Return only -const std::int32_t kReturnOnlyMethodReturnValue{15}; - -// Test values for method with InArgs only -const std::int32_t kInArgOnlyMethodTestValueA{17}; -const std::int32_t kInArgOnlyMethodTestValueB{18}; - -const InstanceSpecifier kInstanceSpecifier = - InstanceSpecifier::Create(std::string{"test/methods/signature_variations/MethodSignature"}).value(); - -} // namespace - -int run_consumer() -{ - MethodConsumer consumer{}; - auto process_synchronizer_result = ProcessSynchronizer::Create(kInterprocessNotificationShmPath); - if (!(process_synchronizer_result.has_value())) - { - std::cerr << "Methods signature_variations consumer failed: Could not create ProcessSynchronizer" << std::endl; - return EXIT_FAILURE; - } - - // Step 1. Find service and create proxy - std::cout << "\nConsumer: Step 1" << std::endl; - consumer.CreateProxy(kInstanceSpecifier, "signature_variations"); - - // Step 2. Call method with InArgs and Return with copy - std::cout << "\nConsumer: Step 2" << std::endl; - if (!consumer.CallMethodWithInArgsAndReturn( - kTestValueA, kTestValueB, MethodConsumer::CopyMode::WITH_COPY)) - { - std::cerr << "Methods signature_variations consumer failed: CallMethodWithInArgsAndReturnWithCopy" << std::endl; - process_synchronizer_result->Notify(); - return EXIT_FAILURE; - } - - // Step 3. Call zero-copy method with InArgs and Return - std::cout << "\nConsumer: Step 3" << std::endl; - if (!consumer.CallMethodWithInArgsAndReturn( - kTestValueA, kTestValueB, MethodConsumer::CopyMode::ZERO_COPY)) - { - std::cerr << "Methods signature_variations consumer failed: CallMethodWithInArgsAndReturnZeroCopy" << std::endl; - process_synchronizer_result->Notify(); - return EXIT_FAILURE; - } - - // Step 4. Call method with InArgs only with copy - std::cout << "\nConsumer: Step 4" << std::endl; - if (!consumer.CallMethodWithInArgsOnly(kInArgOnlyMethodTestValueA, - kInArgOnlyMethodTestValueB, - MethodConsumer::CopyMode::WITH_COPY)) - { - std::cerr << "Methods signature_variations consumer failed: CallMethodWithInArgsOnlyWithCopy" << std::endl; - process_synchronizer_result->Notify(); - return EXIT_FAILURE; - } - - // Step 5. Call zero-copy method with InArgs only - std::cout << "\nConsumer: Step 5" << std::endl; - if (!consumer.CallMethodWithInArgsOnly(kInArgOnlyMethodTestValueA, - kInArgOnlyMethodTestValueB, - MethodConsumer::CopyMode::ZERO_COPY)) - { - std::cerr << "Methods signature_variations consumer failed: CallMethodWithInArgsOnlyZeroCopy" << std::endl; - process_synchronizer_result->Notify(); - return EXIT_FAILURE; - } - - // Step 6. Call method with return only with copy - std::cout << "\nConsumer: Step 6" << std::endl; - if (!consumer.CallMethodWithReturnOnly(kReturnOnlyMethodReturnValue)) - { - std::cerr << "Methods signature_variations consumer failed: CallMethodWithReturnOnly" << std::endl; - process_synchronizer_result->Notify(); - return EXIT_FAILURE; - } - - // Step 7. Call method without InArgs or return - std::cout << "\nConsumer: Step 7" << std::endl; - if (!consumer.CallMethodWithoutInArgsOrReturn()) - { - std::cerr << "Methods signature_variations consumer failed: CallMethodWithoutInArgsOrReturn" << std::endl; - process_synchronizer_result->Notify(); - return EXIT_FAILURE; - } - - process_synchronizer_result->Notify(); - - return EXIT_SUCCESS; -} - -} // namespace score::mw::com::test diff --git a/score/mw/com/test/methods/signature_variations/integration_test/signature_variations_test.py b/score/mw/com/test/methods/signature_variations/integration_test/signature_variations_test.py deleted file mode 100644 index 5f967f154..000000000 --- a/score/mw/com/test/methods/signature_variations/integration_test/signature_variations_test.py +++ /dev/null @@ -1,34 +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 -# ******************************************************************************* - - -def provider(target, **kwargs): - args = [] - return target.wrap_exec("bin/main_provider", args, cwd="/opt/MainProviderApp", wait_on_exit=True, **kwargs) - - -def consumer(target, **kwargs): - args = [] - return target.wrap_exec("bin/main_consumer", args, cwd="/opt/MainConsumerApp", wait_on_exit=True, **kwargs) - - -def test_signature_variations(target): - """Test method calls for different signature variations between provider and consumer. - - The provider Skeleton contains four methods: Method with InArg and Return, - Method with InArg only, Method with Return only, and Method without InArg or Return. - The consumer Proxy calls zero-copy and with-copy variants for each method, - verifying all calls succeed and return expected values. - """ - with provider(target), consumer(target): - pass diff --git a/score/mw/com/test/methods/signature_variations/provider.cpp b/score/mw/com/test/methods/signature_variations/provider.cpp deleted file mode 100644 index ffd5ec0f1..000000000 --- a/score/mw/com/test/methods/signature_variations/provider.cpp +++ /dev/null @@ -1,106 +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/methods/signature_variations/provider.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" - -#include "score/result/result.h" - -#include - -#include -#include - -namespace score::mw::com::test -{ -namespace -{ -const std::string kInterprocessNotificationShmPath{"/signature_variations_test_interprocess_notification"}; - -const InstanceSpecifier kInstanceSpecifier = - InstanceSpecifier::Create(std::string{"test/methods/signature_variations/MethodSignature"}).value(); - -const std::int32_t kReturnOnlyMethodReturnValue{15}; -const std::int32_t kInArgOnlyMethodTestValueA{17}; -const std::int32_t kInArgOnlyMethodTestValueB{18}; - -} // namespace - -bool run_provider(const score::cpp::stop_token& stop_token) -{ - MethodProvider provider{}; - auto process_synchronizer_result = ProcessSynchronizer::Create(kInterprocessNotificationShmPath); - if (!(process_synchronizer_result.has_value())) - { - std::cerr << "Methods signature_variations provider failed: Could not create ProcessSynchronizer" << std::endl; - return EXIT_FAILURE; - } - - // Step 1. Create skeleton - std::cout << "\nProvider: Step 1" << std::endl; - if (!provider.CreateSkeleton(kInstanceSpecifier)) - { - std::cerr << "Methods signature_variations provider failed: CreateSkeleton" << std::endl; - return EXIT_FAILURE; - } - - // Step 2. Register method handlers - std::cout << "\nProvider: Step 2" << std::endl; - if (!provider.RegisterMethodHandlerWithInArgsAndReturn()) - { - std::cerr << "Methods signature_variations provider failed: RegisterMethodHandlerWithInArgsAndReturn" - << std::endl; - return EXIT_FAILURE; - } - if (!provider.RegisterMethodHandlerWithInArgsOnly(kInArgOnlyMethodTestValueA, kInArgOnlyMethodTestValueB)) - { - std::cerr << "Methods signature_variations provider failed: RegisterMethodHandlerWithInArgsOnly" << std::endl; - return EXIT_FAILURE; - } - if (!provider.RegisterMethodHandlerWithReturnOnly(kReturnOnlyMethodReturnValue)) - { - std::cerr << "Methods signature_variations provider failed: RegisterMethodHandlerWithReturnOnly" << std::endl; - return EXIT_FAILURE; - } - if (!provider.RegisterWithoutInArgsOrReturn()) - { - std::cerr << "Methods signature_variations provider failed: RegisterWithoutInArgsOrReturn" << std::endl; - return EXIT_FAILURE; - } - - // Step 3. Offer service - std::cout << "\nProvider: Step 3" << std::endl; - if (!provider.OfferService()) - { - std::cerr << "Methods signature_variations provider failed: OfferService" << std::endl; - return EXIT_FAILURE; - } - - // Step 4. Wait for proxy test to finish - std::cout << "Provider: Ready for method calls" << std::endl; - if (!process_synchronizer_result->WaitWithAbort(stop_token)) - { - std::cerr << "Methods signature_variations provider failed: WaitForProxyTestToFinish was stopped by " - "stop_token instead of notification" - << std::endl; - return EXIT_FAILURE; - } - - std::cout << "Provider: Shutting down" << std::endl; - return EXIT_SUCCESS; -} - -} // namespace score::mw::com::test diff --git a/score/mw/com/test/methods/signature_variations/test_method_datatype.h b/score/mw/com/test/methods/signature_variations/test_method_datatype.h deleted file mode 100644 index 01668b3cb..000000000 --- a/score/mw/com/test/methods/signature_variations/test_method_datatype.h +++ /dev/null @@ -1,54 +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 - ********************************************************************************/ -#ifndef SCORE_MW_COM_TEST_METHODS_SIGNATURE_VARIATIONS_TEST_METHOD_DATATYPE_H -#define SCORE_MW_COM_TEST_METHODS_SIGNATURE_VARIATIONS_TEST_METHOD_DATATYPE_H - -#include "score/mw/com/types.h" - -#include - -namespace score::mw::com::test -{ - -/// \brief Test interface with methods covering all signature variations -/// \tparam T Either ProxyTrait or SkeletonTrait -template -class MethodSignatureInterface : public T::Base -{ - public: - using T::Base::Base; - - /// \brief Method with both InArgs and Result - typename T::template Method with_in_args_and_return{ - *this, - "with_in_args_and_return"}; - - /// \brief Method with only InArgs, no Result (void return) - typename T::template Method with_in_args_only{*this, "with_in_args_only"}; - - /// \brief Method with only Result, no InArgs - typename T::template Method with_return_only{*this, "with_return_only"}; - - /// \brief Method without InArgs or Result - typename T::template Method without_args_or_return{*this, "without_args_or_return"}; -}; - -/// \brief Proxy side of the test service -using MethodSignatureProxy = score::mw::com::AsProxy; - -/// \brief Skeleton side of the test service -using MethodSignatureSkeleton = score::mw::com::AsSkeleton; - -} // namespace score::mw::com::test - -#endif // SCORE_MW_COM_TEST_METHODS_SIGNATURE_VARIATIONS_TEST_METHOD_DATATYPE_H diff --git a/score/mw/com/test/methods/stop_offer_during_call/BUILD b/score/mw/com/test/methods/stop_offer_during_call/BUILD new file mode 100644 index 000000000..36e05ea6c --- /dev/null +++ b/score/mw/com/test/methods/stop_offer_during_call/BUILD @@ -0,0 +1,61 @@ +load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") +load("//bazel/tools:json_schema_validator.bzl", "validate_json_schema_test") + +# ******************************************************************************* +# 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("//score/mw/com/test:pkg_application.bzl", "pkg_application") + +validate_json_schema_test( + name = "validate_mw_com_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, + visibility = ["//visibility:private"], + deps = [ + "//score/mw/com", + ], +) + +cc_binary( + name = "stop_offer_during_call", + srcs = ["stop_offer_during_call.cpp"], + data = [ + "config/mw_com_config.json", + ], + features = COMPILER_WARNING_FEATURES, + deps = [ + ":test_method_datatype", + "//score/mw/com", + "//score/mw/com:runtime", + "//score/mw/com/test/common_test_resources:fail_test", + ], +) + +pkg_application( + name = "stop-offer-during-call-pkg", + app_name = "StopOfferDuringCallApp", + bin = [":stop_offer_during_call"], + etc = [ + "config/mw_com_config.json", + ], + visibility = [ + "//score/mw/com/test/methods/stop_offer_during_call/integration_test:__subpackages__", + ], +) diff --git a/score/mw/com/test/methods/stop_offer_during_call/config/mw_com_config.json b/score/mw/com/test/methods/stop_offer_during_call/config/mw_com_config.json new file mode 100644 index 000000000..5d63c3fd5 --- /dev/null +++ b/score/mw/com/test/methods/stop_offer_during_call/config/mw_com_config.json @@ -0,0 +1,65 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/methods/stop_offer_during_call/StopOfferTest", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 5000, + "methods": [ + { + "methodName": "long_running_method", + "methodId": 1 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "test/methods/StopOfferTest", + "serviceTypeName": "/test/methods/stop_offer_during_call/StopOfferTest", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "QM", + "binding": "SHM", + "methods": [ + { + "methodName": "long_running_method", + "queueSize": 1 + } + ], + "allowedConsumer": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + }, + "allowedProvider": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + } + } + ] + } + ], + "global": { + "asil-level": "QM" + } +} diff --git a/score/mw/com/test/methods/signature_variations/integration_test/BUILD b/score/mw/com/test/methods/stop_offer_during_call/integration_test/BUILD similarity index 71% rename from score/mw/com/test/methods/signature_variations/integration_test/BUILD rename to score/mw/com/test/methods/stop_offer_during_call/integration_test/BUILD index 9906a1db7..190d5a257 100644 --- a/score/mw/com/test/methods/signature_variations/integration_test/BUILD +++ b/score/mw/com/test/methods/stop_offer_during_call/integration_test/BUILD @@ -17,22 +17,14 @@ load("//quality/integration_testing:integration_testing.bzl", "integration_test" pkg_filegroup( name = "filesystem", srcs = [ - "//score/mw/com/test/methods/signature_variations:main_consumer-pkg", - "//score/mw/com/test/methods/signature_variations:main_provider-pkg", + "//score/mw/com/test/methods/stop_offer_during_call:stop-offer-during-call-pkg", ], ) integration_test( - name = "signature_variations_test", + name = "stop_offer_during_call", srcs = [ - "signature_variations_test.py", + "stop_offer_during_call_test.py", ], filesystem = ":filesystem", ) - -test_suite( - name = "component_tests", - tests = [ - ":signature_variations_test", - ], -) diff --git a/score/mw/com/test/methods/stop_offer_during_call/integration_test/stop_offer_during_call_test.py b/score/mw/com/test/methods/stop_offer_during_call/integration_test/stop_offer_during_call_test.py new file mode 100644 index 000000000..c6f74ecaa --- /dev/null +++ b/score/mw/com/test/methods/stop_offer_during_call/integration_test/stop_offer_during_call_test.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 +# ******************************************************************************* + +def consumer_and_provider(target, **kwargs): + args = [] + return target.wrap_exec("./bin/stop_offer_during_call",args, + cwd="/opt/StopOfferDuringCallApp/", wait_on_exit=True, **kwargs) + +def test_multiple_proxies(target, **kwargs): + with consumer_and_provider(target, **kwargs): + pass diff --git a/score/mw/com/test/methods/stop_offer_during_call/main.cpp b/score/mw/com/test/methods/stop_offer_during_call/main.cpp new file mode 100644 index 000000000..97b2b0608 --- /dev/null +++ b/score/mw/com/test/methods/stop_offer_during_call/main.cpp @@ -0,0 +1,208 @@ +/******************************************************************************** + * 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/fail_test.h" +#include "score/mw/com/test/methods/stop_offer_during_call/test_method_datatype.h" + +#include "score/mw/com/runtime.h" +#include "score/mw/com/types.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ +using score::mw::com::InstanceSpecifier; +using score::mw::com::MethodReturnTypePtr; +using score::mw::com::test::FailTest; +using score::mw::com::test::StopOfferDuringCallProxy; +using score::mw::com::test::StopOfferDuringCallSkeleton; + +const std::string_view kInstanceSpecifierSV = "test/methods/StopOfferTest"; +void check_result(std::future>>&& result_future, + std::int32_t expected_value) +{ + std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; + auto result = std::move(result_future).get(); + std::cout << "Async call completed, checking result...\n"; + if (!result.has_value()) + { + FailTest("FAIL: Method call failed with error unexpectedly: ", result.error()); + } + + auto& return_value_ptr = result.value(); + + if (return_value_ptr.get() == nullptr) + { + FailTest(" FAIL: Method call returned null pointer, instead of a valid ptr to shm."); + } + + // clang-format off + if (*return_value_ptr != expected_value) + { + FailTest("FAIL: Method call returned wrong value. Expected: ", expected_value, ", Actual: ", *return_value_ptr); + } + // clang-format on + + std::cout << "Method call succeeded with expected value: " << *return_value_ptr << std::endl; +} +struct ExecutionMarkers +{ + std::atomic method_started{false}; + std::atomic green_light_for_completion{false}; + std::atomic method_completed{false}; +}; + +void ResetExecutionMarkers(ExecutionMarkers& markers) +{ + markers.method_started = false; + markers.green_light_for_completion = false; + markers.method_completed = false; +} + +} // namespace + +int main(int argc, const char** argv) +{ + score::mw::com::runtime::InitializeRuntime(argc, argv); + + ExecutionMarkers execution_markers{}; + + std::cout << "Step 1: Create skeleton with long-running handler." << std::endl; + + auto instance_specifier_result = InstanceSpecifier::Create(std::string{kInstanceSpecifierSV}); + if (!instance_specifier_result.has_value()) + { + std::cout << "Could not create Instance specifier!.\n" + << "Here is why: " << instance_specifier_result.error() << "\n\n" + << std::flush; + } + auto instance_specifier = std::move(instance_specifier_result).value(); + + auto skeleton_result = StopOfferDuringCallSkeleton::Create(instance_specifier); + + if (!skeleton_result.has_value()) + { + std::cerr << "Failed to create skeleton: " << skeleton_result.error() << std::endl; + return EXIT_FAILURE; + } + auto skeleton = std::make_unique(std::move(skeleton_result).value()); + std::cout << "Step 2.1: skeleton created." << std::endl; + + auto handler = [&execution_markers](std::int32_t input) -> std::int32_t { + execution_markers.method_started = true; + constexpr auto idling_time = std::chrono::milliseconds(1); + while (!execution_markers.green_light_for_completion) + { + std::this_thread::sleep_for(idling_time); + } + std::cout << "Consumer: green light was given... Continuing to completion...\n"; + execution_markers.method_completed = true; + return input * 2; + }; + + std::cout << "Step 2.2: Registering handler." << std::endl; + auto register_result = skeleton->long_running_method.RegisterHandler(std::move(handler)); + if (!register_result.has_value()) + { + std::cerr << "Failed to register handler: " << register_result.error() << std::endl; + return EXIT_FAILURE; + } + + auto offer_result = skeleton->OfferService(); + if (!offer_result.has_value()) + { + std::cerr << "Failed to offer service: " << offer_result.error() << std::endl; + return EXIT_FAILURE; + } + std::cout << "Step 2.3: Service offered" << std::endl; + + std::cout << "Step 3: Creating proxy" << std::endl; + auto find_result = StopOfferDuringCallProxy::FindService(instance_specifier); + if (!find_result.has_value() || find_result->size() != 1) + { + std::cerr << "FindService failed or returned wrong count" << std::endl; + return EXIT_FAILURE; + } + + auto proxy_result = StopOfferDuringCallProxy::Create((*find_result)[0]); + if (!proxy_result.has_value()) + { + std::cerr << "Failed to create proxy: " << proxy_result.error() << std::endl; + return EXIT_FAILURE; + } + auto proxy = std::make_unique(std::move(proxy_result).value()); + + std::cout << "Step 3.1: Call the long running method asynchronously\n"; + constexpr std::int32_t test_input = 12; + constexpr std::int32_t expected_output = 24; + + constexpr std::size_t max_repetition_count{5}; + std::cout + << "Step 4: Starting test loop. Call method, wait for it to start, call StopOfferService, check result. Repeat " + << max_repetition_count << " times.\n"; + std::size_t repetition_count{0}; + while (repetition_count < max_repetition_count) + { + + auto async_result = + std::async(std::launch::async, [&proxy, test_input]() -> score::Result> { + auto result = proxy->long_running_method(test_input); + return result; + }); + + std::cout << "Step 4.1: Wait for handler to start, then call StopOfferService\n" << std::flush; + while (!execution_markers.method_started) + { + constexpr auto idling_time = std::chrono::microseconds(50); + std::this_thread::sleep_for(idling_time); + } + + if (execution_markers.method_completed) + { + std::cout << "Step 4.2: StopOfferService was not called since the method was already completed\n" + << std::flush; + continue; + } + + // green_light_for_completion needs to be set before calling StopOfferService, otherwise we will get a + // deadlock, since StopOfferService always waits for the completion of the method calls before it + // completes. + execution_markers.green_light_for_completion = true; + skeleton->StopOfferService(); + std::cout << "Step 4.3: StopOfferService was called while the method was still running\n" << std::flush; + + ++repetition_count; + check_result(std::move(async_result), expected_output); + ResetExecutionMarkers(execution_markers); + } + + // Test passes if: + // - No crash occurred + // - StopOfferService didn't hang indefinitely + // - Method call either succeeded or returned an error (not hung) + + if (repetition_count == 0) + { + std::cerr << "FAIL: StopOfferService was never called during method execution" << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/score/mw/com/test/methods/stop_offer_during_call/stop_offer_during_call.cpp b/score/mw/com/test/methods/stop_offer_during_call/stop_offer_during_call.cpp new file mode 100644 index 000000000..4564b9ad7 --- /dev/null +++ b/score/mw/com/test/methods/stop_offer_during_call/stop_offer_during_call.cpp @@ -0,0 +1,228 @@ +/******************************************************************************** + * 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/fail_test.h" +#include "score/mw/com/test/methods/stop_offer_during_call/test_method_datatype.h" + +#include "score/mw/com/runtime.h" +#include "score/mw/com/types.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ +using namespace score::mw::com; +using namespace score::mw::com::test; + +const std::string_view kInstanceSpecifierSV = "test/methods/StopOfferTest"; + +void check_result(std::future>>&& result_future, + std::int32_t expected_value) +{ + auto result = std::move(result_future).get(); + std::cout << "Async call completed, checking result...\n"; + if (!result.has_value()) + { + FailTest("FAIL: Method call failed with error unexpectedly: ", result.error()); + } + + auto& return_value_ptr = result.value(); + + if (return_value_ptr.get() == nullptr) + { + FailTest(" FAIL: Method call returned null pointer, instead of a valid ptr to shm."); + } + + // clang-format off + if (*return_value_ptr != expected_value) + { + FailTest("FAIL: Method call returned wrong value. Expected: ", expected_value, ", Actual: ", *return_value_ptr); + } + // clang-format on + + std::cout << "Method call succeeded with expected value: " << *return_value_ptr << std::endl; +} + +void offer_service(StopOfferDuringCallSkeleton& skeleton) +{ + auto offer_result = skeleton.OfferService(); + if (!offer_result.has_value()) + { + FailTest("Could not offer service: ", offer_result.error()); + } + std::cout << "Service offered successfully\n"; +} + +auto find_service(InstanceSpecifier& instance_specifier) -> StopOfferDuringCallProxy +{ + + auto find_result = StopOfferDuringCallProxy::FindService(instance_specifier); + if (!find_result.has_value() || find_result->size() != 1) + { + FailTest("FindService failed or returned wrong count. Error: ", find_result.error()); + } + + auto proxy_result = StopOfferDuringCallProxy::Create((*find_result)[0]); + if (!proxy_result.has_value()) + { + FailTest("Failed to create proxy: ", proxy_result.error()); + } + auto proxy = std::move(proxy_result).value(); + return proxy; +} + +struct ExecutionMarkers +{ + std::atomic method_started{false}; + std::atomic green_light_for_completion{false}; + std::atomic method_completed{false}; +}; + +void ResetExecutionMarkers(ExecutionMarkers& markers) +{ + markers.method_started = false; + markers.green_light_for_completion = false; + markers.method_completed = false; +} + +} // namespace + +/// Test passes if: +/// - No crash occurred +/// - StopOfferService didn't hang indefinitely +/// - Method call succeeded +int main(int argc, const char** argv) +{ + score::mw::com::runtime::InitializeRuntime(argc, argv); + + ExecutionMarkers execution_markers{}; + + std::cout << "Step 1: Create skeleton with long-running handler." << std::endl; + + auto instance_specifier_result = InstanceSpecifier::Create(std::string{kInstanceSpecifierSV}); + if (!instance_specifier_result.has_value()) + { + std::cout << "Could not create Instance specifier!.\n" + << "Here is why: " << instance_specifier_result.error() << "\n\n" + << std::flush; + } + auto instance_specifier = std::move(instance_specifier_result).value(); + + auto skeleton_result = StopOfferDuringCallSkeleton::Create(instance_specifier); + + if (!skeleton_result.has_value()) + { + std::cerr << "Failed to create skeleton: " << skeleton_result.error() << std::endl; + return EXIT_FAILURE; + } + + auto skeleton = std::move(skeleton_result).value(); + std::cout << "Step 2.1: skeleton created." << std::endl; + + auto handler = [&execution_markers](std::int32_t input) -> std::int32_t { + execution_markers.method_started = true; + constexpr auto idling_time = std::chrono::milliseconds(1); + while (!execution_markers.green_light_for_completion) + { + std::this_thread::sleep_for(idling_time); + } + std::cout << "Consumer: green light was given... Continuing to completion...\n"; + execution_markers.method_completed = true; + return input * 2; + }; + + std::cout << "Step 2.2: Registering handler." << std::endl; + auto register_result = skeleton.long_running_method.RegisterHandler(std::move(handler)); + if (!register_result.has_value()) + { + std::cerr << "Failed to register handler: " << register_result.error() << std::endl; + return EXIT_FAILURE; + } + + offer_service(skeleton); + + std::cout << "Step 2.3: Creating a proxy and finding reoffered service" << std::endl; + auto proxy = find_service(instance_specifier); + + std::cout << "Step 3: Call the long running method asynchronously\n"; + constexpr std::int32_t test_input = 12; + constexpr std::int32_t expected_output = 24; + + constexpr std::size_t max_repetition_count{5}; + std::cout + << "Step 4: Starting test loop. Call method, wait for it to start, call StopOfferService, check result. Repeat " + << max_repetition_count << " times.\n"; + std::size_t repetiction_count{0}; + + while (repetiction_count < max_repetition_count) + { + + std::cout << "Step 4.1: (Re)Offering the service\n" << std::flush; + + auto async_result = + std::async(std::launch::async, [&proxy, test_input]() -> score::Result> { + while (true) + { + // this loop is required since the proxy might not yet be resubscribed to the reoffered service + auto result = proxy.long_running_method(test_input); + if (result.has_value()) + { + return result; + } + auto idling_time = std::chrono::milliseconds(1); + std::this_thread::sleep_for(idling_time); + } + }); + + std::cout << "Step 4.2: Wait for handler to start, then call StopOfferService\n" << std::flush; + while (!execution_markers.method_started) + { + constexpr auto idling_time = std::chrono::microseconds(50); + std::cout << "."; + std::this_thread::sleep_for(idling_time); + } + + if (execution_markers.method_completed) + { + std::cout << "Step 4.3: StopOfferService was not called since the method was already completed\n" + << std::flush; + continue; + } + + // green_light_for_completion needs to be set before calling StopOfferService, otherwise we will get a + // deadlock, since StopOfferService always waits for the completion of the method calls before it + // completes. + execution_markers.green_light_for_completion = true; + skeleton.StopOfferService(); + std::cout << "Step 4.4: StopOfferService was called while the method was still running\n" << std::flush; + + ++repetiction_count; + check_result(std::move(async_result), expected_output); + ResetExecutionMarkers(execution_markers); + offer_service(skeleton); + } + + if (repetiction_count == 0) + { + std::cerr << "FAIL: StopOfferService was never called during method execution" << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/score/mw/com/test/methods/signature_variations/test_method_datatype.cpp b/score/mw/com/test/methods/stop_offer_during_call/test_method_datatype.cpp similarity index 87% rename from score/mw/com/test/methods/signature_variations/test_method_datatype.cpp rename to score/mw/com/test/methods/stop_offer_during_call/test_method_datatype.cpp index 46c88b2a5..7e8adcb64 100644 --- a/score/mw/com/test/methods/signature_variations/test_method_datatype.cpp +++ b/score/mw/com/test/methods/stop_offer_during_call/test_method_datatype.cpp @@ -10,4 +10,4 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#include "score/mw/com/test/methods/signature_variations/test_method_datatype.h" +#include "score/mw/com/test/methods/stop_offer_during_call/test_method_datatype.h" diff --git a/score/mw/com/test/methods/stop_offer_during_call/test_method_datatype.h b/score/mw/com/test/methods/stop_offer_during_call/test_method_datatype.h new file mode 100644 index 000000000..6c6272c67 --- /dev/null +++ b/score/mw/com/test/methods/stop_offer_during_call/test_method_datatype.h @@ -0,0 +1,44 @@ +/******************************************************************************** + * 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_METHODS_STOP_OFFER_DURING_CALL_METHOD_DATATYPE_H +#define SCORE_MW_COM_TEST_METHODS_STOP_OFFER_DURING_CALL_METHOD_DATATYPE_H + +#include "score/mw/com/types.h" + +#include + +namespace score::mw::com::test +{ + +/// \brief Test interface for StopOfferService during active method call scenario +template +class StopOfferDuringCallInterface : public T::Base +{ + public: + using T::Base::Base; + + /// \brief Method that simulates a long-running operation + /// The handler will sleep for the specified milliseconds to allow testing + /// StopOfferService being called while the method is executing + typename T::template Method long_running_method{*this, "long_running_method"}; +}; + +/// \brief Proxy side of the test service +using StopOfferDuringCallProxy = score::mw::com::AsProxy; + +/// \brief Skeleton side of the test service +using StopOfferDuringCallSkeleton = score::mw::com::AsSkeleton; + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_METHODS_STOP_OFFER_DURING_CALL_METHOD_DATATYPE_H