diff --git a/score/mw/com/gateway/BUILD b/score/mw/com/gateway/BUILD new file mode 100644 index 000000000..60af59dc8 --- /dev/null +++ b/score/mw/com/gateway/BUILD @@ -0,0 +1,16 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +package( + default_visibility = ["//score/mw/com:__subpackages__"], +) diff --git a/score/mw/com/gateway/gateway_application/BUILD b/score/mw/com/gateway/gateway_application/BUILD new file mode 100644 index 000000000..bc49619bb --- /dev/null +++ b/score/mw/com/gateway/gateway_application/BUILD @@ -0,0 +1,56 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@rules_cc//cc:defs.bzl", "cc_library") +load("//quality/unit_testing:unit_testing.bzl", "cc_unit_test") +load("//score/mw:common_features.bzl", "COMPILER_WARNING_FEATURES") + +package( + default_visibility = ["//score/mw/com/gateway:__subpackages__"], +) + +cc_library( + name = "gateway_core", + hdrs = ["gateway_core.h"], + features = COMPILER_WARNING_FEATURES, + visibility = [ + "//score/mw/com/gateway:__subpackages__", + ], + deps = [ + "//score/mw/com/gateway/transport_layer:transport", + "//score/mw/com/impl:instance_specifier", + "//score/mw/com/impl:service_element_type", + "@score_baselibs//score/result", + ], +) + +cc_library( + name = "gateway_error", + srcs = ["gateway_error.cpp"], + hdrs = ["gateway_error.h"], + features = COMPILER_WARNING_FEATURES, + visibility = [ + "//score/mw/com/gateway:__subpackages__", + ], + deps = ["@score_baselibs//score/result"], +) + +cc_unit_test( + name = "gateway_error_test", + srcs = ["gateway_error_test.cpp"], + features = COMPILER_WARNING_FEATURES, + deps = [ + ":gateway_error", + "@score_baselibs//score/result", + ], +) diff --git a/score/mw/com/gateway/gateway_application/gateway_core.h b/score/mw/com/gateway/gateway_application/gateway_core.h new file mode 100644 index 000000000..bc27fe8fb --- /dev/null +++ b/score/mw/com/gateway/gateway_application/gateway_core.h @@ -0,0 +1,93 @@ +/******************************************************************************** + * 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_GATEWAY_GATEWAY_APPLICATION_GATEWAY_CORE_H +#define SCORE_MW_COM_GATEWAY_GATEWAY_APPLICATION_GATEWAY_CORE_H + +#include "score/mw/com/gateway/transport_layer/transport.h" +#include "score/mw/com/impl/instance_specifier.h" +#include "score/mw/com/impl/service_element_type.h" +#include "score/result/result.h" + +#include +#include + +namespace score::mw::com::gateway +{ + +class GatewayCore +{ + public: + virtual ~GatewayCore() = default; + + /// \brief Provide the given service instance locally. + /// \details This API is expected to be called by the transport layer implementation, when the transport layer + /// receives a request from the remote gateway to provide a service instance. It shall provide/create the given + /// service instance locally (e.g. by creating a GenericSkeleton) and offer it. + /// + /// \param service_instance_specifier instance specifier of the service instance to provide. It is expected, that + /// this specifier is configured/existent in the mw_com_config.json at the local gateway side. + /// \param service_elements description of the service elements (events, fields, methods) which should be provided + /// for the service instance, which will be realized as a GenericSkeleton. + /// \return result indicating success or failure. + virtual score::Result ProvideService(impl::InstanceSpecifier service_instance_specifier, + std::vector service_elements) = 0; + + /// \brief Stop providing the given service instance locally. + /// \param service_instance_specifier instance specifier of the service instance to stop offering. It is expected, + /// that this specifier is configured/existent in the mw_com_config.json at the local gateway side. + virtual void StopOfferService(impl::InstanceSpecifier service_instance_specifier) = 0; + + /// \brief Offer the given service instance locally. + /// \param service_instance_specifier instance specifier of the service instance to offer. It is expected, that + /// this specifier is configured/existent in the mw_com_config.json at the local gateway side. + /// \return result indicating success or failure. + virtual score::Result OfferService(impl::InstanceSpecifier service_instance_specifier) = 0; + + /// \brief Register an event-update notification for the given service instance and element locally. + /// \param service_instance_specifier instance specifier of the service instance, for which registration takes + /// place. It is expected, that this specifier is configured/existent in the mw_com_config.json at the local + /// gateway side. + /// \param element_type type of the service element (event, field, method). Currently only EVENT is supported. + /// \param element_name name of the service element for which update notifications shall be registered. + /// \return result indicating success or failure. + virtual score::Result RegisterUpdateNotification(impl::InstanceSpecifier service_instance_specifier, + impl::ServiceElementType element_type, + std::string element_name) = 0; + + /// \brief Unregister an event-update notification for the given service instance and element locally. + /// \param service_instance_specifier instance specifier of the service instance, for which unregistration takes + /// place. It is expected, that this specifier is configured/existent in the mw_com_config.json at the local + /// gateway side. + /// \param element_type type of the service element (event, field, method). Currently only EVENT is supported. + /// \param element_name name of the service element for which update notifications shall be unregistered. + /// \return result indicating success or failure. + virtual score::Result UnregisterUpdateNotification(impl::InstanceSpecifier service_instance_specifier, + impl::ServiceElementType element_type, + std::string element_name) = 0; + + /// \brief Notify an event update for the given service instance and element locally. + /// \param service_instance_specifier instance specifier of the service instance, for which notification takes + /// place. It is expected, that this specifier is configured/existent in the mw_com_config.json at the local + /// gateway side. + /// \param updated_element_type type of the element (currently only events supported) for which an update shall be + /// notified. + /// \param updated_element_name name of the element (e.g. event name) for which an update shall be notified. + /// \return result indicating success or failure. + virtual score::Result NotifyUpdate(impl::InstanceSpecifier service_instance_specifier, + impl::ServiceElementType updated_element_type, + std::string updated_element_name) = 0; +}; + +} // namespace score::mw::com::gateway + +#endif // SCORE_MW_COM_GATEWAY_GATEWAY_APPLICATION_GATEWAY_CORE_H diff --git a/score/mw/com/gateway/gateway_application/gateway_error.cpp b/score/mw/com/gateway/gateway_application/gateway_error.cpp new file mode 100644 index 000000000..83e265d0d --- /dev/null +++ b/score/mw/com/gateway/gateway_application/gateway_error.cpp @@ -0,0 +1,23 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/com/gateway/gateway_application/gateway_error.h" + +namespace +{ +constexpr score::mw::com::gateway::GatewayErrorDomain g_gateway_error_domain; +} // namespace + +score::result::Error score::mw::com::gateway::MakeError(const GatewayErrorc code, const std::string_view message) +{ + return {static_cast(code), g_gateway_error_domain, message}; +} diff --git a/score/mw/com/gateway/gateway_application/gateway_error.h b/score/mw/com/gateway/gateway_application/gateway_error.h new file mode 100644 index 000000000..a5fc57d9a --- /dev/null +++ b/score/mw/com/gateway/gateway_application/gateway_error.h @@ -0,0 +1,64 @@ +/******************************************************************************** + * 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_GATEWAY_GATEWAY_APPLICATION_GATEWAY_ERROR_H +#define SCORE_MW_COM_GATEWAY_GATEWAY_APPLICATION_GATEWAY_ERROR_H + +#include "score/result/result.h" + +#include + +namespace score::mw::com::gateway +{ + +enum class GatewayErrorc : score::result::ErrorCode +{ + kSkeletonCreationFailed = 1, + kSkeletonOfferFailed, + kSkeletonStopOfferFailed, + kUnknownServiceInstance, + kUnknownServiceElement, + kReceiveHandlerRegistrationFailed, + kNonWhitelistedService, +}; + +score::result::Error MakeError(const GatewayErrorc code, const std::string_view message = ""); + +class GatewayErrorDomain final : public score::result::ErrorDomain +{ + public: + std::string_view MessageFor(const score::result::ErrorCode& code) const noexcept override final + { + switch (code) + { + case static_cast(GatewayErrorc::kSkeletonCreationFailed): + return "Creation of generic skeleton on destination gateway failed."; + case static_cast(GatewayErrorc::kSkeletonOfferFailed): + return "Offering of generic skeleton on destination gateway failed."; + case static_cast(GatewayErrorc::kUnknownServiceInstance): + return "Gateway received an API call for an unknown service instance identifier."; + case static_cast(GatewayErrorc::kUnknownServiceElement): + return "Gateway received an API call for an unknown service element within an instance identifier."; + case static_cast(GatewayErrorc::kReceiveHandlerRegistrationFailed): + return "Gateway couldn't register event-receive-handler at its generic proxy to forward event update " + "notifications."; + case static_cast(GatewayErrorc::kNonWhitelistedService): + return "Gateway received request to provide a non-whitelised service."; + default: + return "unknown gateway error"; + } + } +}; + +} // namespace score::mw::com::gateway + +#endif // SCORE_MW_COM_GATEWAY_GATEWAY_APPLICATION_GATEWAY_ERROR_H diff --git a/score/mw/com/gateway/gateway_application/gateway_error_test.cpp b/score/mw/com/gateway/gateway_application/gateway_error_test.cpp new file mode 100644 index 000000000..2205c675d --- /dev/null +++ b/score/mw/com/gateway/gateway_application/gateway_error_test.cpp @@ -0,0 +1,98 @@ +/******************************************************************************** + * 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/gateway/gateway_application/gateway_error.h" + +#include + +namespace score::mw::com::gateway +{ +namespace +{ + +class GatewayErrorTest : public ::testing::Test +{ + protected: + void TestErrorMessage(const GatewayErrorc error_code, const std::string_view expected_error_output) + { + const auto error_code_test = + gateway_error_domain_dummy_.MessageFor(static_cast(error_code)); + ASSERT_EQ(error_code_test, expected_error_output); + } + + GatewayErrorDomain gateway_error_domain_dummy_{}; +}; + +TEST_F(GatewayErrorTest, MessageForSkeletonCreationFailed) +{ + TestErrorMessage(GatewayErrorc::kSkeletonCreationFailed, + "Creation of generic skeleton on destination gateway failed."); +} + +TEST_F(GatewayErrorTest, MessageForSkeletonOfferFailed) +{ + TestErrorMessage(GatewayErrorc::kSkeletonOfferFailed, + "Offering of generic skeleton on destination gateway failed."); +} + +TEST_F(GatewayErrorTest, MessageForSkeletonStopOfferFailedFallsBackToDefault) +{ + TestErrorMessage(GatewayErrorc::kSkeletonStopOfferFailed, "unknown gateway error"); +} + +TEST_F(GatewayErrorTest, MessageForUnknownServiceInstance) +{ + TestErrorMessage(GatewayErrorc::kUnknownServiceInstance, + "Gateway received an API call for an unknown service instance identifier."); +} + +TEST_F(GatewayErrorTest, MessageForUnknownServiceElement) +{ + TestErrorMessage(GatewayErrorc::kUnknownServiceElement, + "Gateway received an API call for an unknown service element within an instance identifier."); +} + +TEST_F(GatewayErrorTest, MessageForReceiveHandlerRegistrationFailed) +{ + TestErrorMessage( + GatewayErrorc::kReceiveHandlerRegistrationFailed, + "Gateway couldn't register event-receive-handler at its generic proxy to forward event update notifications."); +} + +TEST_F(GatewayErrorTest, MessageForNonWhitelistedService) +{ + TestErrorMessage(GatewayErrorc::kNonWhitelistedService, + "Gateway received request to provide a non-whitelised service."); +} + +TEST_F(GatewayErrorTest, MessageForDefault) +{ + TestErrorMessage(static_cast(-1), "unknown gateway error"); +} + +TEST(GatewayError, MakeErrorExpectedError) +{ + const score::result::Error err{MakeError(GatewayErrorc::kUnknownServiceInstance)}; + EXPECT_EQ(*err, static_cast(GatewayErrorc::kUnknownServiceInstance)); + EXPECT_TRUE(err.UserMessage().empty()); +} + +TEST(GatewayError, MakeErrorExpectedErrorWithUserMessage) +{ + constexpr std::string_view kUserMessage{"test user message"}; + const score::result::Error err{MakeError(GatewayErrorc::kSkeletonOfferFailed, kUserMessage)}; + EXPECT_EQ(*err, static_cast(GatewayErrorc::kSkeletonOfferFailed)); + EXPECT_EQ(err.UserMessage(), kUserMessage); +} + +} // namespace +} // namespace score::mw::com::gateway diff --git a/score/mw/com/gateway/transport_layer/BUILD b/score/mw/com/gateway/transport_layer/BUILD new file mode 100644 index 000000000..c883f0c29 --- /dev/null +++ b/score/mw/com/gateway/transport_layer/BUILD @@ -0,0 +1,66 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@rules_cc//cc:defs.bzl", "cc_library") +load("//quality/unit_testing:unit_testing.bzl", "cc_unit_test") +load("//score/mw:common_features.bzl", "COMPILER_WARNING_FEATURES") + +package( + default_visibility = ["//score/mw/com/gateway:__subpackages__"], +) + +cc_library( + name = "transport", + srcs = ["transport.cpp"], + hdrs = ["transport.h"], + features = COMPILER_WARNING_FEATURES, + visibility = [ + "//score/mw/com/gateway:__subpackages__", + ], + deps = [ + "//score/mw/com/impl:instance_specifier", + "//score/mw/com/impl:service_element_type", + "@score_baselibs//score/result", + ], +) + +cc_library( + name = "transport_error", + srcs = ["transport_error.cpp"], + hdrs = ["transport_error.h"], + features = COMPILER_WARNING_FEATURES, + visibility = [ + "//score/mw/com/gateway:__subpackages__", + ], + deps = ["@score_baselibs//score/result"], +) + +cc_unit_test( + name = "transport_error_test", + srcs = ["transport_error_test.cpp"], + features = COMPILER_WARNING_FEATURES, + deps = [ + ":transport_error", + "@score_baselibs//score/result", + ], +) + +cc_unit_test( + name = "transport_test", + srcs = ["transport_test.cpp"], + features = COMPILER_WARNING_FEATURES, + deps = [ + ":transport", + "@score_baselibs//score/result", + ], +) diff --git a/score/mw/com/gateway/transport_layer/transport.cpp b/score/mw/com/gateway/transport_layer/transport.cpp new file mode 100644 index 000000000..b4d8c29e7 --- /dev/null +++ b/score/mw/com/gateway/transport_layer/transport.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/gateway/transport_layer/transport.h" diff --git a/score/mw/com/gateway/transport_layer/transport.h b/score/mw/com/gateway/transport_layer/transport.h new file mode 100644 index 000000000..9953d6955 --- /dev/null +++ b/score/mw/com/gateway/transport_layer/transport.h @@ -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 + ********************************************************************************/ +#ifndef SCORE_MW_COM_GATEWAY_TRANSPORT_LAYER_TRANSPORT_H +#define SCORE_MW_COM_GATEWAY_TRANSPORT_LAYER_TRANSPORT_H + +#include "score/mw/com/impl/instance_specifier.h" +#include "score/mw/com/impl/service_element_type.h" +#include "score/result/result.h" + +#include +#include +#include +#include +#include + +namespace score::mw::com::gateway +{ + +/// \brief Holds size and alignment information for a data type exchanged via the transport layer. +struct DataTypeSizeInfo +{ + std::size_t size{0U}; + std::size_t alignment{0U}; + + friend bool operator==(const DataTypeSizeInfo& lhs, const DataTypeSizeInfo& rhs) noexcept + { + return lhs.size == rhs.size && lhs.alignment == rhs.alignment; + } + + friend bool operator!=(const DataTypeSizeInfo& lhs, const DataTypeSizeInfo& rhs) noexcept + { + return !(lhs == rhs); + } +}; + +static_assert(std::is_trivially_copyable_v, "DataTypeSizeInfo must be trivially copyable"); + +/// \brief Describes a single service element (event, field, or method) and its serialization type info. +struct ServiceElementConfiguration +{ + std::string element_name; + DataTypeSizeInfo size_info; + + std::tuple GetSerializeMembers() const + { + return {element_name, size_info}; + } + + std::tuple GetSerializeMembers() + { + return {element_name, size_info}; + } +}; + +/// \brief Abstract base class for gateway transport layer implementations. +class Transport +{ + public: + Transport() = default; + virtual ~Transport() = default; + + /// \brief Returns whether this transport implementation supports memory sharing between source and destination. + /// \return true if shared memory transport is supported, false otherwise. + virtual bool IsMemorySharingSupported() = 0; + + /// \brief Sets up the transport layer (e.g. establishes connections or allocates resources). + /// \return result indicating success or failure. + virtual score::Result Setup() = 0; + + /// \brief Shuts down the transport layer and releases all resources. + virtual void Shutdown() = 0; + + /// \brief ProvideService API signature to be provided when IsMemorySharingSupported() == true. + /// \details Transport layer implementation shall "forward" this call to the destination gateway, which is + /// expected to create a (generic) skeleton for the given service instance and service elements. The (generic) + /// skeleton created by the destination gateway in this case expects to work on existing shared memory, which is + /// shared between the source and destination gateway. The transport layer implementation must ensure, that the + /// shared memory is visible/accessible on the destination gateway side, when the (generic) skeleton gets created. + /// \param service_instance_specifier instance specifier of the service to provide. It is expected, that this + /// specifier is configured/existent in the mw_com_config.json at the destination gateway side. + /// \param service_elements configuration of the service elements (events, fields, methods) of the service to + /// provide. This information is needed to create the (generic) skeleton on the destination gateway side. + /// \return result indicating success or failure. + virtual score::Result ProvideService(impl::InstanceSpecifier service_instance_specifier, + std::vector service_elements) = 0; + + /// \brief OfferService API to trigger service-instance offering at the destination gateway side. + /// \details Transport layer implementation shall "forward" this call to the destination gateway and trigger the + /// offering of the service instance at the destination gateway side. The service instance is expected to be + /// already created at the destination gateway side, e.g. by a previous call to ProvideService(). + /// \param service_instance_specifier instance specifier of the service instance to offer. + /// \return result indicating success or failure. + virtual score::Result OfferService(impl::InstanceSpecifier service_instance_specifier) = 0; + + /// \brief StopOfferService API to trigger service-instance stop-offer at the destination gateway side. + /// \details Transport layer implementation shall "forward" this call to the destination gateway and trigger the + /// stop-offer of the service instance at the destination gateway side. The service instance is expected to be + /// already created at the destination gateway side, e.g. by a previous call to ProvideService(). + /// \param service_instance_specifier instance specifier of the service instance to stop-offer. + /// \return result indicating success or failure. + virtual score::Result StopOfferService(impl::InstanceSpecifier service_instance_specifier) = 0; + + /// \brief NotifyUpdate API to inform the destination gateway about updates of service elements of a service + /// instance on the source gateway side. + /// \details The transport layer implementation shall "forward" this call to the destination gateway, which is + /// expected to trigger update notifications at the (generic) skeleton of the related service instance on the + /// destination gateway side for the given service element. API will only be called by the source gateway, if + /// previously the destination gateway registered for update notifications for the given service instance and + /// service element by calling the RegisterUpdateNotification API. + /// \param service_instance_specifier instance specifier of the service instance owning the service element, for + /// which an update shall be notified. + /// \param updated_element_type type of the updated service element (event, field, method). Currently only EVENT + /// is supported. + /// \param updated_element_name name of the updated service element. + /// \return result indicating success or failure. + virtual score::Result NotifyUpdate(impl::InstanceSpecifier service_instance_specifier, + impl::ServiceElementType updated_element_type, + std::string updated_element_name) = 0; + + /// \brief API to register for update notifications for service elements of a service instance at the source + /// gateway side. + /// \details The transport layer implementation shall "forward" this call to the source gateway, which is expected + /// to register for update notifications at the (generic) proxy of the related service instance on the source + /// gateway side for the given service element. + /// \param service_instance_specifier instance specifier of the service instance owning the service element, for + /// which an update-notification shall be registered. + /// \param element_type type of the service element (event, field, method). Currently only EVENT is supported. + /// \param element_name name of the service element. + /// \return result indicating success or failure. + virtual score::Result RegisterUpdateNotification(impl::InstanceSpecifier service_instance_specifier, + impl::ServiceElementType element_type, + std::string element_name) = 0; + + /// \brief API to unregister update notifications for service elements of a service instance at the source gateway + /// side. + /// \details See RegisterUpdateNotification API for the corresponding registration semantics. + /// \param service_instance_specifier instance specifier of the service instance owning the service element, for + /// which the update-notification shall be unregistered. + /// \param element_type type of the service element (event, field, method). Currently only EVENT is supported. + /// \param element_name name of the service element. + /// \return result indicating success or failure. + virtual score::Result UnregisterUpdateNotification(impl::InstanceSpecifier service_instance_specifier, + impl::ServiceElementType element_type, + std::string element_name) = 0; +}; + +} // namespace score::mw::com::gateway + +#endif // SCORE_MW_COM_GATEWAY_TRANSPORT_LAYER_TRANSPORT_H diff --git a/score/mw/com/gateway/transport_layer/transport_error.cpp b/score/mw/com/gateway/transport_layer/transport_error.cpp new file mode 100644 index 000000000..618bc630b --- /dev/null +++ b/score/mw/com/gateway/transport_layer/transport_error.cpp @@ -0,0 +1,23 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/com/gateway/transport_layer/transport_error.h" + +namespace +{ +constexpr score::mw::com::gateway::TransportErrorDomain g_transport_error_domain; +} // namespace + +score::result::Error score::mw::com::gateway::MakeError(const TransportErrorc code, const std::string_view message) +{ + return {static_cast(code), g_transport_error_domain, message}; +} diff --git a/score/mw/com/gateway/transport_layer/transport_error.h b/score/mw/com/gateway/transport_layer/transport_error.h new file mode 100644 index 000000000..c1c83ef14 --- /dev/null +++ b/score/mw/com/gateway/transport_layer/transport_error.h @@ -0,0 +1,77 @@ +/******************************************************************************** + * 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_GATEWAY_TRANSPORT_LAYER_TRANSPORT_ERROR_H +#define SCORE_MW_COM_GATEWAY_TRANSPORT_LAYER_TRANSPORT_ERROR_H + +#include "score/result/result.h" + +#include + +namespace score::mw::com::gateway +{ + +enum class TransportErrorc : score::result::ErrorCode +{ + kServerSetupFailed = 1, + kServerListeningReturned, + kClientSetupFailed, + kFailedToProvideService, + kConnectionFailure, + kNotConnected, + kTimeout, + kInvalidMessage, + kNotSupported, + kSendFailure, + kReceiveFailure, +}; + +score::result::Error MakeError(const TransportErrorc code, const std::string_view message = ""); + +class TransportErrorDomain final : public score::result::ErrorDomain +{ + public: + std::string_view MessageFor(const score::result::ErrorCode& code) const noexcept override final + { + switch (code) + { + case static_cast(TransportErrorc::kServerSetupFailed): + return "Gateway server could not be setup."; + case static_cast(TransportErrorc::kServerListeningReturned): + return "Server returned from its listening thread."; + case static_cast(TransportErrorc::kClientSetupFailed): + return "Gateway client could not be setup."; + case static_cast(TransportErrorc::kFailedToProvideService): + return "Failed to provide service."; + case static_cast(TransportErrorc::kConnectionFailure): + return "Transport connection failed."; + case static_cast(TransportErrorc::kNotConnected): + return "Transport not connected."; + case static_cast(TransportErrorc::kTimeout): + return "Transport operation timed out."; + case static_cast(TransportErrorc::kInvalidMessage): + return "Invalid transport message."; + case static_cast(TransportErrorc::kNotSupported): + return "Operation not supported by this transport."; + case static_cast(TransportErrorc::kSendFailure): + return "Failed to send message."; + case static_cast(TransportErrorc::kReceiveFailure): + return "Failed to receive message."; + default: + return "unknown transport error"; + } + } +}; + +} // namespace score::mw::com::gateway + +#endif // SCORE_MW_COM_GATEWAY_TRANSPORT_LAYER_TRANSPORT_ERROR_H diff --git a/score/mw/com/gateway/transport_layer/transport_error_test.cpp b/score/mw/com/gateway/transport_layer/transport_error_test.cpp new file mode 100644 index 000000000..134e7c691 --- /dev/null +++ b/score/mw/com/gateway/transport_layer/transport_error_test.cpp @@ -0,0 +1,111 @@ +/******************************************************************************** + * 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/gateway/transport_layer/transport_error.h" + +#include + +namespace score::mw::com::gateway +{ +namespace +{ + +class TransportErrorTest : public ::testing::Test +{ + protected: + void TestErrorMessage(const TransportErrorc error_code, const std::string_view expected_error_output) + { + const auto error_code_test = + transport_error_domain_dummy_.MessageFor(static_cast(error_code)); + ASSERT_EQ(error_code_test, expected_error_output); + } + + TransportErrorDomain transport_error_domain_dummy_{}; +}; + +TEST_F(TransportErrorTest, MessageForServerSetupFailed) +{ + TestErrorMessage(TransportErrorc::kServerSetupFailed, "Gateway server could not be setup."); +} + +TEST_F(TransportErrorTest, MessageForServerListeningReturned) +{ + TestErrorMessage(TransportErrorc::kServerListeningReturned, "Server returned from its listening thread."); +} + +TEST_F(TransportErrorTest, MessageForClientSetupFailed) +{ + TestErrorMessage(TransportErrorc::kClientSetupFailed, "Gateway client could not be setup."); +} + +TEST_F(TransportErrorTest, MessageForFailedToProvideService) +{ + TestErrorMessage(TransportErrorc::kFailedToProvideService, "Failed to provide service."); +} + +TEST_F(TransportErrorTest, MessageForConnectionFailure) +{ + TestErrorMessage(TransportErrorc::kConnectionFailure, "Transport connection failed."); +} + +TEST_F(TransportErrorTest, MessageForNotConnected) +{ + TestErrorMessage(TransportErrorc::kNotConnected, "Transport not connected."); +} + +TEST_F(TransportErrorTest, MessageForTimeout) +{ + TestErrorMessage(TransportErrorc::kTimeout, "Transport operation timed out."); +} + +TEST_F(TransportErrorTest, MessageForInvalidMessage) +{ + TestErrorMessage(TransportErrorc::kInvalidMessage, "Invalid transport message."); +} + +TEST_F(TransportErrorTest, MessageForNotSupported) +{ + TestErrorMessage(TransportErrorc::kNotSupported, "Operation not supported by this transport."); +} + +TEST_F(TransportErrorTest, MessageForSendFailure) +{ + TestErrorMessage(TransportErrorc::kSendFailure, "Failed to send message."); +} + +TEST_F(TransportErrorTest, MessageForReceiveFailure) +{ + TestErrorMessage(TransportErrorc::kReceiveFailure, "Failed to receive message."); +} + +TEST_F(TransportErrorTest, MessageForDefault) +{ + TestErrorMessage(static_cast(-1), "unknown transport error"); +} + +TEST(TransportError, MakeErrorExpectedError) +{ + const score::result::Error err{MakeError(TransportErrorc::kReceiveFailure)}; + EXPECT_EQ(*err, static_cast(TransportErrorc::kReceiveFailure)); + EXPECT_TRUE(err.UserMessage().empty()); +} + +TEST(TransportError, MakeErrorExpectedErrorWithUserMessage) +{ + constexpr std::string_view kUserMessage{"test user message"}; + const score::result::Error err{MakeError(TransportErrorc::kConnectionFailure, kUserMessage)}; + EXPECT_EQ(*err, static_cast(TransportErrorc::kConnectionFailure)); + EXPECT_EQ(err.UserMessage(), kUserMessage); +} + +} // namespace +} // namespace score::mw::com::gateway diff --git a/score/mw/com/gateway/transport_layer/transport_test.cpp b/score/mw/com/gateway/transport_layer/transport_test.cpp new file mode 100644 index 000000000..fa6134780 --- /dev/null +++ b/score/mw/com/gateway/transport_layer/transport_test.cpp @@ -0,0 +1,68 @@ +/******************************************************************************** + * 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/gateway/transport_layer/transport.h" + +#include + +#include + +namespace score::mw::com::gateway +{ +namespace +{ + +TEST(DataTypeSizeInfoTest, EqualityComparesAllMembers) +{ + const DataTypeSizeInfo first{8U, 4U}; + const DataTypeSizeInfo same{8U, 4U}; + const DataTypeSizeInfo different_size{16U, 4U}; + const DataTypeSizeInfo different_alignment{8U, 8U}; + + EXPECT_EQ(first, same); + EXPECT_NE(first, different_size); + EXPECT_NE(first, different_alignment); +} + +TEST(DataTypeSizeInfoTest, DefaultInitializationSetsZeroValues) +{ + const DataTypeSizeInfo value{}; + + EXPECT_EQ(value.size, 0U); + EXPECT_EQ(value.alignment, 0U); +} + +TEST(ServiceElementConfigurationTest, SerializeMembersExposeMutableReferences) +{ + ServiceElementConfiguration config{"EventA", DataTypeSizeInfo{32U, 8U}}; + auto members = config.GetSerializeMembers(); + + std::get<0>(members) = "EventB"; + std::get<1>(members).size = 64U; + + EXPECT_EQ(config.element_name, "EventB"); + EXPECT_EQ(config.size_info.size, 64U); + EXPECT_EQ(config.size_info.alignment, 8U); +} + +TEST(ServiceElementConfigurationTest, SerializeMembersConstOverloadReturnsConstReferences) +{ + const ServiceElementConfiguration config{"EventA", DataTypeSizeInfo{32U, 8U}}; + const auto members = config.GetSerializeMembers(); + + EXPECT_EQ(std::get<0>(members), "EventA"); + EXPECT_EQ(std::get<1>(members).size, 32U); + EXPECT_EQ(std::get<1>(members).alignment, 8U); +} + +} // namespace +} // namespace score::mw::com::gateway