diff --git a/score/mw/com/impl/BUILD b/score/mw/com/impl/BUILD index 8172c3dee..cd455d16e 100644 --- a/score/mw/com/impl/BUILD +++ b/score/mw/com/impl/BUILD @@ -1176,6 +1176,13 @@ cc_unit_test( ], ) +cc_unit_test( + name = "method_type_test", + srcs = ["method_type_test.cpp"], + features = COMPILER_WARNING_FEATURES, + deps = [":method_type"], +) + cc_unit_test( name = "sample_reference_tracker_test", srcs = ["sample_reference_tracker_test.cpp"], @@ -1243,9 +1250,11 @@ cc_unit_test( deps = [ ":impl", ":runtime_mock", + "//score/mw/com/impl/bindings/mock_binding", "//score/mw/com/impl/plumbing:proxy_field_binding_factory_mock", "//score/mw/com/impl/test:binding_factory_resources", "//score/mw/com/impl/test:proxy_resources", + "//score/mw/com/impl/test:runtime_mock_guard", ], ) diff --git a/score/mw/com/impl/method_type_test.cpp b/score/mw/com/impl/method_type_test.cpp new file mode 100644 index 000000000..f83d429bb --- /dev/null +++ b/score/mw/com/impl/method_type_test.cpp @@ -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 + ********************************************************************************/ +#include "score/mw/com/impl/method_type.h" + +#include + +namespace score::mw::com::impl +{ +namespace +{ + +TEST(MethodTypeToStringTest, ReturnsMethodForKMethod) +{ + // When converting a MethodType of kMethod to string + // Then the result is "Method" + EXPECT_EQ(to_string(MethodType::kMethod), "Method"); +} + +TEST(MethodTypeToStringTest, ReturnsGetForKGet) +{ + // When converting a MethodType of kGet to string + // Then the result is "Get" + EXPECT_EQ(to_string(MethodType::kGet), "Get"); +} + +TEST(MethodTypeToStringTest, ReturnsSetForKSet) +{ + // When converting a MethodType of kSet to string + // Then the result is "Set" + EXPECT_EQ(to_string(MethodType::kSet), "Set"); +} + +TEST(MethodTypeToStringTest, ReturnsUnknownForKUnknown) +{ + // When converting a MethodType of kUnknown to string + // Then the result is "Unknown" + EXPECT_EQ(to_string(MethodType::kUnknown), "Unknown"); +} + +TEST(MethodTypeToStringTest, ReturnsInvalidForOutOfRangeValue) +{ + // Given an out-of-range MethodType value + const auto invalid_value = static_cast(255U); + + // When converting to string + // Then the result is "Invalid" + EXPECT_EQ(to_string(invalid_value), "Invalid"); +} + +} // namespace +} // namespace score::mw::com::impl diff --git a/score/mw/com/impl/plumbing/proxy_method_binding_factory_impl.h b/score/mw/com/impl/plumbing/proxy_method_binding_factory_impl.h index d572003ed..23caaf73e 100644 --- a/score/mw/com/impl/plumbing/proxy_method_binding_factory_impl.h +++ b/score/mw/com/impl/plumbing/proxy_method_binding_factory_impl.h @@ -104,6 +104,9 @@ Result> ProxyMethodBindingFactoryImpl>; diff --git a/score/mw/com/impl/plumbing/proxy_method_binding_factory_test.cpp b/score/mw/com/impl/plumbing/proxy_method_binding_factory_test.cpp index a057829b8..cfd0b4d94 100644 --- a/score/mw/com/impl/plumbing/proxy_method_binding_factory_test.cpp +++ b/score/mw/com/impl/plumbing/proxy_method_binding_factory_test.cpp @@ -13,6 +13,8 @@ #include "score/mw/com/impl/plumbing/proxy_method_binding_factory.h" #include "score/mw/com/impl/bindings/lola/test/proxy_event_test_resources.h" #include "score/mw/com/impl/bindings/mock_binding/proxy.h" +#include "score/mw/com/impl/configuration/lola_event_instance_deployment.h" +#include "score/mw/com/impl/configuration/lola_field_instance_deployment.h" #include "score/mw/com/impl/configuration/lola_service_instance_deployment.h" #include "score/mw/com/impl/configuration/lola_service_instance_id.h" #include "score/mw/com/impl/configuration/quality_type.h" @@ -63,6 +65,27 @@ ConfigurationStore kConfigStoreAsilB{kInstanceSpecifier, kLolaServiceTypeDeployment, kLolaServiceInstanceDeployment}; +constexpr auto kDummyFieldName{"Field1"}; +constexpr std::uint16_t kDummyFieldId{6U}; +const auto kFieldInstanceSpecifier = InstanceSpecifier::Create(std::string{"/my_field_instance_specifier"}).value(); + +const LolaServiceInstanceDeployment kLolaServiceInstanceDeploymentWithField{ + LolaServiceInstanceId{kInstanceId}, + {}, + {{kDummyFieldName, LolaFieldInstanceDeployment{LolaEventInstanceDeployment{{1U}, {3U}, 1U, true, 0}, true, true}}}, + {{kDummyFieldName, LolaMethodInstanceDeployment{kQueueSize, true}}}}; + +const LolaServiceTypeDeployment kLolaServiceTypeDeploymentWithField{kServiceId, + {}, + {{kDummyFieldName, kDummyFieldId}}, + {{kDummyMethodName, kDummyMethodId}}}; + +ConfigurationStore kConfigStoreWithFieldAsilB{kFieldInstanceSpecifier, + make_ServiceIdentifierType("/a/service/somewhere/out/there", 13U, 37U), + kQualityType, + kLolaServiceTypeDeploymentWithField, + kLolaServiceInstanceDeploymentWithField}; + class ProxyMethodFactoryFixture : public lola::ProxyMockedMemoryFixture { @@ -72,6 +95,11 @@ class ProxyMethodFactoryFixture : public lola::ProxyMockedMemoryFixture return kConfigStoreAsilB.GetHandle(); } + HandleType GetValidLoLaHandleWithField() + { + return kConfigStoreWithFieldAsilB.GetHandle(); + } + HandleType GetBlankBindingHandle() { const auto instance_identifier = dummy_instance_identifier_builder_.CreateBlankBindingInstanceIdentifier(); @@ -108,6 +136,25 @@ TYPED_TEST(ProxyMethodFactoryTypedFixture, CanConstructLolaProxyMethod) ASSERT_NE(proxy_method.value(), nullptr); } +TYPED_TEST(ProxyMethodFactoryTypedFixture, CanConstructFieldGetAndSetMethods) +{ + // Given a valid lola binding whose deployment carries a field named kDummyFieldName + const auto handle = this->GetValidLoLaHandleWithField(); + this->InitialiseProxyWithConstructor(handle.GetInstanceIdentifier()); + using MethodSignature = TypeParam; + + // When creating a binding for both the Get and the Set of that field + for (const auto method_type : {MethodType::kGet, MethodType::kSet}) + { + auto proxy_method = + ProxyMethodBindingFactory::Create(handle, *this->proxy_, kDummyFieldName, method_type); + + // Then a valid binding is created + ASSERT_TRUE(proxy_method.has_value()); + ASSERT_NE(proxy_method.value(), nullptr); + } +} + TYPED_TEST(ProxyMethodFactoryTypedFixture, ConstructingLolaMethodBindingWhichIsDisabledInConfigurationReturnsNullptr) { // Given a valid lola binding with a method which is disabled in the configuration @@ -223,6 +270,20 @@ TYPED_TEST(ProxyMethodFactoryTypedFixture, GetQueueSizeTerminatesForMethodNotInL score::cpp::ignore = detail::GetQueueSize(handle, wrong_name, MethodType::kMethod)); } +TYPED_TEST(ProxyMethodFactoryTypedFixture, ConstructingLolaMethodBindingWithUnknownMethodTypeTerminates) +{ + // Given a valid lola binding + const auto handle = this->GetValidLoLaHandle(); + this->InitialiseProxyWithConstructor(handle.GetInstanceIdentifier()); + + // When creating a ProxyMethod using MethodBindingFactory with MethodType::kUnknown + // Then the program terminates + using MethodSignature = TypeParam; + EXPECT_DEATH(score::cpp::ignore = ProxyMethodBindingFactory::Create( + handle, *this->proxy_, kDummyMethodName, MethodType::kUnknown), + ".*"); +} + TYPED_TEST(ProxyMethodFactoryTypedFixture, GetQueueSizeTerminatesForMethodInLolaDeploymentWithoutQueueSize) { // Given a handle to a valid lola deployment which contains a method with empty QueueSize diff --git a/score/mw/com/impl/proxy_field_test.cpp b/score/mw/com/impl/proxy_field_test.cpp index c33fec6f2..4f3f38234 100644 --- a/score/mw/com/impl/proxy_field_test.cpp +++ b/score/mw/com/impl/proxy_field_test.cpp @@ -19,13 +19,21 @@ #include "score/mw/com/impl/proxy_field.h" +#include "score/mw/com/impl/bindings/mock_binding/proxy_event.h" +#include "score/mw/com/impl/bindings/mock_binding/proxy_method.h" +#include "score/mw/com/impl/com_error.h" #include "score/mw/com/impl/runtime.h" #include "score/mw/com/impl/runtime_mock.h" #include "score/mw/com/impl/test/binding_factory_resources.h" #include "score/mw/com/impl/test/proxy_resources.h" +#include "score/mw/com/impl/test/runtime_mock_guard.h" #include #include +#include +#include +#include +#include #include namespace score::mw::com::impl @@ -330,5 +338,129 @@ TEST(ProxyFieldNotifierGatingTest, OnlyNotifierApiExistsWhenNoTagsArePresent) static_assert(!has_set::value); } +/// \brief Test fixture for the runtime behavior of ProxyField's Get / Set API. +class ProxyFieldGetSetFixture : public ::testing::Test +{ + public: + void SetUp() override + { + ON_CALL(runtime_mock_guard_.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr)); + + ON_CALL(get_method_binding_mock_, GetReturnValueBuffer(0)) + .WillByDefault(Return(score::Result>{ + score::cpp::span{return_type_buffer_.data(), return_type_buffer_.size()}})); + ON_CALL(get_method_binding_mock_, GetInArgsBuffer(0)) + .WillByDefault(Return(score::Result>{ + score::cpp::span{in_args_buffer_.data(), in_args_buffer_.size()}})); + ON_CALL(get_method_binding_mock_, DoCall(0)).WillByDefault(Return(score::ResultBlank{})); + + ON_CALL(set_method_binding_mock_, GetReturnValueBuffer(0)) + .WillByDefault(Return(score::Result>{ + score::cpp::span{return_type_buffer_.data(), return_type_buffer_.size()}})); + ON_CALL(set_method_binding_mock_, GetInArgsBuffer(0)) + .WillByDefault(Return(score::Result>{ + score::cpp::span{in_args_buffer_.data(), in_args_buffer_.size()}})); + ON_CALL(set_method_binding_mock_, DoCall(0)).WillByDefault(Return(score::ResultBlank{})); + } + + ProxyField CreateFieldWithGetOnly(const std::string_view name = "TestField") + { + return ProxyField{ + name, + std::make_unique>(proxy_event_mock_), + nullptr, + std::make_unique(get_method_binding_mock_)}; + } + + ProxyField CreateFieldWithSetAndNotifier( + const std::string_view name = "TestField") + { + return ProxyField{ + name, + std::make_unique>(proxy_event_mock_), + std::make_unique(set_method_binding_mock_)}; + } + + alignas(8) std::array return_type_buffer_{}; + alignas(8) std::array in_args_buffer_{}; + RuntimeMockGuard runtime_mock_guard_{}; + mock_binding::ProxyEvent proxy_event_mock_{}; + mock_binding::ProxyMethod get_method_binding_mock_{}; + mock_binding::ProxyMethod set_method_binding_mock_{}; +}; + +TEST_F(ProxyFieldGetSetFixture, GetDelegatesToProxyMethodBinding) +{ + // Given a Get-enabled ProxyField and a mock method binding with a value pre-written into the return buffer + auto field = CreateFieldWithGetOnly(); + const TestSampleType expected_value{123U}; + return_type_buffer_[0] = static_cast(expected_value); + + // Expecting that the binding returns a buffer for the return value and then calls the method + EXPECT_CALL(get_method_binding_mock_, GetReturnValueBuffer(0U)); + EXPECT_CALL(get_method_binding_mock_, DoCall(0U)); + + // When calling Get() + auto result = field.Get(); + + // Then the call succeeds and returns the buffered value + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(*result.value(), expected_value); +} + +TEST_F(ProxyFieldGetSetFixture, SetDelegatesToProxyMethodBindingAndCopiesValueIntoInArgsBuffer) +{ + // Given a ProxyField with a setter (WithSetter, WithNotifier) + auto field = CreateFieldWithSetAndNotifier(); + const TestSampleType value{42U}; + + // Expecting that the binding returns buffers for in-args and return value and then calls the method + EXPECT_CALL(set_method_binding_mock_, GetInArgsBuffer(0U)); + EXPECT_CALL(set_method_binding_mock_, GetReturnValueBuffer(0U)); + EXPECT_CALL(set_method_binding_mock_, DoCall(0U)); + + // When Set is called + auto result = field.Set(value); + + // Then the call succeeds and the value lands in the in-args buffer + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(static_cast(in_args_buffer_[0]), value); +} + +TEST_F(ProxyFieldGetSetFixture, GetPropagatesBindingError) +{ + // Given a Get-enabled ProxyField (WithGetter) + auto field = CreateFieldWithGetOnly(); + + // Expecting that the binding returns an error when asked for the return value buffer + EXPECT_CALL(get_method_binding_mock_, GetReturnValueBuffer(0U)) + .WillOnce(Return(MakeUnexpected(ComErrc::kBindingFailure))); + + // When calling Get() + auto result = field.Get(); + + // Then the error is propagated back to the caller + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ComErrc::kBindingFailure); +} + +TEST_F(ProxyFieldGetSetFixture, SetPropagatesBindingError) +{ + // Given a Set-enabled ProxyField (WithSetter, WithNotifier) + auto field = CreateFieldWithSetAndNotifier(); + const TestSampleType value{42U}; + + // Expecting that the binding returns an error when asked for the in-args buffer + EXPECT_CALL(set_method_binding_mock_, GetInArgsBuffer(0U)) + .WillOnce(Return(MakeUnexpected(ComErrc::kBindingFailure))); + + // When Set is called + auto result = field.Set(value); + + // Then the error is propagated back to the caller + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ComErrc::kBindingFailure); +} + } // namespace } // namespace score::mw::com::impl