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..1b7e3481b --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/BUILD @@ -0,0 +1,159 @@ +# ******************************************************************************* +# 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/common_test_resources:proxy_container", + "//score/mw/com/test/methods/methods_test_resources:process_synchronizer", + ], +) + +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/common_test_resources:skeleton_container", + "//score/mw/com/test/methods/methods_test_resources:process_synchronizer", + ], +) + +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", + "config/logging.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", + "config/logging.json", + ], + 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", + "config/logging.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..7bc1bbd61 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/common_resources.cpp @@ -0,0 +1,28 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#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; +} + +} // 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..e88ff066b --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/common_resources.h @@ -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 + ********************************************************************************/ +#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 +#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); + +/// \brief Utility function to print a line to the console in a thread safe manner (i.e. the entire string is printed at +/// once, without interleaving with other print statements from other threads). +template +void PrintLine(Args&&... args) +{ + std::ostringstream oss; + (oss << ... << std::forward(args)); + std::cout << oss.str(); +} + +} // 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..014421747 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/config/combined_mw_com_config.json @@ -0,0 +1,321 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethod", + "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/MultiMethod", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "method0", + "queueSize": 1 + }, + { + "methodName": "method1", + "queueSize": 1 + }, + { + "methodName": "method2", + "queueSize": 1 + }, + { + "methodName": "method3", + "queueSize": 1 + }, + { + "methodName": "method4", + "queueSize": 1 + } + ], + "allowedConsumer": { + "QM": [ + 0,1263030 + ], + "B": [ + 0,1263030 + ] + }, + "allowedProvider": { + "QM": [ + 0,1263030 + ], + "B": [ + 0,1263030 + ] + } + } + ] + }, + { + "instanceSpecifier": "multiple_proxies/MultiMethodConsumer0", + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethod", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "method0", + "queueSize": 1 + }, + { + "methodName": "method1", + "queueSize": 1 + }, + { + "methodName": "method2", + "queueSize": 1, + "use": false + }, + { + "methodName": "method3", + "queueSize": 1, + "use": false + }, + { + "methodName": "method4", + "queueSize": 1, + "use": false + } + ], + "allowedConsumer": { + "QM": [ + 0,1263030 + ], + "B": [ + 0,1263030 + ] + }, + "allowedProvider": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + } + } + ] + }, + { + "instanceSpecifier": "multiple_proxies/MultiMethodConsumer1", + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethod", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "method0", + "queueSize": 1, + "use": false + }, + { + "methodName": "method1", + "queueSize": 1 + }, + { + "methodName": "method2", + "queueSize": 1 + }, + { + "methodName": "method3", + "queueSize": 1, + "use": false + }, + { + "methodName": "method4", + "queueSize": 1, + "use": false + } + ], + "allowedConsumer": { + "QM": [ + 0,1263030 + ], + "B": [ + 0,1263030 + ] + }, + "allowedProvider": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + } + } + ] + }, + { + "instanceSpecifier": "multiple_proxies/MultiMethodConsumer2", + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethod", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "method0", + "queueSize": 1, + "use": false + }, + { + "methodName": "method1", + "queueSize": 1, + "use": false + }, + { + "methodName": "method2", + "queueSize": 1, + "use": false + }, + { + "methodName": "method3", + "queueSize": 1 + }, + { + "methodName": "method4", + "queueSize": 1 + } + ], + "allowedConsumer": { + "QM": [ + 0,1263030 + ], + "B": [ + 0,1263030 + ] + }, + "allowedProvider": { + "QM": [ + 0 + ], + "B": [ + 0 + ] + } + } + ] + }, + { + "instanceSpecifier": "multiple_proxies/MultiMethodConsumer3", + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethod", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "method0", + "queueSize": 1 + }, + { + "methodName": "method1", + "queueSize": 1, + "use": false + }, + { + "methodName": "method2", + "queueSize": 1 + }, + { + "methodName": "method3", + "queueSize": 1 + }, + { + "methodName": "method4", + "queueSize": 1, + "use": false + } + ], + "allowedConsumer": { + "QM": [ + 0,1263030 + ], + "B": [ + 0,1263030 + ] + }, + "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..9d0771aa8 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/config/consumer0_mw_com_config.json @@ -0,0 +1,101 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethod", + "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/MultiMethodConsumer0", + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethod", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "method0", + "queueSize": 1 + }, + { + "methodName": "method1", + "queueSize": 1 + }, + { + "methodName": "method2", + "queueSize": 1, + "use": false + }, + { + "methodName": "method3", + "queueSize": 1, + "use": false + }, + { + "methodName": "method4", + "queueSize": 1, + "use": false + } + ], + "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..3e78c6f92 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/config/consumer1_mw_com_config.json @@ -0,0 +1,101 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethod", + "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/MultiMethodConsumer1", + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethod", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "method0", + "queueSize": 1, + "use": false + }, + { + "methodName": "method1", + "queueSize": 1 + }, + { + "methodName": "method2", + "queueSize": 1 + }, + { + "methodName": "method3", + "queueSize": 1, + "use": false + }, + { + "methodName": "method4", + "queueSize": 1, + "use": false + } + ], + "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..504f8c192 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/config/consumer2_mw_com_config.json @@ -0,0 +1,101 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethod", + "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/MultiMethodConsumer2", + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethod", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "method0", + "queueSize": 1, + "use": false + }, + { + "methodName": "method1", + "queueSize": 1, + "use": false + }, + { + "methodName": "method2", + "queueSize": 1, + "use": false + }, + { + "methodName": "method3", + "queueSize": 1 + }, + { + "methodName": "method4", + "queueSize": 1 + } + ], + "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..e5cba2cde --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/config/consumer3_mw_com_config.json @@ -0,0 +1,100 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethod", + "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/MultiMethodConsumer3", + "serviceTypeName": "/test/methods/multiple_proxies/MultiMethod", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "method0", + "queueSize": 1 + }, + { + "methodName": "method1", + "queueSize": 1, + "use": false + }, + { + "methodName": "method2", + "queueSize": 1 + }, + { + "methodName": "method3", + "queueSize": 1 + }, + { + "methodName": "method4", + "queueSize": 1, + "use": false + } + ], + "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/logging.json b/score/mw/com/test/methods/multiple_proxies/config/logging.json new file mode 100644 index 000000000..9b28a1f1b --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/config/logging.json @@ -0,0 +1,7 @@ +{ + "appId": "MTMU", + "appDesc": "methods_multiple_proxies", + "logLevel": "kDebug", + "logLevelThresholdConsole": "kDebug", + "logMode": "kRemote|kConsole" +} 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..54902c871 --- /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/MultiMethod", + "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/MultiMethod", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "B", + "binding": "SHM", + "methods": [ + { + "methodName": "method0", + "queueSize": 1 + }, + { + "methodName": "method1", + "queueSize": 1 + }, + { + "methodName": "method2", + "queueSize": 1 + }, + { + "methodName": "method3", + "queueSize": 1 + }, + { + "methodName": "method4", + "queueSize": 1 + } + ], + "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..c0b7ebf29 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/consumer.cpp @@ -0,0 +1,215 @@ +/******************************************************************************** + * 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/common_test_resources/proxy_container.h" +#include "score/mw/com/test/methods/methods_test_resources/process_synchronizer.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 + +namespace score::mw::com::test +{ +namespace +{ + +const std::string kInterprocessNotificationShmPath{"/multiple_proxies_test_interprocess_notification"}; + +enum class CopyMode +{ + ZERO_COPY, + WITH_COPY +}; + +void ValidateMethodCallResult( + score::Result>& method_call_return_result, + std::size_t proxy_id, + std::size_t consumer_id, + std::size_t method_id, + std::size_t call_idx, + std::int32_t input_value) +{ + if (!method_call_return_result.has_value()) + { + // clang-format off + FailTest("Consumer process ", consumer_id, ", Proxy ", proxy_id, ": method", method_id, " call failed: ", method_call_return_result.error()); + // clang-format on + } + + const auto method_call_return = *(method_call_return_result.value()); + const bool is_method_return_valid = + ((method_call_return.sent_value == input_value) && (method_call_return.call_index == call_idx) && + (method_call_return.consumer_id == consumer_id) && (method_call_return.proxy_id == proxy_id)); + + if (!is_method_return_valid) + { + // clang-format off + FailTest("Consumer process ", consumer_id, ", Proxy ", proxy_id, ": method", method_id, + " returned unexpected payload. Actual={sent_value:", method_call_return.sent_value, + ", call_count:", method_call_return.call_index, + ", consumer_id:", method_call_return.consumer_id, + ", proxy_id:", method_call_return.proxy_id, + "} Expected={sent_value:", input_value, + ", call_count:", call_idx, + ", consumer_id:", consumer_id, + ", proxy_id:", proxy_id, + "}"); + // clang-format on + } +} + +void CallMethod(DuplicateSignatureProxy& proxy, + CopyMode copy_mode, + std::size_t proxy_id, + std::size_t method_id, + std::size_t consumer_id, + std::size_t call_idx, + std::int32_t input_value) +{ + // clang-format off + PrintLine("Consumer process ", consumer_id, ", Proxy ", proxy_id, ": Calling a method", method_id, + " with the value ", call_idx, ". Call count = ", call_idx, '\n'); + // clang-format on + + auto& method = [&proxy, proxy_id, 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 process ", consumer_id, ", Proxy ", proxy_id, ": Invalid method ID ", method_id, " cannot be larger than ", kNumRegisteredMethods); + // clang-format on + + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(false); + }(); + + auto method_call_result = [&method, copy_mode, call_idx, consumer_id, proxy_id, input_value]() + -> score::Result> { + if (copy_mode == CopyMode::ZERO_COPY) + { + auto allocated_args_result = method.Allocate(); + if (!allocated_args_result.has_value()) + { + // clang-format off + FailTest("Consumer process ", consumer_id, ", Proxy ", proxy_id, ": Failure during zero-copy call. ", allocated_args_result.error(), '\n'); + // clang-format on + } + + auto& [arg_ptr, call_index_ptr, consumer_id_ptr, proxy_id_ptr] = allocated_args_result.value(); + *arg_ptr = input_value; + *call_index_ptr = call_idx; + *consumer_id_ptr = consumer_id; + *proxy_id_ptr = proxy_id; + + return method( + std::move(arg_ptr), std::move(call_index_ptr), std::move(consumer_id_ptr), std::move(proxy_id_ptr)); + } + + return method(input_value, call_idx, consumer_id, proxy_id); + }(); + + ValidateMethodCallResult(method_call_result, proxy_id, consumer_id, method_id, call_idx, input_value); +} + +} // 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 + PrintLine("Consumer process ", consumer_id, ", Proxy ", proxy_id, ": Step 1 - Create proxy\n"); + const auto instance_specifier = + InstanceSpecifier::Create(std::string{"multiple_proxies/MultiMethodConsumer"} + std::to_string(consumer_id)) + .value(); + proxy_container.CreateProxy(instance_specifier, "multiple_proxies"); + auto& proxy = proxy_container.GetProxy(); + + // Step 2. Call method with InArgs and Return with copy + PrintLine("Consumer process ", consumer_id, ", Proxy ", proxy_id, ": Step 2 - Call methods\n"); + std::array num_method_calls_per_method{}; + num_method_calls_per_method.fill(0U); + 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; + + num_method_calls_per_method.at(method_id)++; + CallMethod(proxy, + copy_mode, + proxy_id, + method_id, + consumer_id, + num_method_calls_per_method.at(method_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); + } + } + + PrintLine("Consumer process ", consumer_id, ": All threads have completed. Exiting.\n"); +} + +} // 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/multiple_proxies/duplicate_signatures_datatype.cpp b/score/mw/com/test/methods/multiple_proxies/duplicate_signatures_datatype.cpp new file mode 100644 index 000000000..c684c7abb --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/duplicate_signatures_datatype.cpp @@ -0,0 +1,13 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/com/test/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..44f195eb1 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/duplicate_signatures_datatype.h @@ -0,0 +1,61 @@ +/******************************************************************************** + * 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 +#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 InputArgType = std::int32_t; + using CallIndexType = std::size_t; + using ConsumerIdType = std::size_t; + using ProxyIdType = std::size_t; + + struct ReturnType + { + InputArgType sent_value; + CallIndexType call_index; + ConsumerIdType consumer_id; + ProxyIdType proxy_id; + }; + + using MethodType = ReturnType(InputArgType, CallIndexType, ConsumerIdType, ProxyIdType); + + 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..13319d88b --- /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 = 2 + + +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=120, **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=120, **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..907a8c0cd --- /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 = 2 + + +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, wait_timeout=120, **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..a3c317578 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/main_combined_consumer_provider.cpp @@ -0,0 +1,122 @@ +/******************************************************************************** + * 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)}; + 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(), + test_configuration.num_consumers, + test_configuration.num_proxies_per_process, + test_configuration.num_method_calls_per_proxy); + + 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..ed2c486fb --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/main_provider.cpp @@ -0,0 +1,104 @@ +/******************************************************************************** + * 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); + } + + 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(), + test_configuration.num_consumers, + test_configuration.num_proxies_per_process, + test_configuration.num_method_calls_per_proxy); + 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..c80c107f0 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/provider.cpp @@ -0,0 +1,210 @@ +/******************************************************************************** + * 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/common_test_resources/skeleton_container.h" +#include "score/mw/com/test/methods/methods_test_resources/process_synchronizer.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 +#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(); + +using InputArgType = DuplicateSignatureSkeleton::InputArgType; +using CallCountType = DuplicateSignatureSkeleton::CallIndexType; +using ConsumerIdType = DuplicateSignatureSkeleton::ConsumerIdType; +using ProxyIdType = DuplicateSignatureSkeleton::ProxyIdType; +using ReturnType = DuplicateSignatureSkeleton::ReturnType; + +class MethodCallCounter +{ + public: + MethodCallCounter(std::size_t num_consumers, std::size_t num_proxies_per_consumer) : counts_(num_consumers) + { + for (auto& method_call_counts_per_proxy : counts_) + { + method_call_counts_per_proxy.reserve(num_proxies_per_consumer); + for (std::size_t proxy_id{0U}; proxy_id < num_proxies_per_consumer; ++proxy_id) + { + method_call_counts_per_proxy.emplace_back(std::make_unique()); + } + } + } + + std::size_t Increment(std::size_t consumer_id, std::size_t proxy_id, std::size_t method_id) + { + auto& method_count = counts_.at(consumer_id).at(proxy_id)->at(method_id); + return method_count.fetch_add(1U) + 1U; + } + + std::size_t Get(std::size_t consumer_id, std::size_t proxy_id, std::size_t method_id) const + { + return counts_.at(consumer_id).at(proxy_id)->at(method_id).load(); + } + + private: + using MethodCallCountsPerMethod = std::array, kNumRegisteredMethods>; + using MethodCallCountsPerProxy = std::vector>; + using MethodCallCountsPerConsumer = std::vector; + + MethodCallCountsPerConsumer counts_; +}; + +auto HandlerMaker(MethodCallCounter& method_call_counter, std::size_t method_idx) +{ + return [method_idx, &method_call_counter](ReturnType& return_value, + const InputArgType& sent_value, + const CallCountType& call_count, + const ConsumerIdType& consumer_id, + const ProxyIdType& proxy_id) { + const auto count = method_call_counter.Increment(consumer_id, proxy_id, method_idx); + + if (count != call_count) + { + FailTest("Provider: Call count argument does not match expected value. Expected: ", + count, + ", Actual: ", + call_count, + " for Consumer ", + consumer_id, + ", Proxy ", + proxy_id, + ", method", + method_idx, + '\n'); + } + + // 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)); + + // clang-format off + PrintLine("Provider: Consumer process ", consumer_id, ", Proxy ", proxy_id, ", method", method_idx, + " called (skel count=", count, ") with val=", sent_value, " and proxy count=", call_count, '\n'); + // clang-format on + + return_value = ReturnType{sent_value, call_count, consumer_id, proxy_id}; + }; +} + +} // namespace + +void run_provider(const score::cpp::stop_token& stop_token, + std::size_t num_consumers, + std::size_t num_proxies_per_consumer, + std::size_t num_method_calls_per_proxy) +{ + MethodCallCounter method_call_counter{num_consumers, num_proxies_per_consumer}; + + std::vector> process_synchronizers; + for (std::size_t i{0}; i < num_consumers; ++i) + { + process_synchronizers.emplace_back( + ProcessSynchronizer::CreateUniquePtr(CreateInterprocessNotificationShmPath(i))); + } + + 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 = [&method_call_counter](auto& method, std::size_t method_idx) { + const auto registration_result = method.RegisterHandler(HandlerMaker(method_call_counter, method_idx)); + if (!registration_result.has_value()) + { + 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) + { + PrintLine("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 << "\nProvider: Step 5. Verify the number of method calls are correct" << std::endl; + bool test_failed{false}; + for (std::size_t consumer_id{0U}; consumer_id < num_consumers; ++consumer_id) + { + const auto& enabled_methods_for_consumer = kEnabledMethodsPerProxy.at(consumer_id); + for (std::size_t proxy_id{0U}; proxy_id < num_proxies_per_consumer; ++proxy_id) + { + for (std::size_t method_id{0U}; method_id < kNumRegisteredMethods; ++method_id) + { + const auto is_method_enabled = + std::find(enabled_methods_for_consumer.cbegin(), enabled_methods_for_consumer.cend(), method_id) != + enabled_methods_for_consumer.cend(); + if (is_method_enabled) + { + const auto actual_method_call_count = method_call_counter.Get(consumer_id, proxy_id, method_id); + + const auto expected_method_calls_for_bucket = is_method_enabled ? num_method_calls_per_proxy : 0U; + + // clang-format off + PrintLine("Provider: Consumer process ", consumer_id, ", Proxy ", proxy_id, ", method", method_id, + " -> Expected: ", expected_method_calls_for_bucket, ", Actual: ", actual_method_call_count, '\n'); + // clang-format on + if (expected_method_calls_for_bucket != actual_method_call_count) + { + test_failed = true; + } + } + } + } + } + + if (test_failed) + { + FailTest("Provider: Method call counts did not match expected values"); + } + + 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..32aa758f8 --- /dev/null +++ b/score/mw/com/test/methods/multiple_proxies/provider.h @@ -0,0 +1,28 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#ifndef SCORE_MW_COM_TEST_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, + std::size_t num_consumers, + std::size_t num_proxies_per_consumer, + std::size_t num_method_calls_per_proxy); + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_METHODS_MULTIPLE_PROXIES_PROVIDER_H