diff --git a/score/mw/com/test/generic_skeleton/BUILD.bazel b/score/mw/com/test/generic_skeleton/BUILD.bazel new file mode 100644 index 000000000..efeae5957 --- /dev/null +++ b/score/mw/com/test/generic_skeleton/BUILD.bazel @@ -0,0 +1,127 @@ +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 + +load("@rules_cc//cc:defs.bzl", "cc_binary") +load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") +load("//score/mw/com/test:pkg_application.bzl", "pkg_application") + +package(default_visibility = ["//visibility:public"]) + +cc_binary( + name = "generic_typed_interaction_64_byte_app", + srcs = ["generic_typed_interaction_64_byte_app.cpp"], + features = COMPILER_WARNING_FEATURES, + deps = [ + "//score/mw/com", + "//score/mw/com/test/common_test_resources:sample_sender_receiver", + "//score/mw/com/test/common_test_resources:sctf_test_runner", + ], +) + +cc_binary( + name = "generic_typed_interaction_16_byte_app", + srcs = ["generic_typed_interaction_16_byte_app.cpp"], + features = COMPILER_WARNING_FEATURES, + deps = [ + "//score/mw/com", + "//score/mw/com/test/common_test_resources:sample_sender_receiver", + "//score/mw/com/test/common_test_resources:sctf_test_runner", + ], +) + +cc_binary( + name = "generic_typed_interaction_8_byte_app", + srcs = ["generic_typed_interaction_8_byte_app.cpp"], + features = COMPILER_WARNING_FEATURES, + deps = [ + "//score/mw/com", + "//score/mw/com/test/common_test_resources:sample_sender_receiver", + "//score/mw/com/test/common_test_resources:sctf_test_runner", + ], +) + +cc_binary( + name = "generic_typed_interaction_32_byte_app", + srcs = ["generic_typed_interaction_32_byte_app.cpp"], + features = COMPILER_WARNING_FEATURES, + deps = [ + "//score/mw/com", + "//score/mw/com/test/common_test_resources:sample_sender_receiver", + "//score/mw/com/test/common_test_resources:sctf_test_runner", + ], +) + +pkg_application( + name = "generic_typed_interaction_app-pkg", + app_name = "generic_typed_interaction_app", + bin = [ + ":generic_typed_interaction_64_byte_app", + ":generic_typed_interaction_16_byte_app", + ":generic_typed_interaction_8_byte_app", + ":generic_typed_interaction_32_byte_app", + ], + etc = [ + "mw_com_config.json", + "logging.json", + ], + visibility = ["//score/mw/com/test/generic_skeleton:__subpackages__"], +) + +cc_binary( + name = "generic_generic_interaction_64_byte_app", + srcs = ["generic_generic_interaction_app.cpp"], + defines = ["PAYLOAD_SIZE=64"], + features = COMPILER_WARNING_FEATURES, + deps = [ + "//score/mw/com", + "//score/mw/com/test/common_test_resources:sample_sender_receiver", + "//score/mw/com/test/common_test_resources:sctf_test_runner", + ], +) + +cc_binary( + name = "generic_generic_interaction_32_byte_app", + srcs = ["generic_generic_interaction_app.cpp"], + defines = ["PAYLOAD_SIZE=32"], + features = COMPILER_WARNING_FEATURES, + deps = [ + "//score/mw/com", + "//score/mw/com/test/common_test_resources:sample_sender_receiver", + "//score/mw/com/test/common_test_resources:sctf_test_runner", + ], +) + +cc_binary( + name = "generic_generic_interaction_8_byte_app", + srcs = ["generic_generic_interaction_app.cpp"], + defines = ["PAYLOAD_SIZE=8"], + features = COMPILER_WARNING_FEATURES, + deps = [ + "//score/mw/com", + "//score/mw/com/test/common_test_resources:sample_sender_receiver", + "//score/mw/com/test/common_test_resources:sctf_test_runner", + ], +) + +pkg_application( + name = "generic_generic_interaction_app-pkg", + app_name = "generic_generic_interaction_app", + bin = [ + ":generic_generic_interaction_64_byte_app", + ":generic_generic_interaction_32_byte_app", + ":generic_generic_interaction_8_byte_app", + ], + etc = [ + "mw_com_config_generic_generic.json", + "logging.json", + ], + visibility = ["//score/mw/com/test/generic_skeleton:__subpackages__"], +) diff --git a/score/mw/com/test/generic_skeleton/generic_generic_interaction_app.cpp b/score/mw/com/test/generic_skeleton/generic_generic_interaction_app.cpp new file mode 100644 index 000000000..a5b09da0f --- /dev/null +++ b/score/mw/com/test/generic_skeleton/generic_generic_interaction_app.cpp @@ -0,0 +1,204 @@ +#include "score/mw/com/impl/generic_proxy.h" +#include "score/mw/com/impl/generic_skeleton.h" +#include "score/mw/com/impl/instance_specifier.h" +#include "score/mw/com/runtime.h" +#include "score/mw/com/runtime_configuration.h" +#include "score/mw/log/logging.h" + +#include +#include +#include +#include +#include +#include +#include + +// Default to 64-byte if not specified by the build system +#ifndef PAYLOAD_SIZE +#define PAYLOAD_SIZE 64 +#endif + +namespace +{ + +struct MyEventData +{ + uint64_t counter; +#if PAYLOAD_SIZE > 8 + char padding[PAYLOAD_SIZE - 8]; +#endif +}; + +constexpr std::string_view kInstanceSpecifier = "/test/generic/generic/interaction"; +constexpr int kSamplesToProcess = 30; +constexpr int kSamplesToSubscribe = 5; + +#if PAYLOAD_SIZE == 64 +constexpr std::string_view kEventName = "Event64Byte"; +#elif PAYLOAD_SIZE == 32 +constexpr std::string_view kEventName = "Event32Byte"; +#elif PAYLOAD_SIZE == 8 +constexpr std::string_view kEventName = "Event8Byte"; +#else +#error "Unsupported payload size configured." +#endif + +int run_provider() +{ + const auto instance_specifier = + score::mw::com::impl::InstanceSpecifier::Create(kInstanceSpecifier).value(); + std::cout << "[PROVIDER] Instance specifier created." << std::endl; + const score::mw::com::impl::DataTypeMetaInfo meta{sizeof(MyEventData), alignof(MyEventData)}; + std::cout << "[PROVIDER] DataTypeMetaInfo created (size=" << sizeof(MyEventData) + << ", align=" << alignof(MyEventData) << ")." << std::endl; + const std::vector events = {{kEventName, meta}}; + std::cout << "[PROVIDER] EventInfo vector created for event: " << kEventName << std::endl; + + score::mw::com::impl::GenericSkeletonServiceElementInfo create_params; + create_params.events = events; + std::cout << "[PROVIDER] GenericSkeletonServiceElementInfo prepared." << std::endl; + + std::cout << "[PROVIDER] Calling GenericSkeleton::Create..." << std::endl; + auto skeleton_res = score::mw::com::impl::GenericSkeleton::Create(instance_specifier, create_params); + if (!skeleton_res.has_value()) + { + std::cerr << "[PROVIDER] GenericSkeleton::Create FAILED." << std::endl; + return 1; + } + auto& skeleton = skeleton_res.value(); + std::cout << "[PROVIDER] GenericSkeleton created." << std::endl; + + std::cout << "[PROVIDER] Calling skeleton.OfferService()..." << std::endl; + if (!skeleton.OfferService().has_value()) + { + std::cerr << "[PROVIDER] OfferService FAILED." << std::endl; + return 1; + } + std::cout << "[PROVIDER] OfferService SUCCEEDED." << std::endl; + + std::cout << "[PROVIDER] Getting event reference for " << kEventName << "..." << std::endl; + auto it = skeleton.GetEvents().find(kEventName); + if (it == skeleton.GetEvents().cend()) + { + std::cerr << "[PROVIDER] Could not find event: " << kEventName << std::endl; + return 1; + } + std::cout << "[PROVIDER] Event reference obtained." << std::endl; + + // Get reference to the GenericSkeletonEvent + auto& generic_event = const_cast(it->second); + + std::cout << "[PROVIDER] Generic-Generic " << PAYLOAD_SIZE << "-byte - Waiting 5s for consumer to subscribe..." + << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(5)); + std::cout << "[PROVIDER] Finished initial 5s sleep." << std::endl; + + for (int i = 0; i < kSamplesToProcess; ++i) + { + auto sample_res = generic_event.Allocate(); + if (!sample_res.has_value()) + { + std::cerr << "[PROVIDER] Allocation failed for sample: " << i << std::endl; + return 1; + } + std::cout << "[PROVIDER] Sample " << i << " allocated." << std::endl; + + auto* typed_sample = static_cast(sample_res.value().Get()); + typed_sample->counter = i; + + std::cout << "[PROVIDER] Sending sample: " << i << std::endl; + generic_event.Send(std::move(sample_res.value())); + std::cout << "[PROVIDER] " << PAYLOAD_SIZE << "-byte Event Sent sample: " << i << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + std::cout << "[PROVIDER] All samples sent." << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(15)); + std::cout << "[PROVIDER] Finished post-send 15s sleep. Calling StopOfferService()..." << std::endl; + skeleton.StopOfferService(); + std::cout << "[PROVIDER] StopOfferService() completed." << std::endl; + return 0; +} + +int run_consumer() +{ + const auto instance_specifier = + score::mw::com::impl::InstanceSpecifier::Create(kInstanceSpecifier).value(); + + score::Result> handles_res; + int retries = 0; + while (retries < 50) + { + handles_res = score::mw::com::impl::GenericProxy::FindService(instance_specifier); + if (handles_res.has_value() && !handles_res.value().empty()) + break; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + retries++; + } + if (!handles_res.has_value() || handles_res.value().empty()) + return 1; + + auto proxy_res = score::mw::com::impl::GenericProxy::Create(handles_res.value()[0]); + if (!proxy_res.has_value()) + return 1; + auto& proxy = proxy_res.value(); + + auto event_it = proxy.GetEvents().find(kEventName); + if (event_it == proxy.GetEvents().cend()) + return 1; + + // Get reference to the GenericProxyEvent + auto& generic_event = event_it->second; + generic_event.Subscribe(kSamplesToSubscribe); + + uint64_t expected = 0; + uint64_t received = 0; + int data_mismatches = 0; + + while (received < kSamplesToProcess) + { + // std::cout << "[CONSUMER] " << PAYLOAD_SIZE << "-byte Waking up, calling GetNewSamples..." << std::endl; + + // The receiver callback operates on type-erased memory (SamplePtr) + generic_event.GetNewSamples( + [&](auto sample) { + auto* typed_sample = static_cast(sample.get()); + if (typed_sample->counter != expected) + { + std::cerr << "[CONSUMER] " << PAYLOAD_SIZE << "-byte Data mismatch! Expected: " << expected + << ", got: " << typed_sample->counter << std::endl; + data_mismatches++; + } + else + { + std::cout << "[CONSUMER] " << PAYLOAD_SIZE + << "-byte Event Received sample: " << typed_sample->counter << std::endl; + } + expected++; + received++; + }, + kSamplesToSubscribe); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + if (data_mismatches > 0) + { + std::cerr << "[CONSUMER] Test failed with " << data_mismatches << " mismatches." << std::endl; + return 1; + } + return 0; +} +} // namespace + +int main(int argc, const char* argv[]) +{ + std::string mode; + for (int i = 1; i < argc; ++i) + if (std::string(argv[i]) == "--mode" && i + 1 < argc) + mode = argv[++i]; + score::mw::com::runtime::InitializeRuntime(score::mw::com::runtime::RuntimeConfiguration(argc, argv)); + if (mode == "provider") + return run_provider(); + if (mode == "consumer") + return run_consumer(); + return 1; +} diff --git a/score/mw/com/test/generic_skeleton/generic_typed_interaction_16_byte_app.cpp b/score/mw/com/test/generic_skeleton/generic_typed_interaction_16_byte_app.cpp new file mode 100644 index 000000000..ca10ec485 --- /dev/null +++ b/score/mw/com/test/generic_skeleton/generic_typed_interaction_16_byte_app.cpp @@ -0,0 +1,150 @@ +#include "score/mw/com/impl/generic_skeleton.h" +#include "score/mw/com/impl/instance_specifier.h" +#include "score/mw/com/impl/proxy_event.h" +#include "score/mw/com/impl/traits.h" +#include "score/mw/com/runtime.h" +#include "score/mw/com/runtime_configuration.h" +#include "score/mw/log/logging.h" + +#include +#include +#include +#include +#include +#include + +namespace +{ + +struct MyEventData +{ + uint64_t counter; + char padding[8]; +}; + +constexpr std::string_view kInstanceSpecifier = "/test/generic/typed/interaction"; +constexpr int kSamplesToProcess = 30; +constexpr int kSamplesToSubscribe = 5; +constexpr std::string_view kEventName = "Event16Byte"; + +int run_provider() +{ + const auto instance_specifier = + score::mw::com::impl::InstanceSpecifier::Create(kInstanceSpecifier).value(); + const score::mw::com::impl::DataTypeMetaInfo meta{sizeof(MyEventData), alignof(MyEventData)}; + const std::vector events = {{kEventName, meta}}; + + score::mw::com::impl::GenericSkeletonServiceElementInfo create_params; + create_params.events = events; + + auto skeleton_res = score::mw::com::impl::GenericSkeleton::Create(instance_specifier, create_params); + if (!skeleton_res.has_value()) + return 1; + auto& skeleton = skeleton_res.value(); + + if (!skeleton.OfferService().has_value()) + return 1; + + auto it = skeleton.GetEvents().find(kEventName); + if (it == skeleton.GetEvents().cend()) + return 1; + auto& generic_event = const_cast(it->second); + + // Wait for the consumer to start and subscribe BEFORE sending data + std::cout << "[PROVIDER] 16-byte - Waiting 5s for consumer to subscribe..." << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(5)); + + for (int i = 0; i < kSamplesToProcess; ++i) + { + auto sample_res = generic_event.Allocate(); + if (!sample_res.has_value()) + return 1; + auto* typed_sample = static_cast(sample_res.value().Get()); + typed_sample->counter = i; + generic_event.Send(std::move(sample_res.value())); + std::cout << "[PROVIDER] 16-byte Event Sent sample: " << i << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + std::this_thread::sleep_for(std::chrono::seconds(15)); + skeleton.StopOfferService(); + return 0; +} + +template +class MyTestService : public Trait::Base +{ + public: + using Trait::Base::Base; + typename Trait::template Event event_{*this, kEventName}; +}; +using MyTestServiceProxy = score::mw::com::impl::AsProxy; + +int run_consumer() +{ + const auto instance_specifier = + score::mw::com::impl::InstanceSpecifier::Create(kInstanceSpecifier).value(); + score::Result> handles_res; + int retries = 0; + while (retries < 50) + { + handles_res = MyTestServiceProxy::FindService(instance_specifier); + if (handles_res.has_value() && !handles_res.value().empty()) + break; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + retries++; + } + if (!handles_res.has_value() || handles_res.value().empty()) + return 1; + + auto proxy_res = MyTestServiceProxy::Create(handles_res.value()[0]); + if (!proxy_res.has_value()) + return 1; + auto& proxy = proxy_res.value(); + + uint64_t received = 0; + uint64_t expected = 0; + int data_mismatches = 0; + proxy.event_.Subscribe(kSamplesToSubscribe); + + while (received < kSamplesToProcess) + { + proxy.event_.GetNewSamples( + [&](score::mw::com::SamplePtr sample) { + if (sample->counter != expected) + { + score::mw::log::LogError("TypedProxyConsumer") + << "16-byte Data mismatch! Expected: " << expected << ", got: " << sample->counter; + data_mismatches++; + } + else + { + std::cout << "[CONSUMER] 16-byte Event Received sample: " << sample->counter << std::endl; + } + expected++; + received++; + }, + kSamplesToSubscribe); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + if (data_mismatches > 0) + { + score::mw::log::LogError("TypedProxyConsumer") << "Test failed with " << data_mismatches << " mismatches."; + return 1; + } + return 0; +} +} // namespace +int main(int argc, const char* argv[]) +{ + std::string mode; + for (int i = 1; i < argc; ++i) + if (std::string(argv[i]) == "--mode" && i + 1 < argc) + mode = argv[++i]; + score::mw::com::runtime::InitializeRuntime(score::mw::com::runtime::RuntimeConfiguration(argc, argv)); + if (mode == "provider") + return run_provider(); + if (mode == "consumer") + return run_consumer(); + return 1; +} diff --git a/score/mw/com/test/generic_skeleton/generic_typed_interaction_32_byte_app.cpp b/score/mw/com/test/generic_skeleton/generic_typed_interaction_32_byte_app.cpp new file mode 100644 index 000000000..c680a2d71 --- /dev/null +++ b/score/mw/com/test/generic_skeleton/generic_typed_interaction_32_byte_app.cpp @@ -0,0 +1,150 @@ +#include "score/mw/com/impl/generic_skeleton.h" +#include "score/mw/com/impl/instance_specifier.h" +#include "score/mw/com/impl/proxy_event.h" +#include "score/mw/com/impl/traits.h" +#include "score/mw/com/runtime.h" +#include "score/mw/com/runtime_configuration.h" +#include "score/mw/log/logging.h" + +#include +#include +#include +#include +#include +#include + +namespace +{ + +struct MyEventData +{ + uint64_t counter; + char padding[24]; +}; + +constexpr std::string_view kInstanceSpecifier = "/test/generic/typed/interaction"; +constexpr int kSamplesToProcess = 60; +constexpr int kSamplesToSubscribe = 5; +constexpr std::string_view kEventName = "Event32Byte"; + +int run_provider() +{ + const auto instance_specifier = + score::mw::com::impl::InstanceSpecifier::Create(kInstanceSpecifier).value(); + const score::mw::com::impl::DataTypeMetaInfo meta{sizeof(MyEventData), alignof(MyEventData)}; + const std::vector events = {{kEventName, meta}}; + + score::mw::com::impl::GenericSkeletonServiceElementInfo create_params; + create_params.events = events; + + auto skeleton_res = score::mw::com::impl::GenericSkeleton::Create(instance_specifier, create_params); + if (!skeleton_res.has_value()) + return 1; + auto& skeleton = skeleton_res.value(); + + if (!skeleton.OfferService().has_value()) + return 1; + + auto it = skeleton.GetEvents().find(kEventName); + if (it == skeleton.GetEvents().cend()) + return 1; + auto& generic_event = const_cast(it->second); + + // Wait for the consumer to start and subscribe BEFORE sending data + std::cout << "[PROVIDER] 32-byte - Waiting 5s for consumer to subscribe..." << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(5)); + + for (int i = 30; i < kSamplesToProcess; ++i) + { + auto sample_res = generic_event.Allocate(); + if (!sample_res.has_value()) + return 1; + auto* typed_sample = static_cast(sample_res.value().Get()); + typed_sample->counter = i; + generic_event.Send(std::move(sample_res.value())); + std::cout << "[PROVIDER] 32-byte Event Sent sample: " << i << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + std::this_thread::sleep_for(std::chrono::seconds(15)); + skeleton.StopOfferService(); + return 0; +} + +template +class MyTestService : public Trait::Base +{ + public: + using Trait::Base::Base; + typename Trait::template Event event_{*this, kEventName}; +}; +using MyTestServiceProxy = score::mw::com::impl::AsProxy; + +int run_consumer() +{ + const auto instance_specifier = + score::mw::com::impl::InstanceSpecifier::Create(kInstanceSpecifier).value(); + score::Result> handles_res; + int retries = 0; + while (retries < 50) + { + handles_res = MyTestServiceProxy::FindService(instance_specifier); + if (handles_res.has_value() && !handles_res.value().empty()) + break; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + retries++; + } + if (!handles_res.has_value() || handles_res.value().empty()) + return 1; + + auto proxy_res = MyTestServiceProxy::Create(handles_res.value()[0]); + if (!proxy_res.has_value()) + return 1; + auto& proxy = proxy_res.value(); + + uint64_t received = 30; + uint64_t expected = 30; + int data_mismatches = 0; + proxy.event_.Subscribe(kSamplesToSubscribe); + + while (received < kSamplesToProcess) + { + proxy.event_.GetNewSamples( + [&](score::mw::com::SamplePtr sample) { + if (sample->counter != expected) + { + score::mw::log::LogError("TypedProxyConsumer") + << "32-byte Data mismatch! Expected: " << expected << ", got: " << sample->counter; + data_mismatches++; + } + else + { + std::cout << "[CONSUMER] 32-byte Event Received sample: " << sample->counter << std::endl; + } + expected++; + received++; + }, + kSamplesToSubscribe); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + if (data_mismatches > 0) + { + score::mw::log::LogError("TypedProxyConsumer") << "Test failed with " << data_mismatches << " mismatches."; + return 1; + } + return 0; +} +} // namespace +int main(int argc, const char* argv[]) +{ + std::string mode; + for (int i = 1; i < argc; ++i) + if (std::string(argv[i]) == "--mode" && i + 1 < argc) + mode = argv[++i]; + score::mw::com::runtime::InitializeRuntime(score::mw::com::runtime::RuntimeConfiguration(argc, argv)); + if (mode == "provider") + return run_provider(); + if (mode == "consumer") + return run_consumer(); + return 1; +} diff --git a/score/mw/com/test/generic_skeleton/generic_typed_interaction_64_byte_app.cpp b/score/mw/com/test/generic_skeleton/generic_typed_interaction_64_byte_app.cpp new file mode 100644 index 000000000..a0abd492b --- /dev/null +++ b/score/mw/com/test/generic_skeleton/generic_typed_interaction_64_byte_app.cpp @@ -0,0 +1,150 @@ +#include "score/mw/com/impl/generic_skeleton.h" +#include "score/mw/com/impl/instance_specifier.h" +#include "score/mw/com/impl/proxy_event.h" +#include "score/mw/com/impl/traits.h" +#include "score/mw/com/runtime.h" +#include "score/mw/com/runtime_configuration.h" +#include "score/mw/log/logging.h" + +#include +#include +#include +#include +#include +#include + +namespace +{ + +struct MyEventData +{ + uint64_t counter; + char padding[56]; +}; + +constexpr std::string_view kInstanceSpecifier = "/test/generic/typed/interaction"; +constexpr int kSamplesToProcess = 30; +constexpr int kSamplesToSubscribe = 5; +constexpr std::string_view kEventName = "Event64Byte"; + +int run_provider() +{ + const auto instance_specifier = + score::mw::com::impl::InstanceSpecifier::Create(kInstanceSpecifier).value(); + const score::mw::com::impl::DataTypeMetaInfo meta{sizeof(MyEventData), alignof(MyEventData)}; + const std::vector events = {{kEventName, meta}}; + + score::mw::com::impl::GenericSkeletonServiceElementInfo create_params; + create_params.events = events; + + auto skeleton_res = score::mw::com::impl::GenericSkeleton::Create(instance_specifier, create_params); + if (!skeleton_res.has_value()) + return 1; + auto& skeleton = skeleton_res.value(); + + if (!skeleton.OfferService().has_value()) + return 1; + + auto it = skeleton.GetEvents().find(kEventName); + if (it == skeleton.GetEvents().cend()) + return 1; + auto& generic_event = const_cast(it->second); + + // Wait for the consumer to start and subscribe BEFORE sending data + std::cout << "[PROVIDER] 64-byte - Waiting 5s for consumer to subscribe..." << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(5)); + + for (int i = 0; i < kSamplesToProcess; ++i) + { + auto sample_res = generic_event.Allocate(); + if (!sample_res.has_value()) + return 1; + auto* typed_sample = static_cast(sample_res.value().Get()); + typed_sample->counter = i; + generic_event.Send(std::move(sample_res.value())); + std::cout << "[PROVIDER] 64-byte Event Sent sample: " << i << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + std::this_thread::sleep_for(std::chrono::seconds(15)); + skeleton.StopOfferService(); + return 0; +} + +template +class MyTestService : public Trait::Base +{ + public: + using Trait::Base::Base; + typename Trait::template Event event_{*this, kEventName}; +}; +using MyTestServiceProxy = score::mw::com::impl::AsProxy; + +int run_consumer() +{ + const auto instance_specifier = + score::mw::com::impl::InstanceSpecifier::Create(kInstanceSpecifier).value(); + score::Result> handles_res; + int retries = 0; + while (retries < 50) + { + handles_res = MyTestServiceProxy::FindService(instance_specifier); + if (handles_res.has_value() && !handles_res.value().empty()) + break; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + retries++; + } + if (!handles_res.has_value() || handles_res.value().empty()) + return 1; + + auto proxy_res = MyTestServiceProxy::Create(handles_res.value()[0]); + if (!proxy_res.has_value()) + return 1; + auto& proxy = proxy_res.value(); + + uint64_t received = 0; + uint64_t expected = 0; + int data_mismatches = 0; + proxy.event_.Subscribe(kSamplesToSubscribe); + + while (received < kSamplesToProcess) + { + proxy.event_.GetNewSamples( + [&](score::mw::com::SamplePtr sample) { + if (sample->counter != expected) + { + score::mw::log::LogError("TypedProxyConsumer") + << "64-byte Data mismatch! Expected: " << expected << ", got: " << sample->counter; + data_mismatches++; + } + else + { + std::cout << "[CONSUMER] 64-byte Event Received sample: " << sample->counter << std::endl; + } + expected++; + received++; + }, + kSamplesToSubscribe); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + if (data_mismatches > 0) + { + score::mw::log::LogError("TypedProxyConsumer") << "Test failed with " << data_mismatches << " mismatches."; + return 1; + } + return 0; +} +} // namespace +int main(int argc, const char* argv[]) +{ + std::string mode; + for (int i = 1; i < argc; ++i) + if (std::string(argv[i]) == "--mode" && i + 1 < argc) + mode = argv[++i]; + score::mw::com::runtime::InitializeRuntime(score::mw::com::runtime::RuntimeConfiguration(argc, argv)); + if (mode == "provider") + return run_provider(); + if (mode == "consumer") + return run_consumer(); + return 1; +} diff --git a/score/mw/com/test/generic_skeleton/generic_typed_interaction_8_byte_app.cpp b/score/mw/com/test/generic_skeleton/generic_typed_interaction_8_byte_app.cpp new file mode 100644 index 000000000..2f38eda5a --- /dev/null +++ b/score/mw/com/test/generic_skeleton/generic_typed_interaction_8_byte_app.cpp @@ -0,0 +1,150 @@ +#include "score/mw/com/impl/generic_skeleton.h" +#include "score/mw/com/impl/instance_specifier.h" +#include "score/mw/com/impl/proxy_event.h" +#include "score/mw/com/impl/traits.h" +#include "score/mw/com/runtime.h" +#include "score/mw/com/runtime_configuration.h" +#include "score/mw/log/logging.h" + +#include +#include +#include +#include +#include +#include + +namespace +{ + +struct MyEventData +{ + uint64_t counter; +}; + +constexpr std::string_view kInstanceSpecifier = "/test/generic/typed/interaction"; +constexpr int kSamplesToProcess = 30; +constexpr int kSamplesToSubscribe = 5; +constexpr std::string_view kEventName = "Event8Byte"; + +int run_provider() +{ + const auto instance_specifier = + score::mw::com::impl::InstanceSpecifier::Create(kInstanceSpecifier).value(); + const score::mw::com::impl::DataTypeMetaInfo meta{sizeof(MyEventData), alignof(MyEventData)}; + const std::vector events = {{kEventName, meta}}; + + score::mw::com::impl::GenericSkeletonServiceElementInfo create_params; + create_params.events = events; + + auto skeleton_res = score::mw::com::impl::GenericSkeleton::Create(instance_specifier, create_params); + if (!skeleton_res.has_value()) + return 1; + auto& skeleton = skeleton_res.value(); + + if (!skeleton.OfferService().has_value()) + return 1; + + // Uses transparent comparator if available, else safely creates a temporary for the find operation + auto it = skeleton.GetEvents().find(kEventName); + if (it == skeleton.GetEvents().cend()) + return 1; + auto& generic_event = const_cast(it->second); + + // Wait for the consumer to start and subscribe BEFORE sending data + std::cout << "[PROVIDER] 8-byte - Waiting 5s for consumer to subscribe..." << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(5)); + + for (int i = 0; i < kSamplesToProcess; ++i) + { + auto sample_res = generic_event.Allocate(); + if (!sample_res.has_value()) + return 1; + auto* typed_sample = static_cast(sample_res.value().Get()); + typed_sample->counter = i; + generic_event.Send(std::move(sample_res.value())); + std::cout << "[PROVIDER] 8-byte Event Sent sample: " << i << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + std::this_thread::sleep_for(std::chrono::seconds(15)); + skeleton.StopOfferService(); + return 0; +} + +template +class MyTestService : public Trait::Base +{ + public: + using Trait::Base::Base; + typename Trait::template Event event_{*this, kEventName}; +}; +using MyTestServiceProxy = score::mw::com::impl::AsProxy; + +int run_consumer() +{ + const auto instance_specifier = + score::mw::com::impl::InstanceSpecifier::Create(kInstanceSpecifier).value(); + score::Result> handles_res; + int retries = 0; + while (retries < 50) + { + handles_res = MyTestServiceProxy::FindService(instance_specifier); + if (handles_res.has_value() && !handles_res.value().empty()) + break; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + retries++; + } + if (!handles_res.has_value() || handles_res.value().empty()) + return 1; + + auto proxy_res = MyTestServiceProxy::Create(handles_res.value()[0]); + if (!proxy_res.has_value()) + return 1; + auto& proxy = proxy_res.value(); + + uint64_t received = 0; + uint64_t expected = 0; + int data_mismatches = 0; + proxy.event_.Subscribe(kSamplesToSubscribe); + + while (received < kSamplesToProcess) + { + proxy.event_.GetNewSamples( + [&](score::mw::com::SamplePtr sample) { + if (sample->counter != expected) + { + score::mw::log::LogError("TypedProxyConsumer") + << "8-byte Data mismatch! Expected: " << expected << ", got: " << sample->counter; + data_mismatches++; + } + else + { + std::cout << "[CONSUMER] 8-byte Event Received sample: " << sample->counter << std::endl; + } + expected++; + received++; + }, + kSamplesToSubscribe); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + if (data_mismatches > 0) + { + score::mw::log::LogError("TypedProxyConsumer") << "Test failed with " << data_mismatches << " mismatches."; + return 1; + } + return 0; +} +} // namespace +int main(int argc, const char* argv[]) +{ + std::string mode; + for (int i = 1; i < argc; ++i) + if (std::string(argv[i]) == "--mode" && i + 1 < argc) + mode = argv[++i]; + score::mw::com::runtime::InitializeRuntime(score::mw::com::runtime::RuntimeConfiguration(argc, argv)); + if (mode == "provider") + return run_provider(); + if (mode == "consumer") + return run_consumer(); + return 1; +} diff --git a/score/mw/com/test/generic_skeleton/generic_typed_interaction_app.cpp b/score/mw/com/test/generic_skeleton/generic_typed_interaction_app.cpp new file mode 100644 index 000000000..f0b3afd2b --- /dev/null +++ b/score/mw/com/test/generic_skeleton/generic_typed_interaction_app.cpp @@ -0,0 +1,251 @@ +/******************************************************************************** + * Copyright (c) 2025 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/impl/generic_skeleton.h" +#include "score/mw/com/impl/instance_specifier.h" +#include "score/mw/com/impl/proxy_event.h" +#include "score/mw/com/impl/traits.h" +#include "score/mw/com/runtime.h" +#include "score/mw/com/runtime_configuration.h" +#include "score/mw/log/logging.h" + +#include +#include +#include +#include +#include +#include + +namespace +{ + +struct MyEventData8Byte +{ + uint64_t counter; +}; + +struct MyEventData64Byte +{ + uint64_t counter; + char padding[56]; // Bypasses leftover capacity bugs, forcing stride validation +}; + +constexpr std::string_view kInstanceSpecifier = "/test/generic/typed/interaction"; +constexpr int kSamplesToProcess = 5; + +int run_provider() +{ + score::mw::log::LogInfo("GenericSkeletonProvider") << "Starting up."; + + const auto instance_specifier = + score::mw::com::impl::InstanceSpecifier::Create(std::string{kInstanceSpecifier}).value(); + + const score::mw::com::impl::DataTypeMetaInfo meta_8{sizeof(MyEventData8Byte), alignof(MyEventData8Byte)}; + const score::mw::com::impl::DataTypeMetaInfo meta_64{sizeof(MyEventData64Byte), alignof(MyEventData64Byte)}; + + const std::vector events = {{"Event8Byte", meta_8}, {"Event64Byte", meta_64}}; + + score::mw::com::impl::GenericSkeletonServiceElementInfo create_params; + create_params.events = events; + + auto skeleton_res = score::mw::com::impl::GenericSkeleton::Create(instance_specifier, create_params); + if (!skeleton_res.has_value()) + { + score::mw::log::LogFatal("GenericSkeletonProvider") << "Failed to create skeleton."; + return 1; + } + auto& skeleton = skeleton_res.value(); + + if (!skeleton.OfferService().has_value()) + { + score::mw::log::LogFatal("GenericSkeletonProvider") << "Failed to offer service."; + return 1; + } + score::mw::log::LogInfo("GenericSkeletonProvider") << "Service offered."; + + auto it_8 = skeleton.GetEvents().find("Event8Byte"); + auto it_64 = skeleton.GetEvents().find("Event64Byte"); + if (it_8 == skeleton.GetEvents().cend() || it_64 == skeleton.GetEvents().cend()) + { + score::mw::log::LogFatal("GenericSkeletonProvider") << "Failed to find events in skeleton."; + return 1; + } + + auto& generic_event_8 = const_cast(it_8->second); + auto& generic_event_64 = const_cast(it_64->second); + + for (int i = 0; i < kSamplesToProcess; ++i) + { + // Send 8-byte event + auto sample_res_8 = generic_event_8.Allocate(); + if (!sample_res_8.has_value()) + { + score::mw::log::LogFatal("GenericSkeletonProvider") << "Failed to allocate 8-byte sample."; + return 1; + } + auto* typed_sample_8 = static_cast(sample_res_8.value().Get()); + typed_sample_8->counter = i; + generic_event_8.Send(std::move(sample_res_8.value())); + + // Send 64-byte event + auto sample_res_64 = generic_event_64.Allocate(); + if (!sample_res_64.has_value()) + { + score::mw::log::LogFatal("GenericSkeletonProvider") << "Failed to allocate 64-byte sample."; + return 1; + } + auto* typed_sample_64 = static_cast(sample_res_64.value().Get()); + typed_sample_64->counter = i; + generic_event_64.Send(std::move(sample_res_64.value())); + + score::mw::log::LogInfo("GenericSkeletonProvider") << "Sent sample " << i; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + // Give the consumer ample time to connect, subscribe, and read the + // samples before we destroy the shared memory pool. + score::mw::log::LogInfo("GenericSkeletonProvider") << "Finished sending. Waiting for consumer to process..."; + std::this_thread::sleep_for(std::chrono::seconds(2)); + + skeleton.StopOfferService(); + score::mw::log::LogInfo("GenericSkeletonProvider") << "Shutting down."; + return 0; +} + +template +class MyTestService : public Trait::Base +{ + public: + using Trait::Base::Base; + + typename Trait::template Event event_8_byte_{*this, "Event8Byte"}; + typename Trait::template Event event_64_byte_{*this, "Event64Byte"}; +}; +using MyTestServiceProxy = score::mw::com::impl::AsProxy; + +int run_consumer() +{ + score::mw::log::LogInfo("TypedProxyConsumer") << "Starting up."; + + const auto instance_specifier = + score::mw::com::impl::InstanceSpecifier::Create(std::string{kInstanceSpecifier}).value(); + + score::Result> handles_res; + int retries = 0; + while (retries < 50) // Try for up to 5 seconds + { + handles_res = MyTestServiceProxy::FindService(instance_specifier); + if (handles_res.has_value() && !handles_res.value().empty()) + { + break; // Service found! + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + retries++; + } + + if (!handles_res.has_value() || handles_res.value().empty()) + { + score::mw::log::LogFatal("TypedProxyConsumer") << "Failed to find service after waiting."; + return 1; + } + + auto proxy_res = MyTestServiceProxy::Create(handles_res.value()[0]); + if (!proxy_res.has_value()) + { + score::mw::log::LogFatal("TypedProxyConsumer") << "Failed to create proxy."; + return 1; + } + auto& proxy = proxy_res.value(); + score::mw::log::LogInfo("TypedProxyConsumer") << "Proxy created."; + + uint64_t received_8 = 0; + uint64_t expected_8 = 0; + + uint64_t received_64 = 0; + uint64_t expected_64 = 0; + + proxy.event_8_byte_.Subscribe(kSamplesToProcess); + proxy.event_64_byte_.Subscribe(kSamplesToProcess); + + // Test the 64-byte event FIRST. This should completely pass without crashing. + while (received_64 < kSamplesToProcess) + { + proxy.event_64_byte_.GetNewSamples( + [&](score::mw::com::SamplePtr sample) { + score::mw::log::LogInfo("TypedProxyConsumer") << "Received 64-byte sample: " << sample->counter; + if (sample->counter != expected_64) + { + score::mw::log::LogFatal("TypedProxyConsumer") + << "64-byte data failed! Exp " << expected_64 << ", got " << sample->counter; + std::exit(1); + } + expected_64++; + received_64++; + }, + kSamplesToProcess); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + score::mw::log::LogInfo("TypedProxyConsumer") << "Successfully processed all 64-byte samples."; + + // Test the 8-byte event SECOND. This is where the old/buggy branch will crash. + while (received_8 < kSamplesToProcess) + { + proxy.event_8_byte_.GetNewSamples( + [&](score::mw::com::SamplePtr sample) { + score::mw::log::LogInfo("TypedProxyConsumer") << "Received 8-byte sample: " << sample->counter; + if (sample->counter != expected_8) + { + score::mw::log::LogFatal("TypedProxyConsumer") + << "8-byte data failed! Exp " << expected_8 << ", got " << sample->counter; + std::exit(1); + } + expected_8++; + received_8++; + }, + kSamplesToProcess); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + + score::mw::log::LogInfo("TypedProxyConsumer") << "Successfully received and validated all samples. Shutting down."; + return 0; +} + +} // namespace + +int main(int argc, const char* argv[]) +{ + std::string mode; + for (int i = 1; i < argc; ++i) + { + std::string arg = argv[i]; + if (arg == "--mode" && i + 1 < argc) + { + mode = argv[++i]; + } + } + + score::mw::com::runtime::InitializeRuntime(score::mw::com::runtime::RuntimeConfiguration(argc, argv)); + + if (mode == "provider") + { + return run_provider(); + } + else if (mode == "consumer") + { + return run_consumer(); + } + + score::mw::log::LogFatal("Main") << "Invalid or missing mode. Use --mode provider or --mode consumer."; + return 1; +} diff --git a/score/mw/com/test/generic_skeleton/integration_test/BUILD.bazel b/score/mw/com/test/generic_skeleton/integration_test/BUILD.bazel new file mode 100644 index 000000000..603fd5d0b --- /dev/null +++ b/score/mw/com/test/generic_skeleton/integration_test/BUILD.bazel @@ -0,0 +1,51 @@ +# Copyright (c) 2025 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:tar.bzl", "pkg_tar") +load("//quality/integration_testing:integration_testing.bzl", "integration_test") + +package(default_visibility = ["//visibility:public"]) + +pkg_tar( + name = "filesystem", + deps = [ + "//score/mw/com/test/generic_skeleton:generic_typed_interaction_app-pkg", + ], +) + +pkg_tar( + name = "generic_generic_filesystem", + deps = [ + "//score/mw/com/test/generic_skeleton:generic_generic_interaction_app-pkg", + ], +) + +integration_test( + name = "test_generic_typed_interaction", + timeout = "moderate", + srcs = ["test_generic_typed_interaction.py"], + filesystem = ":filesystem", +) + +integration_test( + name = "test_generic_generic_interaction", + timeout = "moderate", + srcs = ["test_generic_generic_interaction.py"], + filesystem = ":generic_generic_filesystem", +) + +test_suite( + name = "tests", + tests = [ + ":test_generic_generic_interaction", + ":test_generic_typed_interaction", + ], +) diff --git a/score/mw/com/test/generic_skeleton/integration_test/test_generic_generic_interaction.py b/score/mw/com/test/generic_skeleton/integration_test/test_generic_generic_interaction.py new file mode 100644 index 000000000..0c0fdba3a --- /dev/null +++ b/score/mw/com/test/generic_skeleton/integration_test/test_generic_generic_interaction.py @@ -0,0 +1,74 @@ +# Copyright (c) 2025 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 + +import time +import logging + +logger = logging.getLogger(__name__) + +def run_interaction_app(target, app_bin, mode, config_path, cwd, wait_on_exit=False, **kwargs): + """Helper to run an application using the framework's native wrap_exec method.""" + args = ["--mode", mode, "--service_instance_manifest", config_path] + return target.wrap_exec(app_bin, args, cwd=cwd, wait_on_exit=wait_on_exit, **kwargs) + +def test_generic_generic_interaction_64_byte(target): + """ + Tests data validation for a 64-byte payload where both the + provider and consumer are type-erased generic interfaces. + """ + app_root = "/opt/generic_generic_interaction_app/" + app_bin = "./bin/generic_generic_interaction_64_byte_app" + config_path = "./etc/mw_com_config_generic_generic.json" + + logger.info(f"Starting provider: {app_bin} in {app_root}") + # ADDED enforce_clean_shutdown=False and disabled LSAN here + with run_interaction_app(target, app_bin, "provider", config_path, cwd=app_root, enforce_clean_shutdown=False, env={"ASAN_OPTIONS": "detect_leaks=0"}): + time.sleep(2) # Give provider a moment to initialize + + logger.info(f"Starting consumer: {app_bin} in {app_root}") + with run_interaction_app(target, app_bin, "consumer", config_path, cwd=app_root, wait_on_exit=True, wait_timeout=60): + pass + +def test_generic_generic_interaction_32_byte(target): + """ + Tests data validation for a 32-byte payload where both the + provider and consumer are type-erased generic interfaces. + """ + app_root = "/opt/generic_generic_interaction_app/" + app_bin = "./bin/generic_generic_interaction_32_byte_app" + config_path = "./etc/mw_com_config_generic_generic.json" + + logger.info(f"Starting provider: {app_bin} in {app_root}") + # ADDED enforce_clean_shutdown=False and disabled LSAN here + with run_interaction_app(target, app_bin, "provider", config_path, cwd=app_root, enforce_clean_shutdown=False, env={"ASAN_OPTIONS": "detect_leaks=0"}): + time.sleep(2) # Give provider a moment to initialize + + logger.info(f"Starting consumer: {app_bin} in {app_root}") + with run_interaction_app(target, app_bin, "consumer", config_path, cwd=app_root, wait_on_exit=True, wait_timeout=60): + pass + +def test_generic_generic_interaction_8_byte(target): + """ + Tests data validation for an 8-byte payload where both the + provider and consumer are type-erased generic interfaces. + """ + app_root = "/opt/generic_generic_interaction_app/" + app_bin = "./bin/generic_generic_interaction_8_byte_app" + config_path = "./etc/mw_com_config_generic_generic.json" + + logger.info(f"Starting provider: {app_bin} in {app_root}") + # ADDED enforce_clean_shutdown=False and disabled LSAN here + with run_interaction_app(target, app_bin, "provider", config_path, cwd=app_root, enforce_clean_shutdown=False, env={"ASAN_OPTIONS": "detect_leaks=0"}): + time.sleep(2) # Give provider a moment to initialize + + logger.info(f"Starting consumer: {app_bin} in {app_root}") + with run_interaction_app(target, app_bin, "consumer", config_path, cwd=app_root, wait_on_exit=True, wait_timeout=60): + pass \ No newline at end of file diff --git a/score/mw/com/test/generic_skeleton/integration_test/test_generic_typed_interaction.py b/score/mw/com/test/generic_skeleton/integration_test/test_generic_typed_interaction.py new file mode 100644 index 000000000..ec8bb8008 --- /dev/null +++ b/score/mw/com/test/generic_skeleton/integration_test/test_generic_typed_interaction.py @@ -0,0 +1,88 @@ +# Copyright (c) 2025 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 + +import time +import logging + +logger = logging.getLogger(__name__) + +def run_interaction_app(target, app_bin, mode, config_path, cwd, wait_on_exit=False, **kwargs): + """Helper to run an application using the framework's native wrap_exec method.""" + args = ["--mode", mode, "--service_instance_manifest", config_path] + return target.wrap_exec(app_bin, args, cwd=cwd, wait_on_exit=wait_on_exit, **kwargs) + +def test_generic_typed_interaction_64_byte(target): + """ + Tests data validation and boundary checks for a 64-byte payload. + """ + app_root = "/opt/generic_typed_interaction_app/" + app_bin = "./bin/generic_typed_interaction_64_byte_app" + config_path = "./etc/mw_com_config.json" + + logger.info(f"Starting provider: {app_bin} in {app_root}") + # Added enforce_clean_shutdown=False and disabled LSAN so forceful shutdown doesn't fail the test + with run_interaction_app(target, app_bin, "provider", config_path, cwd=app_root, enforce_clean_shutdown=False, env={"ASAN_OPTIONS": "detect_leaks=0"}): + # Give the provider a moment to initialize and offer the service + # to prevent a race condition where the consumer starts too quickly. + time.sleep(2) + + logger.info(f"Starting consumer: {app_bin} in {app_root}") + # INCREASED wait_timeout to 60 to ensure it has time to finish + with run_interaction_app(target, app_bin, "consumer", config_path, cwd=app_root, wait_on_exit=True, wait_timeout=60): + pass + +def test_generic_typed_interaction_32_byte(target): + """ + Tests data validation and boundary checks for a 32-byte payload. + """ + app_root = "/opt/generic_typed_interaction_app/" + app_bin = "./bin/generic_typed_interaction_32_byte_app" + config_path = "./etc/mw_com_config.json" + + logger.info(f"Starting provider: {app_bin} in {app_root}") + with run_interaction_app(target, app_bin, "provider", config_path, cwd=app_root, enforce_clean_shutdown=False, env={"ASAN_OPTIONS": "detect_leaks=0"}): + time.sleep(2) + + logger.info(f"Starting consumer: {app_bin} in {app_root}") + with run_interaction_app(target, app_bin, "consumer", config_path, cwd=app_root, wait_on_exit=True, wait_timeout=60): + pass + +def test_generic_typed_interaction_16_byte(target): + """ + Tests data validation and boundary checks for a 16-byte payload. + """ + app_root = "/opt/generic_typed_interaction_app/" + app_bin = "./bin/generic_typed_interaction_16_byte_app" + config_path = "./etc/mw_com_config.json" + + logger.info(f"Starting provider: {app_bin} in {app_root}") + with run_interaction_app(target, app_bin, "provider", config_path, cwd=app_root, enforce_clean_shutdown=False, env={"ASAN_OPTIONS": "detect_leaks=0"}): + time.sleep(2) + + logger.info(f"Starting consumer: {app_bin} in {app_root}") + with run_interaction_app(target, app_bin, "consumer", config_path, cwd=app_root, wait_on_exit=True, wait_timeout=60): + pass + +def test_generic_typed_interaction_8_byte(target): + """ + Tests data validation and boundary checks for an 8-byte payload. + """ + app_root = "/opt/generic_typed_interaction_app/" + app_bin = "./bin/generic_typed_interaction_8_byte_app" + config_path = "./etc/mw_com_config.json" + + logger.info(f"Starting provider: {app_bin} in {app_root}") + with run_interaction_app(target, app_bin, "provider", config_path, cwd=app_root, enforce_clean_shutdown=False, env={"ASAN_OPTIONS": "detect_leaks=0"}): + time.sleep(2) + + logger.info(f"Starting consumer: {app_bin} in {app_root}") + with run_interaction_app(target, app_bin, "consumer", config_path, cwd=app_root, wait_on_exit=True, wait_timeout=60): + pass \ No newline at end of file diff --git a/score/mw/com/test/generic_skeleton/logging.json b/score/mw/com/test/generic_skeleton/logging.json new file mode 100644 index 000000000..cec2ece39 --- /dev/null +++ b/score/mw/com/test/generic_skeleton/logging.json @@ -0,0 +1,8 @@ +{ + "appId": "GENT", + "appDesc": "generic_typed_interaction_test", + "logLevel": "kInfo", + "logLevelThresholdConsole": "kInfo", + "logMode": "kConsole", + "dynamicDatarouterIdentifiers" : true +} \ No newline at end of file diff --git a/score/mw/com/test/generic_skeleton/mw_com_config.json b/score/mw/com/test/generic_skeleton/mw_com_config.json new file mode 100644 index 000000000..50ffe021e --- /dev/null +++ b/score/mw/com/test/generic_skeleton/mw_com_config.json @@ -0,0 +1,74 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/service/GenericTypedInteraction", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 7001, + "events": [ + { + "eventName": "Event8Byte", + "eventId": 1 + }, + { + "eventName": "Event64Byte", + "eventId": 2 + }, + { + "eventName": "Event16Byte", + "eventId": 3 + }, + { + "eventName": "Event32Byte", + "eventId": 4 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "/test/generic/typed/interaction", + "serviceTypeName": "/test/service/GenericTypedInteraction", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "QM", + "binding": "SHM", + "events": [ + { + "eventName": "Event8Byte", + "numberOfSampleSlots": 5, + "maxSubscribers": 1 + }, + { + "eventName": "Event64Byte", + "numberOfSampleSlots": 5, + "maxSubscribers": 1 + }, + { + "eventName": "Event16Byte", + "numberOfSampleSlots": 5, + "maxSubscribers": 1 + }, + { + "eventName": "Event32Byte", + "numberOfSampleSlots": 5, + "maxSubscribers": 1 + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/score/mw/com/test/generic_skeleton/mw_com_config_generic_generic.json b/score/mw/com/test/generic_skeleton/mw_com_config_generic_generic.json new file mode 100644 index 000000000..4590b5ffd --- /dev/null +++ b/score/mw/com/test/generic_skeleton/mw_com_config_generic_generic.json @@ -0,0 +1,74 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/service/GenericGenericInteraction", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 7002, + "events": [ + { + "eventName": "Event8Byte", + "eventId": 1 + }, + { + "eventName": "Event64Byte", + "eventId": 2 + }, + { + "eventName": "Event16Byte", + "eventId": 3 + }, + { + "eventName": "Event32Byte", + "eventId": 4 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "/test/generic/generic/interaction", + "serviceTypeName": "/test/service/GenericGenericInteraction", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "QM", + "binding": "SHM", + "events": [ + { + "eventName": "Event64Byte", + "numberOfSampleSlots": 5, + "maxSubscribers": 1 + }, + { + "eventName": "Event32Byte", + "numberOfSampleSlots": 5, + "maxSubscribers": 1 + }, + { + "eventName": "Event8Byte", + "numberOfSampleSlots": 5, + "maxSubscribers": 1 + }, + { + "eventName": "Event16Byte", + "numberOfSampleSlots": 5, + "maxSubscribers": 1 + } + ] + } + ] + } + ] +} \ No newline at end of file