diff --git a/score/mw/com/impl/bindings/lola/proxy_method.cpp b/score/mw/com/impl/bindings/lola/proxy_method.cpp index 546aeded9..852b52867 100644 --- a/score/mw/com/impl/bindings/lola/proxy_method.cpp +++ b/score/mw/com/impl/bindings/lola/proxy_method.cpp @@ -48,7 +48,7 @@ ProxyMethod::ProxyMethod(Proxy& proxy, proxy.RegisterMethod(element_fq_id.element_id_, *this); } -score::Result> ProxyMethod::AllocateInArgs(std::size_t queue_position) +score::Result> ProxyMethod::GetInArgsBuffer(std::size_t queue_position) { if (!is_subscribed_) { @@ -65,7 +65,7 @@ score::Result> ProxyMethod::AllocateInArgs(std::size return GetInArgValuesElementStorage(queue_position, in_args_storage_.value(), type_erased_element_info_); } -score::Result> ProxyMethod::AllocateReturnType(std::size_t queue_position) +score::Result> ProxyMethod::GetReturnValueBuffer(std::size_t queue_position) { if (!is_subscribed_) { diff --git a/score/mw/com/impl/bindings/lola/proxy_method.h b/score/mw/com/impl/bindings/lola/proxy_method.h index e86aec98b..97d0828fd 100644 --- a/score/mw/com/impl/bindings/lola/proxy_method.h +++ b/score/mw/com/impl/bindings/lola/proxy_method.h @@ -44,12 +44,12 @@ class ProxyMethod : public ProxyMethodBinding /// \brief Allocates storage for the in-arguments of a method call at the given queue position. /// /// See ProxyMethodBinding for details - score::Result> AllocateInArgs(std::size_t queue_position) override; + score::Result> GetInArgsBuffer(std::size_t queue_position) override; /// \brief Allocates storage for the return type of a method call at the given queue position. /// /// See ProxyMethodBinding for details - score::Result> AllocateReturnType(std::size_t queue_position) override; + score::Result> GetReturnValueBuffer(std::size_t queue_position) override; /// \brief Performs the actual method call at the given call-queue position. /// diff --git a/score/mw/com/impl/bindings/lola/proxy_method_handling_test.cpp b/score/mw/com/impl/bindings/lola/proxy_method_handling_test.cpp index a03ce0cd7..0ef3d0ed2 100644 --- a/score/mw/com/impl/bindings/lola/proxy_method_handling_test.cpp +++ b/score/mw/com/impl/bindings/lola/proxy_method_handling_test.cpp @@ -319,7 +319,7 @@ TEST_F(ProxyMethodHandlingFixture, SetsInArgsAndReturnStoragesForEachMethodInShm // can allocate InArgs without crashing, since the allocation is using the inserted storages) for (auto& method : proxy_method_storage_) { - score::cpp::ignore = method.AllocateInArgs(0); + score::cpp::ignore = method.GetInArgsBuffer(0); } } diff --git a/score/mw/com/impl/bindings/lola/proxy_method_test.cpp b/score/mw/com/impl/bindings/lola/proxy_method_test.cpp index 27196564d..1fdb32a38 100644 --- a/score/mw/com/impl/bindings/lola/proxy_method_test.cpp +++ b/score/mw/com/impl/bindings/lola/proxy_method_test.cpp @@ -149,7 +149,7 @@ TEST_F(ProxyMethodAllocateInArgsFixture, CallingWithoutMarkingSubscribedReturnsE unit_->SetInArgsAndReturnStorages(kValidInArgStorage, kEmptyReturnStorage); // When calling AllocateInArgs - const auto result = unit_->AllocateInArgs(kDummyQueuePosition); + const auto result = unit_->GetInArgsBuffer(kDummyQueuePosition); // Then an error is returned EXPECT_FALSE(result.has_value()); @@ -167,7 +167,7 @@ TEST_F(ProxyMethodAllocateInArgsFixture, CallingAfterMarkingSubscribedThenUnsubs unit_->SetInArgsAndReturnStorages(kValidInArgStorage, kEmptyReturnStorage); // When calling AllocateInArgs - const auto result = unit_->AllocateInArgs(kDummyQueuePosition); + const auto result = unit_->GetInArgsBuffer(kDummyQueuePosition); // Then an error is returned EXPECT_FALSE(result.has_value()); @@ -182,7 +182,7 @@ TEST_F(ProxyMethodAllocateInArgsFixture, CallingAfterSettingValidStoragesWithVal unit_->SetInArgsAndReturnStorages(kValidInArgStorage, kEmptyReturnStorage); // When calling AllocateInArgs - const auto result = unit_->AllocateInArgs(kDummyQueuePosition); + const auto result = unit_->GetInArgsBuffer(kDummyQueuePosition); // Then a valid result is returned EXPECT_TRUE(result.has_value()); @@ -197,7 +197,7 @@ TEST_F(ProxyMethodAllocateInArgsFixture, CallingAfterSettingValidInArgsStorageWi // When calling AllocateInArgs // Then the program terminates - SCORE_LANGUAGE_FUTURECPP_EXPECT_CONTRACT_VIOLATED(score::cpp::ignore = unit_->AllocateInArgs(kDummyQueuePosition)); + SCORE_LANGUAGE_FUTURECPP_EXPECT_CONTRACT_VIOLATED(score::cpp::ignore = unit_->GetInArgsBuffer(kDummyQueuePosition)); } TEST_F(ProxyMethodAllocateInArgsFixture, CallingAfterSettingEmptyInArgsStorageWithInArgsTypeInfoTerminates) @@ -209,7 +209,7 @@ TEST_F(ProxyMethodAllocateInArgsFixture, CallingAfterSettingEmptyInArgsStorageWi // When calling AllocateInArgs // Then the program terminates - SCORE_LANGUAGE_FUTURECPP_EXPECT_CONTRACT_VIOLATED(score::cpp::ignore = unit_->AllocateInArgs(kDummyQueuePosition)); + SCORE_LANGUAGE_FUTURECPP_EXPECT_CONTRACT_VIOLATED(score::cpp::ignore = unit_->GetInArgsBuffer(kDummyQueuePosition)); } using ProxyMethodAllocateReturnTypeFixture = ProxyMethodFixture; @@ -222,7 +222,7 @@ TEST_F(ProxyMethodAllocateReturnTypeFixture, CallingWithoutMarkingSubscribedRetu unit_->SetInArgsAndReturnStorages(kEmptyInArgStorage, kValidReturnStorage); // When calling AllocateReturnType - const auto result = unit_->AllocateReturnType(kDummyQueuePosition); + const auto result = unit_->GetReturnValueBuffer(kDummyQueuePosition); // Then an error is returned EXPECT_FALSE(result.has_value()); @@ -240,7 +240,7 @@ TEST_F(ProxyMethodAllocateReturnTypeFixture, CallingAfterMarkingSubscribedThenUn unit_->SetInArgsAndReturnStorages(kEmptyInArgStorage, kValidReturnStorage); // When calling AllocateReturnType - const auto result = unit_->AllocateReturnType(kDummyQueuePosition); + const auto result = unit_->GetReturnValueBuffer(kDummyQueuePosition); // Then an error is returned EXPECT_FALSE(result.has_value()); @@ -255,7 +255,7 @@ TEST_F(ProxyMethodAllocateReturnTypeFixture, CallingAfterSettingValidStoragesWit unit_->SetInArgsAndReturnStorages(kEmptyInArgStorage, kValidReturnStorage); // When calling AllocateReturnType - const auto result = unit_->AllocateReturnType(kDummyQueuePosition); + const auto result = unit_->GetReturnValueBuffer(kDummyQueuePosition); // Then a valid result is returned EXPECT_TRUE(result.has_value()); @@ -271,7 +271,7 @@ TEST_F(ProxyMethodAllocateReturnTypeFixture, CallingAfterSettingValidReturnStora // When calling AllocateReturnType // Then the program terminates SCORE_LANGUAGE_FUTURECPP_EXPECT_CONTRACT_VIOLATED(score::cpp::ignore = - unit_->AllocateReturnType(kDummyQueuePosition)); + unit_->GetReturnValueBuffer(kDummyQueuePosition)); } TEST_F(ProxyMethodAllocateReturnTypeFixture, CallingAfterSettingEmptyReturnStorageWithReturnTypeInfoTerminates) @@ -284,7 +284,7 @@ TEST_F(ProxyMethodAllocateReturnTypeFixture, CallingAfterSettingEmptyReturnStora // When calling AllocateReturnType // Then the program terminates SCORE_LANGUAGE_FUTURECPP_EXPECT_CONTRACT_VIOLATED(score::cpp::ignore = - unit_->AllocateReturnType(kDummyQueuePosition)); + unit_->GetReturnValueBuffer(kDummyQueuePosition)); } using ProxyMethodDoCallFixture = ProxyMethodFixture; diff --git a/score/mw/com/impl/bindings/mock_binding/proxy_method.h b/score/mw/com/impl/bindings/mock_binding/proxy_method.h index 5c194d144..9d990addc 100644 --- a/score/mw/com/impl/bindings/mock_binding/proxy_method.h +++ b/score/mw/com/impl/bindings/mock_binding/proxy_method.h @@ -27,8 +27,8 @@ class ProxyMethod : public ProxyMethodBinding public: ~ProxyMethod() override = default; - MOCK_METHOD(score::Result>, AllocateInArgs, (std::size_t), (override)); - MOCK_METHOD(score::Result>, AllocateReturnType, (std::size_t), (override)); + MOCK_METHOD(score::Result>, GetInArgsBuffer, (std::size_t), (override)); + MOCK_METHOD(score::Result>, GetReturnValueBuffer, (std::size_t), (override)); MOCK_METHOD(ResultBlank, DoCall, (std::size_t), (override)); }; @@ -38,14 +38,14 @@ class ProxyMethodFacade : public ProxyMethodBinding ProxyMethodFacade(ProxyMethod& proxy_method) : ProxyMethodBinding{}, proxy_method_{proxy_method} {} ~ProxyMethodFacade() override = default; - score::Result> AllocateInArgs(std::size_t queue_position) override + score::Result> GetInArgsBuffer(std::size_t queue_position) override { - return proxy_method_.AllocateInArgs(queue_position); + return proxy_method_.GetInArgsBuffer(queue_position); } - score::Result> AllocateReturnType(std::size_t queue_position) override + score::Result> GetReturnValueBuffer(std::size_t queue_position) override { - return proxy_method_.AllocateReturnType(queue_position); + return proxy_method_.GetReturnValueBuffer(queue_position); } score::ResultBlank DoCall(std::size_t queue_position) override diff --git a/score/mw/com/impl/methods/proxy_method.h b/score/mw/com/impl/methods/proxy_method.h index 2fbae9833..74ed22eb5 100644 --- a/score/mw/com/impl/methods/proxy_method.h +++ b/score/mw/com/impl/methods/proxy_method.h @@ -133,7 +133,7 @@ score::Result...>> AllocateImpl( return Unexpected(available_queue_slot.error()); } const std::size_t queue_index = available_queue_slot.value(); - auto allocated_in_args_storage = binding.AllocateInArgs(queue_index); + auto allocated_in_args_storage = binding.GetInArgsBuffer(queue_index); if (!allocated_in_args_storage.has_value()) { return Unexpected(allocated_in_args_storage.error()); @@ -146,6 +146,52 @@ score::Result...>> AllocateImpl( std::move(method_in_arg_ptr_tuple)); } +/// \brief Initializes all InArgs by calling the default constructor for each argument. +/// +/// This step is important to avoid undefined behaviour (interpreting uninitialized memory) and also to ensure that any +/// non-trivially constructible types are properly initialized. +template +ResultBlank InitializeInArgs(ProxyMethodBinding& binding, const std::size_t queue_size) +{ + for (std::size_t queue_index = 0U; queue_index < queue_size; ++queue_index) + { + auto allocated_in_args_storage = binding.GetInArgsBuffer(queue_index); + if (!allocated_in_args_storage.has_value()) + { + return Unexpected(allocated_in_args_storage.error()); + } + const auto deserialized_arg_pointers = impl::Deserialize(allocated_in_args_storage.value()); + + // std::apply takes a callable and a tuple. It calls the callable with the arguments from the unpacked tuple. + // E.g. In this case, it will call the lambda, fn, with: `fn(get<0>(args), get<1>(args), ..., get(args))` + std::apply( + [](typename std::add_pointer::type... arg_pointers) { + ((score::cpp::ignore = new (arg_pointers) ArgTypes{}), ...); + }, + deserialized_arg_pointers); + } + return {}; +} + +/// \brief Initializes all Return values by calling the default constructor for each argument. +/// +/// This step is important to avoid undefined behaviour (interpreting uninitialized memory) and also to ensure that any +/// non-trivially constructible types are properly initialized. +template +ResultBlank InitializeReturnValue(ProxyMethodBinding& binding, const std::size_t queue_size) +{ + for (std::size_t queue_index = 0U; queue_index < queue_size; ++queue_index) + { + auto allocated_return_value_storage = binding.GetReturnValueBuffer(queue_index); + if (!allocated_return_value_storage.has_value()) + { + return Unexpected(allocated_return_value_storage.error()); + } + score::cpp::ignore = new (allocated_return_value_storage->data()) ReturnType{}; + } + return {}; +} + /// \brief Checks, that all MethodInArgPtr arguments have the same queue_position_ and returns this common value. /// \details Will assert/terminate, if the queue_position_ values differ. /// \tparam MethodInArgPtrs Variadic template parameter pack for MethodInArgPtr types. diff --git a/score/mw/com/impl/methods/proxy_method_base.h b/score/mw/com/impl/methods/proxy_method_base.h index c1f3f502f..78534362e 100644 --- a/score/mw/com/impl/methods/proxy_method_base.h +++ b/score/mw/com/impl/methods/proxy_method_base.h @@ -53,6 +53,17 @@ class ProxyMethodBase proxy_base_ = proxy_base; } + /// \brief Default initializes each method InArg and Return value (if they exist) + /// + /// This function is called on creation of a Proxy during ProxyBase::SetupMethods. Since the binding creates a type + /// erased buffer in which the InArgs and Return value are created, each value must be explicitly instantiated to + /// begin the object lifetime and also perform the correct initialization (in case the type cannot be trivially + /// default constructed). We do this once on startup instead of in a call to Allocate() to prevent the type being + /// reinitialized on every method call. This potentially would have performance benefits but more importantly this + /// allows us to support "semi-dynamic" types in which a type dynamically allocates once on construction and the + /// constructor is then never called again. + virtual ResultBlank InitializeInArgsAndReturnValues() = 0; + protected: /// \brief Size of the call-queue is currently fixed to 1! As soon as we are going to support larger call-queues, /// the call-queue-size shall be taken from configuration and handed over to ProxyMethod ctor. diff --git a/score/mw/com/impl/methods/proxy_method_binding.h b/score/mw/com/impl/methods/proxy_method_binding.h index 8d053f287..0cc1fa633 100644 --- a/score/mw/com/impl/methods/proxy_method_binding.h +++ b/score/mw/com/impl/methods/proxy_method_binding.h @@ -38,14 +38,14 @@ class ProxyMethodBinding /// \return span of bytes representing the allocated storage or an error. /// \note If the method has no in-arguments (i.e. in_args_type_erased_info_ is a std::nullopt), this method shall /// not be called. - virtual score::Result> AllocateInArgs(std::size_t queue_position) = 0; + virtual score::Result> GetInArgsBuffer(std::size_t queue_position) = 0; /// \brief Allocates storage for the return type of a method call at the given queue position. /// \param queue_position The call-queue position for which to allocate the return type storage. /// \return span of bytes representing the allocated storage or an error. /// \note If the method has no return type (i.e. void, thus return_type_type_erased_info_ is a std::nullopt), this /// method shall not be called. - virtual score::Result> AllocateReturnType(std::size_t queue_position) = 0; + virtual score::Result> GetReturnValueBuffer(std::size_t queue_position) = 0; /// \brief Performs the actual method call at the given call-queue position. /// \details The in-arguments and return type storage must have been allocated before calling this method. The diff --git a/score/mw/com/impl/methods/proxy_method_test.cpp b/score/mw/com/impl/methods/proxy_method_test.cpp index 4704c1e28..d547b69d5 100644 --- a/score/mw/com/impl/methods/proxy_method_test.cpp +++ b/score/mw/com/impl/methods/proxy_method_test.cpp @@ -57,6 +57,15 @@ class TestProxyBase : public ProxyBase } }; +struct NonTriviallyConstructibleType +{ + NonTriviallyConstructibleType() : value{kInitialValue} {} + + static constexpr std::int32_t kInitialValue{21}; + + std::int32_t value; +}; + template class ProxyMethodTestFixture : public ::testing::Test { @@ -67,10 +76,10 @@ class ProxyMethodTestFixture : public ::testing::Test { ProxyMethodBindingFactory::InjectMockBinding(&proxy_method_binding_factory_mock_); - ON_CALL(proxy_method_binding_mock_, AllocateInArgs(0)) + ON_CALL(proxy_method_binding_mock_, GetInArgsBuffer(0)) .WillByDefault(Return(score::Result>{ score::cpp::span{method_in_args_buffer_.data(), method_in_args_buffer_.size()}})); - ON_CALL(proxy_method_binding_mock_, AllocateReturnType(0)) + ON_CALL(proxy_method_binding_mock_, GetReturnValueBuffer(0)) .WillByDefault(Return(score::Result>{ score::cpp::span{method_return_type_buffer_.data(), method_return_type_buffer_.size()}})); } @@ -115,6 +124,11 @@ using WithInArgs = ::testing::Types; using WithoutInArgs = ::testing::Types; using WithResult = ::testing::Types; +using NonTrivialConstructibleInArgsAndReturn = NonTriviallyConstructibleType(NonTriviallyConstructibleType, + NonTriviallyConstructibleType); +using NonTrivialConstructibleInArgsOnly = void(NonTriviallyConstructibleType, NonTriviallyConstructibleType); +using NonTrivialConstructibleReturnOnly = NonTriviallyConstructibleType(); + template using ProxyMethodAllArgCombinationsTestFixture = ProxyMethodTestFixture; TYPED_TEST_SUITE(ProxyMethodAllArgCombinationsTestFixture, AllArgCombinations, ); @@ -127,15 +141,18 @@ template using ProxyMethodWithoutInArgsTestFixture = ProxyMethodTestFixture; TYPED_TEST_SUITE(ProxyMethodWithoutInArgsTestFixture, WithoutInArgs, ); -template -using ProxyMethodWithResultTestFixture = ProxyMethodTestFixture; -TYPED_TEST_SUITE(ProxyMethodWithResultTestFixture, WithResult, ); - using ProxyMethodWithInArgsAndReturnFixture = ProxyMethodTestFixture; using ProxyMethodWithReturnOnlyFixture = ProxyMethodTestFixture; using ProxyMethodWithNoInArgsOrReturnFixture = ProxyMethodTestFixture; using ProxyMethodWithInArgsOnlyFixture = ProxyMethodTestFixture; +using ProxyMethodWithNonTrivialConstructibleInArgsAndReturnFixture = + ProxyMethodTestFixture; +using ProxyMethodWithNonTrivialConstructibleReturnOnlyFixture = + ProxyMethodTestFixture; +using ProxyMethodWithNonTrivialConstructibleInArgsOnlyFixture = + ProxyMethodTestFixture; + TYPED_TEST(ProxyMethodAllArgCombinationsTestFixture, Construction) { // When constructing a ProxyMethod with all combinations of InArgs / return types @@ -214,12 +231,12 @@ TYPED_TEST(ProxyMethodAllArgCombinationsTestFixture, EXPECT_EQ(registered_method_address, &proxy_method); } -TYPED_TEST(ProxyMethodWithInArgsTestFixture, AllocateInArgs_ReturnsInArgPointersPointingToQueuePositionZero) +TYPED_TEST(ProxyMethodWithInArgsTestFixture, GetInArgsBuffer_ReturnsInArgPointersPointingToQueuePositionZero) { this->GivenAValidProxyMethod(); - // Expecting that AllocateInArgs is called once for queue position 0 on the binding mock - EXPECT_CALL(this->proxy_method_binding_mock_, AllocateInArgs(0U)); + // Expecting that GetInArgsBuffer is called once for queue position 0 on the binding mock + EXPECT_CALL(this->proxy_method_binding_mock_, GetInArgsBuffer(0U)); // When Allocate is called on the ProxyMethod auto method_in_arg_ptr_tuple = this->unit_->Allocate(); @@ -282,7 +299,7 @@ TYPED_TEST( // Then the binding cannot be created and calling AreBindingsValid returns false EXPECT_FALSE(ProxyBaseView{this->proxy_base_}.AreBindingsValid()); } -TYPED_TEST(ProxyMethodWithInArgsTestFixture, AllocateInArgs_ReturnsInArgPointersPointingToInArgsAllocatedByBinding) +TYPED_TEST(ProxyMethodWithInArgsTestFixture, GetInArgsBuffer_ReturnsInArgPointersPointingToInArgsAllocatedByBinding) { auto* const buffer_start_address = &(this->method_in_args_buffer_[0]); const auto method_in_args_buffer_size = std::tuple_sizemethod_in_args_buffer_)>{}; @@ -291,9 +308,9 @@ TYPED_TEST(ProxyMethodWithInArgsTestFixture, AllocateInArgs_ReturnsInArgPointers this->GivenAValidProxyMethod(); - // Expecting that AllocateInArgs is called once for queue position 0 on the binding mock and returns a pointer + // Expecting that GetInArgsBuffer is called once for queue position 0 on the binding mock and returns a pointer // to our buffer - EXPECT_CALL(this->proxy_method_binding_mock_, AllocateInArgs(0U)); + EXPECT_CALL(this->proxy_method_binding_mock_, GetInArgsBuffer(0U)); // When Allocate is called on the ProxyMethod auto method_in_arg_ptr_tuple = this->unit_->Allocate(); @@ -301,7 +318,7 @@ TYPED_TEST(ProxyMethodWithInArgsTestFixture, AllocateInArgs_ReturnsInArgPointers // Then a valid tuple of MethodInArgPtrs is returned ASSERT_TRUE(method_in_arg_ptr_tuple.has_value()); - // and the first MethodInArgPtr points to the start of the buffer returned by the AllocateInArgs + // and the first MethodInArgPtr points to the start of the buffer returned by the GetInArgsBuffer auto& pointer0 = std::get<0>(method_in_arg_ptr_tuple.value()); auto* const pointed_to_address_0 = reinterpret_cast(pointer0.get()); EXPECT_EQ(pointed_to_address_0, buffer_start_address); @@ -321,7 +338,7 @@ TYPED_TEST(ProxyMethodWithInArgsTestFixture, AllocateInArgs_ReturnsInArgPointers EXPECT_LT(pointed_to_address_2, buffer_end_address); } -TYPED_TEST(ProxyMethodWithInArgsTestFixture, AllocateInArgs_QueueFullError) +TYPED_TEST(ProxyMethodWithInArgsTestFixture, GetInArgsBuffer_QueueFullError) { this->GivenAValidProxyMethod(); @@ -338,12 +355,12 @@ TYPED_TEST(ProxyMethodWithInArgsTestFixture, AllocateInArgs_QueueFullError) EXPECT_EQ(method_in_arg_ptr_tuple_2.error(), ComErrc::kCallQueueFull); } -TYPED_TEST(ProxyMethodWithInArgsTestFixture, AllocateInArgs_BindingErrorPropagation) +TYPED_TEST(ProxyMethodWithInArgsTestFixture, GetInArgsBuffer_BindingErrorPropagation) { this->GivenAValidProxyMethod(); - // Expect that AllocateInArgs is called once for queue position 0 on the binding mock and returns an error. - EXPECT_CALL(this->proxy_method_binding_mock_, AllocateInArgs(0U)) + // Expect that GetInArgsBuffer is called once for queue position 0 on the binding mock and returns an error. + EXPECT_CALL(this->proxy_method_binding_mock_, GetInArgsBuffer(0U)) .WillOnce(Return(MakeUnexpected(ComErrc::kBindingFailure))); // When calling Allocate() @@ -369,12 +386,12 @@ TYPED_TEST(ProxyMethodWithInArgsTestFixture, CallOperator_WithCopy) EXPECT_TRUE(call_result.has_value()); } -TYPED_TEST(ProxyMethodWithInArgsTestFixture, CallOperator_WithCopy_AllocateInArgs_BindingErrorPropagation) +TYPED_TEST(ProxyMethodWithInArgsTestFixture, CallOperator_WithCopy_GetInArgsBuffer_BindingErrorPropagation) { this->GivenAValidProxyMethod(); - // Expect that AllocateInArgs is called and returns an error (e.g., method disabled/not subscribed) - EXPECT_CALL(this->proxy_method_binding_mock_, AllocateInArgs(0U)) + // Expect that GetInArgsBuffer is called and returns an error (e.g., method disabled/not subscribed) + EXPECT_CALL(this->proxy_method_binding_mock_, GetInArgsBuffer(0U)) .WillOnce(Return(MakeUnexpected(ComErrc::kBindingFailure))); // When call operator is called with copy arguments (which internally calls Allocate) @@ -477,8 +494,8 @@ TEST_F(ProxyMethodWithInArgsAndReturnFixture, CallOperator_ReturnsReturnTypePoin this->GivenAValidProxyMethod(); - // Expecting that AllocateReturnType will be called on the binding - EXPECT_CALL(this->proxy_method_binding_mock_, AllocateReturnType(0U)); + // Expecting that GetReturnValueBuffer will be called on the binding + EXPECT_CALL(this->proxy_method_binding_mock_, GetReturnValueBuffer(0U)); // When call operator is called on the ProxyMethod auto& proxy_method = *(this->unit_); @@ -493,12 +510,12 @@ TEST_F(ProxyMethodWithInArgsAndReturnFixture, CallOperator_ReturnsReturnTypePoin EXPECT_EQ(pointed_to_address, return_buffer_start_address); } -TEST_F(ProxyMethodWithInArgsAndReturnFixture, AllocateInArgs_BindingErrorPropagation) +TEST_F(ProxyMethodWithInArgsAndReturnFixture, GetInArgsBuffer_BindingErrorPropagation) { this->GivenAValidProxyMethod(); - // Expect that AllocateInArgs is called and returns an error (e.g., method disabled/not subscribed) - EXPECT_CALL(this->proxy_method_binding_mock_, AllocateInArgs(0U)) + // Expect that GetInArgsBuffer is called and returns an error (e.g., method disabled/not subscribed) + EXPECT_CALL(this->proxy_method_binding_mock_, GetInArgsBuffer(0U)) .WillOnce(Return(MakeUnexpected(ComErrc::kBindingFailure))); // When calling Allocate() @@ -509,12 +526,12 @@ TEST_F(ProxyMethodWithInArgsAndReturnFixture, AllocateInArgs_BindingErrorPropaga EXPECT_EQ(allocate_result.error(), ComErrc::kBindingFailure); } -TEST_F(ProxyMethodWithInArgsAndReturnFixture, CallOperator_WithCopy_AllocateInArgs_BindingErrorPropagation) +TEST_F(ProxyMethodWithInArgsAndReturnFixture, CallOperator_WithCopy_GetInArgsBuffer_BindingErrorPropagation) { this->GivenAValidProxyMethod(); - // Expect that AllocateInArgs is called and returns an error (e.g., method disabled/not subscribed) - EXPECT_CALL(this->proxy_method_binding_mock_, AllocateInArgs(0U)) + // Expect that GetInArgsBuffer is called and returns an error (e.g., method disabled/not subscribed) + EXPECT_CALL(this->proxy_method_binding_mock_, GetInArgsBuffer(0U)) .WillOnce(Return(MakeUnexpected(ComErrc::kBindingFailure))); // When call operator is called with copy arguments (which internally calls Allocate) @@ -532,8 +549,8 @@ TEST_F(ProxyMethodWithReturnOnlyFixture, CallOperator_ReturnsReturnTypePointerPo this->GivenAValidProxyMethod(); - // Expecting that AllocateReturnType will be called on the binding - EXPECT_CALL(this->proxy_method_binding_mock_, AllocateReturnType(0U)); + // Expecting that GetReturnValueBuffer will be called on the binding + EXPECT_CALL(this->proxy_method_binding_mock_, GetReturnValueBuffer(0U)); // When call operator is called on the ProxyMethod auto& proxy_method = *(this->unit_); @@ -548,12 +565,12 @@ TEST_F(ProxyMethodWithReturnOnlyFixture, CallOperator_ReturnsReturnTypePointerPo EXPECT_EQ(pointed_to_address, return_buffer_start_address); } -TEST_F(ProxyMethodWithInArgsAndReturnFixture, CallOperator_AllocateReturnType_BindingErrorPropagation) +TEST_F(ProxyMethodWithInArgsAndReturnFixture, CallOperator_GetReturnValueBuffer_BindingErrorPropagation) { this->GivenAValidProxyMethod(); - // Expect that AllocateReturnType is called and returns an error (e.g., method disabled/not subscribed) - EXPECT_CALL(this->proxy_method_binding_mock_, AllocateReturnType(0U)) + // Expect that GetReturnValueBuffer is called and returns an error (e.g., method disabled/not subscribed) + EXPECT_CALL(this->proxy_method_binding_mock_, GetReturnValueBuffer(0U)) .WillOnce(Return(MakeUnexpected(ComErrc::kBindingFailure))); // When call operator is called on the ProxyMethod @@ -565,7 +582,7 @@ TEST_F(ProxyMethodWithInArgsAndReturnFixture, CallOperator_AllocateReturnType_Bi EXPECT_EQ(call_result.error(), ComErrc::kBindingFailure); } -TEST_F(ProxyMethodWithInArgsAndReturnFixture, CallOperator_ZeroCopy_AllocateReturnType_BindingErrorPropagation) +TEST_F(ProxyMethodWithInArgsAndReturnFixture, CallOperator_ZeroCopy_GetReturnValueBuffer_BindingErrorPropagation) { this->GivenAValidProxyMethod(); @@ -574,8 +591,8 @@ TEST_F(ProxyMethodWithInArgsAndReturnFixture, CallOperator_ZeroCopy_AllocateRetu auto method_in_arg_ptr_tuple = proxy_method.Allocate(); ASSERT_TRUE(method_in_arg_ptr_tuple.has_value()); - // Expect that AllocateReturnType is called and returns an error (e.g., method disabled/not subscribed) - EXPECT_CALL(this->proxy_method_binding_mock_, AllocateReturnType(0U)) + // Expect that GetReturnValueBuffer is called and returns an error (e.g., method disabled/not subscribed) + EXPECT_CALL(this->proxy_method_binding_mock_, GetReturnValueBuffer(0U)) .WillOnce(Return(MakeUnexpected(ComErrc::kBindingFailure))); // When calling the call operator with pre-allocated argument pointers @@ -591,12 +608,12 @@ TEST_F(ProxyMethodWithInArgsAndReturnFixture, CallOperator_ZeroCopy_AllocateRetu EXPECT_EQ(call_result.error(), ComErrc::kBindingFailure); } -TEST_F(ProxyMethodWithInArgsAndReturnFixture, CallOperator_DoCallError_AfterSuccessfulAllocateReturnType) +TEST_F(ProxyMethodWithInArgsAndReturnFixture, CallOperator_DoCallError_AfterSuccessfulGetReturnValueBuffer) { this->GivenAValidProxyMethod(); - // Expecting that AllocateReturnType succeeds but DoCall fails - EXPECT_CALL(this->proxy_method_binding_mock_, AllocateReturnType(0U)); + // Expecting that GetReturnValueBuffer succeeds but DoCall fails + EXPECT_CALL(this->proxy_method_binding_mock_, GetReturnValueBuffer(0U)); EXPECT_CALL(this->proxy_method_binding_mock_, DoCall(0U)) .WillOnce(Return(MakeUnexpected(ComErrc::kBindingFailure))); @@ -609,12 +626,12 @@ TEST_F(ProxyMethodWithInArgsAndReturnFixture, CallOperator_DoCallError_AfterSucc EXPECT_EQ(call_result.error(), ComErrc::kBindingFailure); } -TEST_F(ProxyMethodWithReturnOnlyFixture, CallOperator_AllocateReturnType_BindingErrorPropagation) +TEST_F(ProxyMethodWithReturnOnlyFixture, CallOperator_GetReturnValueBuffer_BindingErrorPropagation) { this->GivenAValidProxyMethod(); - // Expect that AllocateReturnType is called and returns an error (e.g., method disabled/not subscribed) - EXPECT_CALL(this->proxy_method_binding_mock_, AllocateReturnType(0U)) + // Expect that GetReturnValueBuffer is called and returns an error (e.g., method disabled/not subscribed) + EXPECT_CALL(this->proxy_method_binding_mock_, GetReturnValueBuffer(0U)) .WillOnce(Return(MakeUnexpected(ComErrc::kBindingFailure))); // When call operator is called on the ProxyMethod @@ -643,12 +660,12 @@ TEST_F(ProxyMethodWithReturnOnlyFixture, CallOperator_QueueFullError) EXPECT_EQ(method_return_type_ptr_2.error(), ComErrc::kCallQueueFull); } -TEST_F(ProxyMethodWithReturnOnlyFixture, CallOperator_DoCallError_AfterSuccessfulAllocateReturnType) +TEST_F(ProxyMethodWithReturnOnlyFixture, CallOperator_DoCallError_AfterSuccessfulGetReturnValueBuffer) { this->GivenAValidProxyMethod(); - // Expecting that AllocateReturnType succeeds but DoCall fails - EXPECT_CALL(this->proxy_method_binding_mock_, AllocateReturnType(0U)); + // Expecting that GetReturnValueBuffer succeeds but DoCall fails + EXPECT_CALL(this->proxy_method_binding_mock_, GetReturnValueBuffer(0U)); EXPECT_CALL(this->proxy_method_binding_mock_, DoCall(0U)) .WillOnce(Return(MakeUnexpected(ComErrc::kBindingFailure))); @@ -804,5 +821,174 @@ TEST(DetermineNextAvailableQueueSlot, DetermineNextAvailableQueueSlotCanFail) EXPECT_EQ(result, MakeUnexpected(ComErrc::kCallQueueFull)); } +TEST_F(ProxyMethodWithNonTrivialConstructibleInArgsAndReturnFixture, InitializeInArgsAndReturnValuesInitializesInArgs) +{ + this->GivenAValidProxyMethod(); + + // When calling InitializeInArgsAndReturnValues + const auto result = this->unit_->InitializeInArgsAndReturnValues(); + + // Then a valid result is returned + EXPECT_TRUE(result.has_value()); + + // and Allocate returns a pointer pointing to an initialized object (i.e. the non-trivial default constructor was + // called, initializing value to NonTriviallyConstructibleType::kInitialValue + auto method_in_arg_ptr_tuple = this->unit_->Allocate(); + ASSERT_TRUE(method_in_arg_ptr_tuple.has_value()); + + auto& method_in_arg_ptr_0 = std::get<0>(method_in_arg_ptr_tuple.value()); + NonTriviallyConstructibleType& in_arg_0{*method_in_arg_ptr_0}; + EXPECT_EQ(in_arg_0.value, NonTriviallyConstructibleType::kInitialValue); + + auto& method_in_arg_ptr_1 = std::get<1>(method_in_arg_ptr_tuple.value()); + NonTriviallyConstructibleType& in_arg_1{*method_in_arg_ptr_1}; + EXPECT_EQ(in_arg_1.value, NonTriviallyConstructibleType::kInitialValue); +} + +TEST_F(ProxyMethodWithNonTrivialConstructibleInArgsAndReturnFixture, + InitializeInArgsAndReturnValuesPropagatesErrorFromGetInArgsBuffer) +{ + this->GivenAValidProxyMethod(); + + // Expect that GetInArgsBuffer is called once on the binding mock and returns an error. + EXPECT_CALL(this->proxy_method_binding_mock_, GetInArgsBuffer(0U)) + .WillOnce(Return(MakeUnexpected(ComErrc::kBindingFailure))); + + // When calling InitializeInArgsAndReturnValues + const auto result = this->unit_->InitializeInArgsAndReturnValues(); + + // Then an error is returned + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ComErrc::kBindingFailure); +} + +TEST_F(ProxyMethodWithNonTrivialConstructibleInArgsAndReturnFixture, + InitializeInArgsAndReturnValuesPropagatesErrorFromGetReturnValueBuffer) +{ + this->GivenAValidProxyMethod(); + + // Expect that GetReturnValueBuffer is called once on the binding mock and returns an error. + EXPECT_CALL(this->proxy_method_binding_mock_, GetReturnValueBuffer(0U)) + .WillOnce(Return(MakeUnexpected(ComErrc::kBindingFailure))); + + // When calling InitializeInArgsAndReturnValues + const auto result = this->unit_->InitializeInArgsAndReturnValues(); + + // Then an error is returned + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ComErrc::kBindingFailure); +} + +TEST_F(ProxyMethodWithNonTrivialConstructibleInArgsAndReturnFixture, + CallOperator_ZeroCopy_InitializeInArgsAndReturnValuesInitializesInReturn) +{ + this->GivenAValidProxyMethod(); + + // When calling InitializeInArgsAndReturnValues + this->unit_->InitializeInArgsAndReturnValues(); + + // Then the zero copy call operator returns a pointer pointing to an initialized object (i.e. the non-trivial + // default constructor was called, initializing value to NonTriviallyConstructibleType::kInitialValue + auto method_in_arg_ptr_tuple = this->unit_->Allocate(); + ASSERT_TRUE(method_in_arg_ptr_tuple.has_value()); + + auto& method_in_arg_ptr_0 = std::get<0>(method_in_arg_ptr_tuple.value()); + auto& method_in_arg_ptr_1 = std::get<1>(method_in_arg_ptr_tuple.value()); + auto method_return_ptr = this->unit_->operator()(std::move(method_in_arg_ptr_0), std::move(method_in_arg_ptr_1)); + ASSERT_TRUE(method_return_ptr.has_value()); + + NonTriviallyConstructibleType& return_value{*(method_return_ptr.value())}; + EXPECT_EQ(return_value.value, NonTriviallyConstructibleType::kInitialValue); +} + +TEST_F(ProxyMethodWithNonTrivialConstructibleInArgsAndReturnFixture, + CallOperator_WithCopy_InitializeInArgsAndReturnValuesInitializesInReturn) +{ + this->GivenAValidProxyMethod(); + + // When calling InitializeInArgsAndReturnValues + this->unit_->InitializeInArgsAndReturnValues(); + + // Then the copy call operator returns a pointer pointing to an initialized object (i.e. the non-trivial + // default constructor was called, initializing value to NonTriviallyConstructibleType::kInitialValue + auto method_return_ptr = this->unit_->operator()(NonTriviallyConstructibleType{}, NonTriviallyConstructibleType{}); + ASSERT_TRUE(method_return_ptr.has_value()); + + NonTriviallyConstructibleType& return_value{*(method_return_ptr.value())}; + EXPECT_EQ(return_value.value, NonTriviallyConstructibleType::kInitialValue); +} + +TEST_F(ProxyMethodWithNonTrivialConstructibleInArgsOnlyFixture, InitializeInArgsAndReturnValuesInitializesInArgs) +{ + this->GivenAValidProxyMethod(); + + // When calling InitializeInArgsAndReturnValues + this->unit_->InitializeInArgsAndReturnValues(); + + // Then Allocate returns a pointer pointing to an initialized object (i.e. the non-trivial default constructor was + // called, initializing value to NonTriviallyConstructibleType::kInitialValue + auto method_in_arg_ptr_tuple = this->unit_->Allocate(); + ASSERT_TRUE(method_in_arg_ptr_tuple.has_value()); + + auto& method_in_arg_ptr_0 = std::get<0>(method_in_arg_ptr_tuple.value()); + NonTriviallyConstructibleType& in_arg_0{*method_in_arg_ptr_0}; + EXPECT_EQ(in_arg_0.value, NonTriviallyConstructibleType::kInitialValue); + + auto& method_in_arg_ptr_1 = std::get<1>(method_in_arg_ptr_tuple.value()); + NonTriviallyConstructibleType& in_arg_1{*method_in_arg_ptr_1}; + EXPECT_EQ(in_arg_1.value, NonTriviallyConstructibleType::kInitialValue); +} + +TEST_F(ProxyMethodWithNonTrivialConstructibleInArgsOnlyFixture, + InitializeInArgsAndReturnValuesPropagatesErrorFromBinding) +{ + this->GivenAValidProxyMethod(); + + // Expect that GetInArgsBuffer is called once on the binding mock and returns an error. + EXPECT_CALL(this->proxy_method_binding_mock_, GetInArgsBuffer(0U)) + .WillOnce(Return(MakeUnexpected(ComErrc::kBindingFailure))); + + // When calling InitializeInArgsAndReturnValues + const auto result = this->unit_->InitializeInArgsAndReturnValues(); + + // Then an error is returned + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ComErrc::kBindingFailure); +} + +TEST_F(ProxyMethodWithNonTrivialConstructibleReturnOnlyFixture, + CallOperator_WithCopy_InitializeInArgsAndReturnValuesInitializesInReturn) +{ + this->GivenAValidProxyMethod(); + + // When calling InitializeInArgsAndReturnValues + this->unit_->InitializeInArgsAndReturnValues(); + + // Then the copy call operator returns a pointer pointing to an initialized object (i.e. the non-trivial + // default constructor was called, initializing value to NonTriviallyConstructibleType::kInitialValue + auto method_return_ptr = this->unit_->operator()(); + ASSERT_TRUE(method_return_ptr.has_value()); + + NonTriviallyConstructibleType& return_value{*(method_return_ptr.value())}; + EXPECT_EQ(return_value.value, NonTriviallyConstructibleType::kInitialValue); +} + +TEST_F(ProxyMethodWithNonTrivialConstructibleReturnOnlyFixture, + InitializeInArgsAndReturnValuesPropagatesErrorFromBinding) +{ + this->GivenAValidProxyMethod(); + + // Expect that GetReturnValueBuffer is called once on the binding mock and returns an error. + EXPECT_CALL(this->proxy_method_binding_mock_, GetReturnValueBuffer(0U)) + .WillOnce(Return(MakeUnexpected(ComErrc::kBindingFailure))); + + // When calling InitializeInArgsAndReturnValues + const auto result = this->unit_->InitializeInArgsAndReturnValues(); + + // Then an error is returned + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), ComErrc::kBindingFailure); +} + } // namespace } // namespace score::mw::com::impl diff --git a/score/mw/com/impl/methods/proxy_method_with_in_args.h b/score/mw/com/impl/methods/proxy_method_with_in_args.h index 198cb16c7..8b42520ae 100644 --- a/score/mw/com/impl/methods/proxy_method_with_in_args.h +++ b/score/mw/com/impl/methods/proxy_method_with_in_args.h @@ -83,6 +83,8 @@ class ProxyMethod final : public ProxyMethodBase ProxyMethod(ProxyMethod&&) noexcept; ProxyMethod& operator=(ProxyMethod&&) noexcept; + ResultBlank InitializeInArgsAndReturnValues() override; + /// \brief Allocates the necessary storage for the argument values and the return value of a method call. /// \return On success, a tuple of MethodInArgPtr for each argument type is returned. On failure, an error code is /// returned. @@ -177,6 +179,18 @@ score::ResultBlank ProxyMethod::operator()(MethodInArgPtr +ResultBlank ProxyMethod::InitializeInArgsAndReturnValues() +{ + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(binding_ != nullptr); + const auto init_in_args_result = detail::InitializeInArgs(*binding_, kCallQueueSize); + if (!init_in_args_result.has_value()) + { + return Unexpected(init_in_args_result.error()); + } + return {}; +} + } // namespace score::mw::com::impl #endif // SCORE_MW_COM_IMPL_METHODS_PROXY_METHOD_WITH_IN_ARGS_H diff --git a/score/mw/com/impl/methods/proxy_method_with_in_args_and_return.h b/score/mw/com/impl/methods/proxy_method_with_in_args_and_return.h index 6bec0ff22..aea5b2010 100644 --- a/score/mw/com/impl/methods/proxy_method_with_in_args_and_return.h +++ b/score/mw/com/impl/methods/proxy_method_with_in_args_and_return.h @@ -95,6 +95,8 @@ class ProxyMethod final : public ProxyMethodBase ProxyMethod(ProxyMethod&&) noexcept; ProxyMethod& operator=(ProxyMethod&&) noexcept; + ResultBlank InitializeInArgsAndReturnValues() override; + /// \brief Allocates the necessary storage for the argument values and the return value of a method call. /// \return On success, a tuple of MethodInArgPtr for each argument type is returned. On failure, an error code is /// returned. @@ -186,7 +188,7 @@ score::Result> ProxyMethod... args) { auto queue_position = detail::GetCommonQueuePosition(args...); - auto allocated_return_type_storage = binding_->AllocateReturnType(queue_position); + auto allocated_return_type_storage = binding_->GetReturnValueBuffer(queue_position); if (!allocated_return_type_storage.has_value()) { return Unexpected(allocated_return_type_storage.error()); @@ -214,6 +216,24 @@ score::Result> ProxyMethod +ResultBlank ProxyMethod::InitializeInArgsAndReturnValues() +{ + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(binding_ != nullptr); + const auto init_in_args_result = detail::InitializeInArgs(*binding_, kCallQueueSize); + if (!init_in_args_result.has_value()) + { + return Unexpected(init_in_args_result.error()); + } + + const auto init_return_result = detail::InitializeReturnValue(*binding_, kCallQueueSize); + if (!init_return_result.has_value()) + { + return Unexpected(init_return_result.error()); + } + return {}; +} + } // namespace score::mw::com::impl #endif // SCORE_MW_COM_IMPL_METHODS_PROXY_METHOD_WITH_IN_ARGS_AND_RETURN_H diff --git a/score/mw/com/impl/methods/proxy_method_with_return_type.h b/score/mw/com/impl/methods/proxy_method_with_return_type.h index dc60e7936..a4084c6a8 100644 --- a/score/mw/com/impl/methods/proxy_method_with_return_type.h +++ b/score/mw/com/impl/methods/proxy_method_with_return_type.h @@ -87,6 +87,8 @@ class ProxyMethod final : public ProxyMethodBase ProxyMethod(ProxyMethod&&) noexcept; ProxyMethod& operator=(ProxyMethod&&) noexcept; + ResultBlank InitializeInArgsAndReturnValues() override; + /// \brief This is the call-operator of ProxyMethod with no arguments for a non-void ReturnType. score::Result> operator()(); @@ -135,7 +137,7 @@ score::Result> ProxyMethod::operat } const auto queue_position = queue_position_result.value(); - auto allocated_return_type_storage = binding_->AllocateReturnType(queue_position); + auto allocated_return_type_storage = binding_->GetReturnValueBuffer(queue_position); if (!allocated_return_type_storage.has_value()) { return Unexpected(allocated_return_type_storage.error()); @@ -163,6 +165,18 @@ score::Result> ProxyMethod::operat queue_position}; } +template +ResultBlank ProxyMethod::InitializeInArgsAndReturnValues() +{ + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(binding_ != nullptr); + const auto init_return_result = detail::InitializeReturnValue(*binding_, kCallQueueSize); + if (!init_return_result.has_value()) + { + return Unexpected(init_return_result.error()); + } + return {}; +} + } // namespace score::mw::com::impl #endif // SCORE_MW_COM_IMPL_METHODS_PROXY_METHOD_WITH_RETURN_TYPE_H diff --git a/score/mw/com/impl/methods/proxy_method_without_in_args_or_return.h b/score/mw/com/impl/methods/proxy_method_without_in_args_or_return.h index 821ed2149..41851a4bb 100644 --- a/score/mw/com/impl/methods/proxy_method_without_in_args_or_return.h +++ b/score/mw/com/impl/methods/proxy_method_without_in_args_or_return.h @@ -83,6 +83,11 @@ class ProxyMethod final : public ProxyMethodBase ProxyMethod(ProxyMethod&&) noexcept; ProxyMethod& operator=(ProxyMethod&&) noexcept; + ResultBlank InitializeInArgsAndReturnValues() override + { + return {}; + } + /// \brief This is the call-operator of ProxyMethod with no arguments and a void ReturnType. score::ResultBlank operator()(); diff --git a/score/mw/com/impl/proxy_base.cpp b/score/mw/com/impl/proxy_base.cpp index 79765790e..6a96f433c 100644 --- a/score/mw/com/impl/proxy_base.cpp +++ b/score/mw/com/impl/proxy_base.cpp @@ -158,6 +158,22 @@ score::ResultBlank ProxyBase::StopFindService(const FindServiceHandle handle) no return stop_find_service_result; } +ResultBlank ProxyBase::SetupMethods(const std::vector& enabled_method_names) +{ + const auto result = proxy_binding_->SetupMethods(enabled_method_names); + if (!result.has_value()) + { + return MakeUnexpected(result.error()); + } + + for (auto& method_key_value_pair : methods_) + { + auto& method = method_key_value_pair.second.get(); + method.InitializeInArgsAndReturnValues(); + } + return {}; +} + ProxyBaseView::ProxyBaseView(ProxyBase& proxy_base) noexcept : proxy_base_(proxy_base) {} ProxyBinding* ProxyBaseView::GetBinding() noexcept diff --git a/score/mw/com/impl/proxy_base.h b/score/mw/com/impl/proxy_base.h index c15fd44f8..f970e1515 100644 --- a/score/mw/com/impl/proxy_base.h +++ b/score/mw/com/impl/proxy_base.h @@ -137,10 +137,7 @@ class ProxyBase return is_proxy_binding_valid && are_service_element_bindings_valid_; } - ResultBlank SetupMethods(const std::vector& enabled_method_names) - { - return proxy_binding_->SetupMethods(enabled_method_names); - } + ResultBlank SetupMethods(const std::vector& enabled_method_names); // Suppress "AUTOSAR C++14 M11-0-1" rule findings. This rule states: "Member data in non-POD class types shall // be private.". We need these data elements to exchange this information between the ProxyBase and the diff --git a/score/mw/com/impl/proxy_base_test.cpp b/score/mw/com/impl/proxy_base_test.cpp index 33b389be5..3cef38922 100644 --- a/score/mw/com/impl/proxy_base_test.cpp +++ b/score/mw/com/impl/proxy_base_test.cpp @@ -555,6 +555,19 @@ class MyProxy : public ProxyBase } }; +// Since ProxyMethodBase is an abstract class, we create a dummy ProxyMethod which has a dummy implementation of the +// pure virtual method which is not required in these tests. We use this class instead of a real ProxyMethod since we +// want to explicitly call RegisterMethod in the tests, and ProxyMethod already calls this on construction. +class DummyProxyMethod : public ProxyMethodBase +{ + public: + using ProxyMethodBase::ProxyMethodBase; + ResultBlank InitializeInArgsAndReturnValues() override + { + return {}; + } +}; + /// Note. Technically, these tests are testing internals of ProxyBase. While we generally strive to test only the public /// interface, we make an exception in this case since the reference updating of service elements is complex and can /// lead to dangling references if not done correctly, which can be hard to test using the public interface alone. @@ -599,8 +612,8 @@ class ProxyBaseServiceElementReferencesFixture : public ::testing::Test ProxyFieldBase field_0_{proxy_, &field_event_dispatch_0_, field_name_0_}; ProxyFieldBase field_1_{proxy_, &field_event_dispatch_1_, field_name_1_}; - ProxyMethodBase method_0_{proxy_, std::make_unique(), method_name_0_}; - ProxyMethodBase method_1_{proxy_, std::make_unique(), method_name_1_}; + DummyProxyMethod method_0_{proxy_, std::make_unique(), method_name_0_}; + DummyProxyMethod method_1_{proxy_, std::make_unique(), method_name_1_}; }; TEST_F(ProxyBaseServiceElementReferencesFixture, RegisteringServiceElementStoresReferenceInMap) @@ -698,7 +711,7 @@ TEST_F(ProxyBaseServiceElementReferencesFixture, MoveAssigningUpdatesReferencesT ProxyEventBase field_event_dispatch{ proxy_2, &proxy_binding_mock, std::make_unique(), other_field_name}; ProxyFieldBase field{proxy_2, &field_event_dispatch, other_field_name}; - ProxyMethodBase method{proxy_2, std::make_unique(), other_method_name}; + DummyProxyMethod method{proxy_2, std::make_unique(), other_method_name}; ProxyBaseView{proxy_2}.RegisterEvent(other_event_name, event); ProxyBaseView{proxy_2}.RegisterField(other_field_name, field); ProxyBaseView{proxy_2}.RegisterMethod(other_method_name, method); diff --git a/score/mw/com/test/methods/methods_test_resources/BUILD b/score/mw/com/test/methods/methods_test_resources/BUILD index ba8105cdc..75fc52816 100644 --- a/score/mw/com/test/methods/methods_test_resources/BUILD +++ b/score/mw/com/test/methods/methods_test_resources/BUILD @@ -26,6 +26,19 @@ cc_library( ], ) +cc_library( + name = "skeleton_container", + srcs = ["skeleton_container.cpp"], + hdrs = ["skeleton_container.h"], + features = COMPILER_WARNING_FEATURES, + visibility = [ + "//score/mw/com/test/methods:__subpackages__", + ], + deps = [ + "//score/mw/com", + ], +) + cc_library( name = "method_provider", srcs = ["method_provider.cpp"], diff --git a/score/mw/com/test/methods/methods_test_resources/skeleton_container.cpp b/score/mw/com/test/methods/methods_test_resources/skeleton_container.cpp new file mode 100644 index 000000000..70f7eca86 --- /dev/null +++ b/score/mw/com/test/methods/methods_test_resources/skeleton_container.cpp @@ -0,0 +1,13 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/com/test/methods/methods_test_resources/skeleton_container.h" diff --git a/score/mw/com/test/methods/methods_test_resources/skeleton_container.h b/score/mw/com/test/methods/methods_test_resources/skeleton_container.h new file mode 100644 index 000000000..31d6f1922 --- /dev/null +++ b/score/mw/com/test/methods/methods_test_resources/skeleton_container.h @@ -0,0 +1,89 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#ifndef SCORE_MW_COM_TEST_METHODS_METHODS_TEST_RESOURCES_SKELETON_CONTAINER_H +#define SCORE_MW_COM_TEST_METHODS_METHODS_TEST_RESOURCES_SKELETON_CONTAINER_H + +#include "score/mw/com/types.h" + +#include +#include + +namespace score::mw::com::test +{ + +template +class SkeletonContainer +{ + public: + SkeletonContainer(std::string instance_specifier_string); + + bool CreateSkeleton(); + bool OfferService(); + + Skeleton& GetSkeleton() + { + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(skeleton_ != nullptr, + "Skeleton was not successfully created! Cannot get it!"); + return *skeleton_; + } + + private: + std::string instance_specifier_string_; + std::unique_ptr skeleton_; +}; + +template +SkeletonContainer::SkeletonContainer(std::string instance_specifier_string) + : instance_specifier_string_{std::move(instance_specifier_string)} +{ +} + +template +bool SkeletonContainer::CreateSkeleton() +{ + auto instance_specifier = InstanceSpecifier::Create(std::string{instance_specifier_string_}); + if (!instance_specifier.has_value()) + { + std::cerr << "Provider: Could not create InstanceSpecifier from: " << instance_specifier_string_ << std::endl; + return false; + } + + auto skeleton_result = Skeleton::Create(instance_specifier.value()); + if (!skeleton_result.has_value()) + { + std::cerr << "Provider: Could not create skeleton: " << skeleton_result.error() << std::endl; + return false; + } + skeleton_ = std::make_unique(std::move(skeleton_result).value()); + return true; +} + +template +bool SkeletonContainer::OfferService() +{ + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(skeleton_ != nullptr); + + auto offer_result = skeleton_->OfferService(); + if (!(offer_result.has_value())) + { + std::cerr << "Provider: Could not offer service: " << offer_result.error() << std::endl; + return false; + } + + std::cout << "Provider: Service offered successfully" << std::endl; + return true; +} + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_METHODS_METHODS_TEST_RESOURCES_SKELETON_CONTAINER_H diff --git a/score/mw/com/test/methods/non_trivial_constructors/BUILD b/score/mw/com/test/methods/non_trivial_constructors/BUILD new file mode 100644 index 000000000..b4e596360 --- /dev/null +++ b/score/mw/com/test/methods/non_trivial_constructors/BUILD @@ -0,0 +1,117 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") +load("//bazel/tools:json_schema_validator.bzl", "validate_json_schema_test") +load("//score/mw/com/test:pkg_application.bzl", "pkg_application") + +validate_json_schema_test( + name = "validate_config_schema", + json = "config/mw_com_config.json", + schema = "//score/mw/com:config_schema", + tags = ["lint"], +) + +cc_library( + name = "test_method_datatype", + srcs = ["test_method_datatype.cpp"], + hdrs = ["test_method_datatype.h"], + features = COMPILER_WARNING_FEATURES, + deps = [ + "//score/mw/com", + ], +) + +cc_library( + name = "consumer", + srcs = ["consumer.cpp"], + hdrs = ["consumer.h"], + features = COMPILER_WARNING_FEATURES, + implementation_deps = [ + ":test_method_datatype", + "//score/mw/com", + "//score/mw/com/test/methods/methods_test_resources:process_synchronizer", + "//score/mw/com/test/methods/methods_test_resources:proxy_container", + ], + deps = [ + "@score_baselibs//score/language/futurecpp", + ], +) + +cc_library( + name = "provider", + srcs = ["provider.cpp"], + hdrs = ["provider.h"], + features = COMPILER_WARNING_FEATURES, + deps = [ + ":test_method_datatype", + "//score/mw/com", + "//score/mw/com/test/methods/methods_test_resources:process_synchronizer", + "//score/mw/com/test/methods/methods_test_resources:skeleton_container", + ], +) + +cc_binary( + name = "main_provider", + srcs = ["main_provider.cpp"], + data = ["config/mw_com_config.json"], + features = COMPILER_WARNING_FEATURES + [ + "aborts_upon_exception", + ], + deps = [ + ":provider", + "//score/mw/com", + "//score/mw/com/test/common_test_resources:assert_handler", + "//score/mw/com/test/common_test_resources:stop_token_sig_term_handler", + ], +) + +cc_binary( + name = "main_consumer", + srcs = ["main_consumer.cpp"], + data = ["config/mw_com_config.json"], + features = COMPILER_WARNING_FEATURES + [ + "aborts_upon_exception", + ], + deps = [ + ":consumer", + "//score/mw/com", + "//score/mw/com/test/common_test_resources:assert_handler", + ], +) + +pkg_application( + name = "main_provider-pkg", + app_name = "MainProviderApp", + bin = [":main_provider"], + etc = [ + "config/mw_com_config.json", + "config/logging.json", + ], + visibility = [ + "//score/mw/com/test/methods/non_trivial_constructors:__subpackages__", + ], +) + +pkg_application( + name = "main_consumer-pkg", + app_name = "MainConsumerApp", + bin = [":main_consumer"], + etc = [ + "config/mw_com_config.json", + "config/logging.json", + ], + visibility = [ + "//score/mw/com/test/methods/non_trivial_constructors:__subpackages__", + ], +) diff --git a/score/mw/com/test/methods/non_trivial_constructors/config/logging.json b/score/mw/com/test/methods/non_trivial_constructors/config/logging.json new file mode 100644 index 000000000..c8ff1b069 --- /dev/null +++ b/score/mw/com/test/methods/non_trivial_constructors/config/logging.json @@ -0,0 +1,7 @@ +{ + "appId": "SV", + "appDesc": "signature_variations", + "logLevel": "kDebug", + "logLevelThresholdConsole": "kInfo", + "logMode": "kRemote|kConsole" +} diff --git a/score/mw/com/test/methods/non_trivial_constructors/config/mw_com_config.json b/score/mw/com/test/methods/non_trivial_constructors/config/mw_com_config.json new file mode 100644 index 000000000..27b0838b2 --- /dev/null +++ b/score/mw/com/test/methods/non_trivial_constructors/config/mw_com_config.json @@ -0,0 +1,86 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/test/methods/non_trivial_constructors/MethodSignature", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 2000, + "methods": [ + { + "methodName": "with_in_args_and_return", + "methodId": 1 + }, + { + "methodName": "with_in_args_only", + "methodId": 2 + }, + { + "methodName": "with_return_only", + "methodId": 3 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "test/methods/non_trivial_constructors/MethodSignature", + "serviceTypeName": "/test/methods/non_trivial_constructors/MethodSignature", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "asil-level": "QM", + "binding": "SHM", + "methods": [ + { + "methodName": "with_in_args_and_return", + "queueSize": 1 + }, + { + "methodName": "with_in_args_only", + "queueSize": 1 + }, + { + "methodName": "with_return_only", + "queueSize": 1 + } + ], + "allowedConsumer": { + "QM": [ + 0 + ,1263030 + ], + "B": [ + 0 + ,1263030 + ] + }, + "allowedProvider": { + "QM": [ + 0 + ,1263030 + ], + "B": [ + 0 + ,1263030 + ] + } + + } + ] + } + ], + "global": { + "asil-level": "QM" + } +} diff --git a/score/mw/com/test/methods/non_trivial_constructors/consumer.cpp b/score/mw/com/test/methods/non_trivial_constructors/consumer.cpp new file mode 100644 index 000000000..d799d5a1c --- /dev/null +++ b/score/mw/com/test/methods/non_trivial_constructors/consumer.cpp @@ -0,0 +1,172 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/com/test/methods/non_trivial_constructors/consumer.h" + +#include "score/mw/com/test/methods/methods_test_resources/process_synchronizer.h" +#include "score/mw/com/test/methods/methods_test_resources/proxy_container.h" +#include "score/mw/com/test/methods/non_trivial_constructors/test_method_datatype.h" +#include "score/mw/com/types.h" + +#include + +#include +#include +#include + +namespace score::mw::com::test +{ +namespace +{ + +const std::string kInterprocessNotificationShmPath{"/non_trivial_constructors_test_interprocess_notification"}; + +const InstanceSpecifier kInstanceSpecifier = + InstanceSpecifier::Create(std::string{"test/methods/non_trivial_constructors/MethodSignature"}).value(); + +bool CallMethodWithInArgsAndReturn(NonTrivialConstructorProxy& proxy) +{ + auto call_result = [&proxy]() -> score::Result> { + std::cout << "\n=== Test: with_in_args_and_return (zero-copy) ===" << std::endl; + auto allocated_args_result = proxy.with_in_args_and_return.Allocate(); + if (!allocated_args_result.has_value()) + { + std::cerr << "Consumer: Could not allocate method args: " << allocated_args_result.error() << std::endl; + return Unexpected(allocated_args_result.error()); + } + + auto& [arg1_ptr, arg2_ptr] = allocated_args_result.value(); + return proxy.with_in_args_and_return(std::move(arg1_ptr), std::move(arg2_ptr)); + }(); + if (!call_result.has_value()) + { + std::cerr << "Consumer: with_in_args_and_return call failed: " << call_result.error() << std::endl; + return false; + } + const auto return_value = *(call_result.value()); + + // Since provider adds the two input args which are both initialized with kInitialValue + const auto expected_return_value = NonTriviallyConstructibleType{} + NonTriviallyConstructibleType{}; + if (return_value != expected_return_value) + { + std::cerr << "Consumer: Expected " << expected_return_value << " but got " << return_value << std::endl; + return false; + } + + std::cout << "Consumer: with_in_args_and_return returned correct result: " << return_value << std::endl; + return true; +} + +bool CallMethodWithInArgsOnly(NonTrivialConstructorProxy& proxy) +{ + auto call_result = [&proxy]() -> ResultBlank { + std::cout << "\n=== Test: with_in_args_only (zero-copy) ===" << std::endl; + auto allocated_args_result = proxy.with_in_args_only.Allocate(); + if (!allocated_args_result.has_value()) + { + std::cerr << "Consumer: Could not allocate method args: " << allocated_args_result.error() << std::endl; + return Unexpected(allocated_args_result.error()); + } + + auto& [arg1_ptr, arg2_ptr] = allocated_args_result.value(); + + return proxy.with_in_args_only(std::move(arg1_ptr), std::move(arg2_ptr)); + }(); + if (!call_result.has_value()) + { + std::cerr << "Consumer: with_in_args_only call failed: " << call_result.error() << std::endl; + return false; + } + + std::cout << "Consumer: with_in_args_only returned without error" << std::endl; + return true; +} + +bool CallMethodWithReturnOnly(NonTrivialConstructorProxy& proxy) +{ + std::cout << "\n=== Test: with_return_only (copy call) ===" << std::endl; + const auto call_result = proxy.with_return_only(); + if (!call_result.has_value()) + { + std::cerr << "Consumer: with_return_only call failed: " << call_result.error() << std::endl; + return false; + } + const auto return_value = *(call_result.value()); + + const auto expected_return_value = NonTriviallyConstructibleType{}; + if (return_value != expected_return_value) + { + std::cerr << "Consumer: Expected " << expected_return_value << " but got " << return_value << std::endl; + return false; + } + + std::cout << "Consumer: with_return_only returned correct result: " << return_value << std::endl; + return true; +} + +} // namespace + +int run_consumer() +{ + auto process_synchronizer_result = ProcessSynchronizer::Create(kInterprocessNotificationShmPath); + if (!(process_synchronizer_result.has_value())) + { + std::cerr << "Methods non_trivial_constructors consumer failed: Could not create ProcessSynchronizer" + << std::endl; + return EXIT_FAILURE; + } + + ProxyContainer consumer{}; + + // Step 1. Find service and create proxy + std::cout << "\nConsumer: Step 1" << std::endl; + if (!consumer.CreateProxy(kInstanceSpecifier, {"with_in_args_and_return", "with_in_args_only", "with_return_only"})) + { + std::cerr << "Methods non_trivial_constructors consumer failed: CreateProxy" << std::endl; + process_synchronizer_result->Notify(); + return EXIT_FAILURE; + } + + // Step 2. Call zero-copy method with InArgs and Return + std::cout << "\nConsumer: Step 2" << std::endl; + if (!CallMethodWithInArgsAndReturn(consumer.GetProxy())) + { + std::cerr << "Methods non_trivial_constructors consumer failed: CallMethodWithInArgsAndReturnZeroCopy" + << std::endl; + process_synchronizer_result->Notify(); + return EXIT_FAILURE; + } + + // Step 3. Call zero-copy method with InArgs only + std::cout << "\nConsumer: Step 3" << std::endl; + if (!CallMethodWithInArgsOnly(consumer.GetProxy())) + { + std::cerr << "Methods non_trivial_constructors consumer failed: CallMethodWithInArgsOnlyZeroCopy" << std::endl; + process_synchronizer_result->Notify(); + return EXIT_FAILURE; + } + + // Step 4. Call method with return only with copy + std::cout << "\nConsumer: Step 4" << std::endl; + if (!CallMethodWithReturnOnly(consumer.GetProxy())) + { + std::cerr << "Methods non_trivial_constructors consumer failed: CallMethodWithReturnOnly" << std::endl; + process_synchronizer_result->Notify(); + return EXIT_FAILURE; + } + + process_synchronizer_result->Notify(); + + return EXIT_SUCCESS; +} + +} // namespace score::mw::com::test diff --git a/score/mw/com/test/methods/non_trivial_constructors/consumer.h b/score/mw/com/test/methods/non_trivial_constructors/consumer.h new file mode 100644 index 000000000..2e29d24fb --- /dev/null +++ b/score/mw/com/test/methods/non_trivial_constructors/consumer.h @@ -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 + ********************************************************************************/ +#ifndef SCORE_MW_COM_TEST_METHODS_NON_TRIVIAL_CONSTRUCTORS_CONSUMER_H +#define SCORE_MW_COM_TEST_METHODS_NON_TRIVIAL_CONSTRUCTORS_CONSUMER_H + +namespace score::mw::com::test +{ + +int run_consumer(); + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_METHODS_NON_TRIVIAL_CONSTRUCTORS_CONSUMER_H diff --git a/score/mw/com/test/methods/non_trivial_constructors/integration_test/BUILD b/score/mw/com/test/methods/non_trivial_constructors/integration_test/BUILD new file mode 100644 index 000000000..1cb40e7cc --- /dev/null +++ b/score/mw/com/test/methods/non_trivial_constructors/integration_test/BUILD @@ -0,0 +1,38 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@rules_pkg//pkg:tar.bzl", "pkg_tar") +load("//quality/integration_testing:integration_testing.bzl", "integration_test") + +pkg_tar( + name = "filesystem", + deps = [ + "//score/mw/com/test/methods/non_trivial_constructors:main_consumer-pkg", + "//score/mw/com/test/methods/non_trivial_constructors:main_provider-pkg", + ], +) + +integration_test( + name = "non_trivial_constructors_test", + srcs = [ + "non_trivial_constructors_test.py", + ], + filesystem = ":filesystem", +) + +test_suite( + name = "component_tests", + tests = [ + ":non_trivial_constructors_test", + ], +) diff --git a/score/mw/com/test/methods/non_trivial_constructors/integration_test/non_trivial_constructors_test.py b/score/mw/com/test/methods/non_trivial_constructors/integration_test/non_trivial_constructors_test.py new file mode 100644 index 000000000..c2fa7ebcb --- /dev/null +++ b/score/mw/com/test/methods/non_trivial_constructors/integration_test/non_trivial_constructors_test.py @@ -0,0 +1,34 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +def test_signature_variations(sut): + """ + Test method call functionality for different method signature variations between provider and consumer which are run in different processes. Each method signature contains a type which is not trivially constructible. + + The test starts two processes. + The first process creates a Skeleton instance which contains three methods: + - Method with InArg and Return + - Method with InArg only + - Method with Return only + This process checks that the service can be offered and handlers were successfully registered for all four methods. + This registered handlers for the method with InArg only checks that it is called with the expected arguments from the Proxy side. + + The second process creates a Proxy instance which subscribes to the Skeleton in the first process. + This process calls a zero-copy (where possible) and with-copy method call for each of the four methods and verifies that all calls succeed and return the expected value. + + Test is successful if all previous checks return true and we have no crashes. + """ + with sut.start_process("./bin/main_provider", cwd="/opt/MainProviderApp/") as sender: + with sut.start_process("./bin/main_consumer", cwd="/opt/MainConsumerApp/") as receiver: + assert receiver.wait_for_exit(timeout=120) == 0 + assert sender.wait_for_exit(timeout=120) == 0 diff --git a/score/mw/com/test/methods/non_trivial_constructors/main_consumer.cpp b/score/mw/com/test/methods/non_trivial_constructors/main_consumer.cpp new file mode 100644 index 000000000..f4a0fc68b --- /dev/null +++ b/score/mw/com/test/methods/non_trivial_constructors/main_consumer.cpp @@ -0,0 +1,22 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/com/runtime.h" +#include "score/mw/com/test/common_test_resources/assert_handler.h" +#include "score/mw/com/test/methods/non_trivial_constructors/consumer.h" + +int main(int argc, const char** argv) +{ + score::mw::com::test::SetupAssertHandler(); + score::mw::com::runtime::InitializeRuntime(argc, argv); + return score::mw::com::test::run_consumer(); +} diff --git a/score/mw/com/test/methods/non_trivial_constructors/main_provider.cpp b/score/mw/com/test/methods/non_trivial_constructors/main_provider.cpp new file mode 100644 index 000000000..756f6d73f --- /dev/null +++ b/score/mw/com/test/methods/non_trivial_constructors/main_provider.cpp @@ -0,0 +1,33 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/com/runtime.h" +#include "score/mw/com/test/common_test_resources/assert_handler.h" +#include "score/mw/com/test/common_test_resources/stop_token_sig_term_handler.h" +#include "score/mw/com/test/methods/non_trivial_constructors/provider.h" + +#include + +int main(int argc, const char** argv) +{ + score::mw::com::test::SetupAssertHandler(); + score::mw::com::runtime::InitializeRuntime(argc, argv); + + score::cpp::stop_source stop_source{}; + const bool sig_term_handler_setup_success = score::mw::com::SetupStopTokenSigTermHandler(stop_source); + if (!sig_term_handler_setup_success) + { + std::cerr << "Unable to set signal handler for SIGINT and/or SIGTERM, cautiously continuing\n"; + } + + return score::mw::com::test::run_provider(stop_source.get_token()); +} diff --git a/score/mw/com/test/methods/non_trivial_constructors/provider.cpp b/score/mw/com/test/methods/non_trivial_constructors/provider.cpp new file mode 100644 index 000000000..5ee46c003 --- /dev/null +++ b/score/mw/com/test/methods/non_trivial_constructors/provider.cpp @@ -0,0 +1,151 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/com/test/methods/non_trivial_constructors/provider.h" + +#include "score/mw/com/test/methods/methods_test_resources/process_synchronizer.h" +#include "score/mw/com/test/methods/methods_test_resources/skeleton_container.h" +#include "score/mw/com/test/methods/non_trivial_constructors/test_method_datatype.h" + +#include + +#include +#include + +namespace score::mw::com::test +{ +namespace +{ +const std::string kInterprocessNotificationShmPath{"/non_trivial_constructors_test_interprocess_notification"}; + +const std::string kInstanceSpecifier{"test/methods/non_trivial_constructors/MethodSignature"}; + +bool RegisterMethodHandlerWithInArgsAndReturn(NonTrivialConstructorSkeleton& skeleton) +{ + auto handler_with_in_args_and_return = [](NonTriviallyConstructibleType a, + NonTriviallyConstructibleType b) -> NonTriviallyConstructibleType { + std::cout << "Provider: with_in_args_and_return called with " << a << " + " << b << std::endl; + return a + b; + }; + const auto register_result = + skeleton.with_in_args_and_return.RegisterHandler(std::move(handler_with_in_args_and_return)); + if (!register_result) + { + std::cerr << "Provider: Failed to register with_in_args_and_return handler" << std::endl; + return false; + } + + std::cout << "Provider: Successfully registered with_in_args_and_return handler" << std::endl; + return true; +} + +bool RegisterMethodHandlerWithInArgsOnly(NonTrivialConstructorSkeleton& skeleton) +{ + auto handler_with_in_args_only = [](NonTriviallyConstructibleType a, NonTriviallyConstructibleType b) { + std::cout << "Provider: with_in_args_only called with " << a << " + " << b << std::endl; + SCORE_LANGUAGE_FUTURECPP_ASSERT_MESSAGE(a == NonTriviallyConstructibleType{}, + "Unexpected first InArg received!"); + SCORE_LANGUAGE_FUTURECPP_ASSERT_MESSAGE(b == NonTriviallyConstructibleType{}, + "Unexpected second InArg received!"); + }; + const auto register_result = skeleton.with_in_args_only.RegisterHandler(std::move(handler_with_in_args_only)); + if (!register_result) + { + std::cerr << "Provider: Failed to register with_in_args_only handler" << std::endl; + return false; + } + + std::cout << "Provider: Successfully registered with_in_args_only handler" << std::endl; + return true; +} + +bool RegisterMethodHandlerWithReturnOnly(NonTrivialConstructorSkeleton& skeleton) +{ + auto handler_with_return_only = []() -> NonTriviallyConstructibleType { + std::cout << "Provider: with_return_only called. Returning " << NonTriviallyConstructibleType{} << std::endl; + return NonTriviallyConstructibleType{}; + }; + const auto register_result = skeleton.with_return_only.RegisterHandler(std::move(handler_with_return_only)); + if (!register_result) + { + std::cerr << "Provider: Failed to register with_return_only handler" << std::endl; + return false; + } + + std::cout << "Provider: Successfully registered with_return_only handler" << std::endl; + return true; +} + +} // namespace + +bool run_provider(const score::cpp::stop_token& stop_token) +{ + auto process_synchronizer_result = ProcessSynchronizer::Create(kInterprocessNotificationShmPath); + if (!(process_synchronizer_result.has_value())) + { + std::cerr << "Methods non_trivial_constructors provider failed: Could not create ProcessSynchronizer" + << std::endl; + return EXIT_FAILURE; + } + + SkeletonContainer skeleton_container{kInstanceSpecifier}; + + // Step 1. Create skeleton + if (!skeleton_container.CreateSkeleton()) + { + std::cerr << "Non trivial constructors provider failed: CreateSkeleton" << std::endl; + return EXIT_FAILURE; + } + + // Step 2. Register method handler for method with InArgs and return value + if (!RegisterMethodHandlerWithInArgsAndReturn(skeleton_container.GetSkeleton())) + { + std::cerr << "Non trivial constructors provider failed: RegisterMethodHandlerWithInArgsAndReturn" << std::endl; + return EXIT_FAILURE; + } + + // Step 3. Register method handler for method with only InArgs + if (!RegisterMethodHandlerWithInArgsOnly(skeleton_container.GetSkeleton())) + { + std::cerr << "Non trivial constructors provider failed: RegisterMethodHandlerWithInArgsOnly" << std::endl; + return EXIT_FAILURE; + } + + // Step 4. Register method handler for method with only return value + if (!RegisterMethodHandlerWithReturnOnly(skeleton_container.GetSkeleton())) + { + std::cerr << "Non trivial constructors provider failed: RegisterMethodHandlerWithReturnOnly" << std::endl; + return EXIT_FAILURE; + } + + // Step 5. Offer service + if (!skeleton_container.OfferService()) + { + std::cerr << "Non trivial constructors provider failed: OfferService" << std::endl; + return EXIT_FAILURE; + } + + // Step 6. Wait for proxy test to finish + std::cout << "Provider: Ready for method calls" << std::endl; + if (!process_synchronizer_result->WaitWithAbort(stop_token)) + { + std::cerr << "Non trivial constructors provider failed: WaitForProxyTestToFinish was stopped by " + "stop_token instead of notification" + << std::endl; + return EXIT_FAILURE; + } + + std::cout << "Provider: Shutting down" << std::endl; + return EXIT_SUCCESS; +} + +} // namespace score::mw::com::test diff --git a/score/mw/com/test/methods/non_trivial_constructors/provider.h b/score/mw/com/test/methods/non_trivial_constructors/provider.h new file mode 100644 index 000000000..96dd13c12 --- /dev/null +++ b/score/mw/com/test/methods/non_trivial_constructors/provider.h @@ -0,0 +1,27 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#ifndef SCORE_MW_COM_TEST_METHODS_NON_TRIVIAL_CONSTRUCTORS_PROVIDER_H +#define SCORE_MW_COM_TEST_METHODS_NON_TRIVIAL_CONSTRUCTORS_PROVIDER_H + +#include "score/mw/com/test/methods/non_trivial_constructors/test_method_datatype.h" + +#include + +namespace score::mw::com::test +{ + +bool run_provider(const score::cpp::stop_token& stop_token); + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_METHODS_NON_TRIVIAL_CONSTRUCTORS_PROVIDER_H diff --git a/score/mw/com/test/methods/non_trivial_constructors/test_method_datatype.cpp b/score/mw/com/test/methods/non_trivial_constructors/test_method_datatype.cpp new file mode 100644 index 000000000..21f41a0b3 --- /dev/null +++ b/score/mw/com/test/methods/non_trivial_constructors/test_method_datatype.cpp @@ -0,0 +1,39 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/com/test/methods/non_trivial_constructors/test_method_datatype.h" + +namespace score::mw::com::test +{ + +NonTriviallyConstructibleType operator+(const NonTriviallyConstructibleType& a, const NonTriviallyConstructibleType& b) +{ + return NonTriviallyConstructibleType{a.value_ + b.value_}; +} + +bool operator==(const NonTriviallyConstructibleType& a, const NonTriviallyConstructibleType& b) +{ + return a.value_ == b.value_; +} + +bool operator!=(const NonTriviallyConstructibleType& a, const NonTriviallyConstructibleType& b) +{ + return !(a == b); +} + +std::ostream& operator<<(std::ostream& os, const NonTriviallyConstructibleType& obj) +{ + os << obj.value_; + return os; +} + +} // namespace score::mw::com::test diff --git a/score/mw/com/test/methods/non_trivial_constructors/test_method_datatype.h b/score/mw/com/test/methods/non_trivial_constructors/test_method_datatype.h new file mode 100644 index 000000000..dfe56affc --- /dev/null +++ b/score/mw/com/test/methods/non_trivial_constructors/test_method_datatype.h @@ -0,0 +1,75 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#ifndef SCORE_MW_COM_TEST_METHODS_NON_TRIVIAL_CONSTRUCTORS_TEST_METHOD_DATATYPE_H +#define SCORE_MW_COM_TEST_METHODS_NON_TRIVIAL_CONSTRUCTORS_TEST_METHOD_DATATYPE_H + +#include "score/mw/com/types.h" + +#include + +namespace score::mw::com::test +{ + +struct NonTriviallyConstructibleType +{ + NonTriviallyConstructibleType() : value_{kInitialValue} {} + + static constexpr std::int32_t kInitialValue = 21; + + private: + friend NonTriviallyConstructibleType operator+(const NonTriviallyConstructibleType& a, + const NonTriviallyConstructibleType& b); + friend bool operator==(const NonTriviallyConstructibleType& a, const NonTriviallyConstructibleType& b); + friend std::ostream& operator<<(std::ostream& os, const NonTriviallyConstructibleType& obj); + + NonTriviallyConstructibleType(std::int32_t value) : value_{value} {} + + std::int32_t value_; +}; + +NonTriviallyConstructibleType operator+(const NonTriviallyConstructibleType& a, const NonTriviallyConstructibleType& b); +bool operator==(const NonTriviallyConstructibleType& a, const NonTriviallyConstructibleType& b); +bool operator!=(const NonTriviallyConstructibleType& a, const NonTriviallyConstructibleType& b); +std::ostream& operator<<(std::ostream& os, const NonTriviallyConstructibleType& obj); + +/// \brief Test interface with methods covering all signature variations +/// \tparam T Either ProxyTrait or SkeletonTrait +template +class NonTrivialConstructorInterface : public T::Base +{ + public: + using T::Base::Base; + + /// \brief Method with both InArgs and Result + typename T::template Method + with_in_args_and_return{*this, "with_in_args_and_return"}; + + /// \brief Method with only InArgs, no Result (void return) + typename T::template Method with_in_args_only{ + *this, + "with_in_args_only"}; + + /// \brief Method with only Result, no InArgs + typename T::template Method with_return_only{*this, "with_return_only"}; +}; + +/// \brief Proxy side of the test service +using NonTrivialConstructorProxy = score::mw::com::AsProxy; + +/// \brief Skeleton side of the test service +using NonTrivialConstructorSkeleton = score::mw::com::AsSkeleton; + +} // namespace score::mw::com::test + +#endif // SCORE_MW_COM_TEST_METHODS_NON_TRIVIAL_CONSTRUCTORS_TEST_METHOD_DATATYPE_H