// Copyright (c) 2022 by Apex.AI Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // SPDX-License-Identifier: Apache-2.0 #include "iceoryx_binding_c/internal/cpp2c_enum_translation.hpp" #include "iceoryx_hoofs/internal/relocatable_pointer/atomic_relocatable_pointer.hpp" #include "iceoryx_posh/capro/service_description.hpp" #include "iceoryx_posh/internal/mepoo/memory_manager.hpp" #include "iceoryx_posh/internal/popo/ports/client_port_roudi.hpp" #include "iceoryx_posh/mepoo/mepoo_config.hpp" #include "iceoryx_posh/popo/untyped_server.hpp" #include "iceoryx_posh/testing/mocks/posh_runtime_mock.hpp" using namespace iox::popo; using namespace iox::capro; using namespace iox::capro; using namespace iox::cxx; extern "C" { #include "iceoryx_binding_c/server.h" } #include "test.hpp" namespace { using namespace ::testing; class iox_server_test : public Test { public: static constexpr const char RUNTIME_NAME[] = "sven_shwiddzler"; std::unique_ptr runtimeMock = PoshRuntimeMock::create(RUNTIME_NAME); static constexpr uint64_t MAX_REQUESTS_HOLD_IN_PARALLEL = iox::MAX_REQUESTS_PROCESSED_SIMULTANEOUSLY + 1U; void SetUp() override { memoryConfig.addMemPool({128, 2}); memoryConfig.addMemPool({1024, MAX_REQUESTS_HOLD_IN_PARALLEL + 1}); memoryManager.configureMemoryManager(memoryConfig, mgmtAllocator, dataAllocator); } ServerPortData* createServerPortData(const ServerOptions& options) { sutPort.emplace(ServiceDescription{IdString_t(TruncateToCapacity, SERVICE), IdString_t(TruncateToCapacity, INSTANCE), IdString_t(TruncateToCapacity, EVENT)}, RUNTIME_NAME, options, &memoryManager); return &*sutPort; } void receiveRequest(const int64_t requestValue = 0, const uint32_t chunkSize = sizeof(int64_t)) { auto chunk = memoryManager.getChunk(*iox::mepoo::ChunkSettings::create( chunkSize, iox::CHUNK_DEFAULT_USER_PAYLOAD_ALIGNMENT, sizeof(RequestHeader))); ASSERT_FALSE(chunk.has_error()); new (chunk->getChunkHeader()->userHeader()) RequestHeader(clientResponseQueueData.m_uniqueId, RpcBaseHeader::UNKNOWN_CLIENT_QUEUE_INDEX); *static_cast(chunk->getUserPayload()) = requestValue; iox::popo::ChunkQueuePusher pusher{&sutPort->m_chunkReceiverData}; if (!pusher.push(*chunk)) { sutPort->m_chunkReceiverData.m_queueHasLostChunks = true; } } void connectClient() { sutPort->m_chunkSenderData.m_queues.emplace_back(&clientResponseQueueData); } void prepareServerInit(const ServerOptions& options = ServerOptions()) { EXPECT_CALL(*runtimeMock, getMiddlewareServer(ServiceDescription{IdString_t(TruncateToCapacity, SERVICE), IdString_t(TruncateToCapacity, INSTANCE), IdString_t(TruncateToCapacity, EVENT)}, options, _)) .WillOnce(Return(createServerPortData(options))); } static constexpr uint64_t MANAGEMENT_MEMORY_SIZE = 1024 * 1024; char managementMemory[MANAGEMENT_MEMORY_SIZE]; iox::posix::Allocator mgmtAllocator{managementMemory, MANAGEMENT_MEMORY_SIZE}; static constexpr uint64_t DATA_MEMORY_SIZE = 1024 * 1024; char dataMemory[DATA_MEMORY_SIZE]; iox::posix::Allocator dataAllocator{dataMemory, DATA_MEMORY_SIZE}; iox::mepoo::MemoryManager memoryManager; iox::mepoo::MePooConfig memoryConfig; iox::cxx::optional sutPort; iox_server_storage_t sutStorage; ClientChunkQueueData_t clientResponseQueueData{iox::popo::QueueFullPolicy::DISCARD_OLDEST_DATA, iox::cxx::VariantQueueTypes::SoFi_MultiProducerSingleConsumer}; ChunkQueuePopper clientResponseQueue{&clientResponseQueueData}; static constexpr const char SERVICE[] = "TheHoff"; static constexpr const char INSTANCE[] = "IsAll"; static constexpr const char EVENT[] = "YouNeed"; }; constexpr const char iox_server_test::RUNTIME_NAME[]; constexpr const char iox_server_test::SERVICE[]; constexpr const char iox_server_test::INSTANCE[]; constexpr const char iox_server_test::EVENT[]; TEST_F(iox_server_test, notInitializedOptionsAreUninitialized) { ::testing::Test::RecordProperty("TEST_ID", "e227f280-3725-44e4-ba08-c9d2a43d5a1b"); iox_server_options_t uninitializedOptions; #if !defined(__clang__) // ignore the warning since we would like to test the behavior of an uninitialized option #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" EXPECT_FALSE(iox_server_options_is_initialized(&uninitializedOptions)); #pragma GCC diagnostic pop #endif } TEST_F(iox_server_test, initializedOptionsAreInitialized) { ::testing::Test::RecordProperty("TEST_ID", "f92d5196-d527-4504-8131-5e5304091068"); iox_server_options_t initializedOptions; iox_server_options_init(&initializedOptions); EXPECT_TRUE(iox_server_options_is_initialized(&initializedOptions)); } TEST_F(iox_server_test, initializedOptionsToCPPDefaults) { ::testing::Test::RecordProperty("TEST_ID", "0b202536-31e3-4439-836f-ba83235048fa"); iox_server_options_t initializedOptions; iox_server_options_init(&initializedOptions); ServerOptions cppOptions; EXPECT_THAT(initializedOptions.requestQueueCapacity, Eq(cppOptions.requestQueueCapacity)); EXPECT_THAT(initializedOptions.nodeName, StrEq(cppOptions.nodeName.c_str())); EXPECT_THAT(initializedOptions.offerOnCreate, Eq(cppOptions.offerOnCreate)); EXPECT_THAT(initializedOptions.requestQueueFullPolicy, Eq(cpp2c::queueFullPolicy(cppOptions.requestQueueFullPolicy))); EXPECT_THAT(initializedOptions.clientTooSlowPolicy, Eq(cpp2c::consumerTooSlowPolicy(cppOptions.clientTooSlowPolicy))); } TEST_F(iox_server_test, InitializingServerWithNullptrOptionsGetsMiddlewareServerWithDefaultOptions) { ::testing::Test::RecordProperty("TEST_ID", "10091749-4c07-442a-b42b-aef0d8d06ceb"); ServerOptions defaultOptions; prepareServerInit(defaultOptions); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, nullptr); ASSERT_THAT(sut, Ne(nullptr)); iox_server_deinit(sut); } TEST_F(iox_server_test, InitializingServerWithCustomOptionsWorks) { ::testing::Test::RecordProperty("TEST_ID", "e21f9257-cf3b-4e18-ad98-126a295e780f"); iox_server_options_t options; iox_server_options_init(&options); options.requestQueueCapacity = 32; strncpy(options.nodeName, "do not hassel with the hoff", IOX_CONFIG_NODE_NAME_SIZE); options.offerOnCreate = false; options.requestQueueFullPolicy = QueueFullPolicy_BLOCK_PRODUCER; options.clientTooSlowPolicy = ConsumerTooSlowPolicy_WAIT_FOR_CONSUMER; ServerOptions cppOptions; cppOptions.requestQueueCapacity = options.requestQueueCapacity; cppOptions.nodeName = iox::NodeName_t(TruncateToCapacity, options.nodeName); cppOptions.offerOnCreate = options.offerOnCreate; cppOptions.requestQueueFullPolicy = iox::popo::QueueFullPolicy::BLOCK_PRODUCER; cppOptions.clientTooSlowPolicy = iox::popo::ConsumerTooSlowPolicy::WAIT_FOR_CONSUMER; prepareServerInit(cppOptions); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, &options); ASSERT_THAT(sut, Ne(nullptr)); iox_server_deinit(sut); } TEST_F(iox_server_test, DeinitReleasesServer) { ::testing::Test::RecordProperty("TEST_ID", "a50eb082-bc40-4c84-95dc-cefcdfcdb70d"); prepareServerInit(); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, nullptr); iox_server_deinit(sut); EXPECT_THAT(sutPort->m_toBeDestroyed.load(), Eq(true)); } TEST_F(iox_server_test, WhenNotOfferedTakeRequestFails) { ::testing::Test::RecordProperty("TEST_ID", "48763e8a-c192-4baf-88c0-fb5377d89127"); prepareServerInit(); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, nullptr); iox_server_stop_offer(sut); const void* payload; EXPECT_THAT(iox_server_take_request(sut, &payload), Eq(ServerRequestResult_NO_PENDING_REQUESTS_AND_SERVER_DOES_NOT_OFFER)); iox_server_deinit(sut); } TEST_F(iox_server_test, WhenOfferedAndNoRequestsPresentTakeFails) { ::testing::Test::RecordProperty("TEST_ID", "409ab402-2452-4250-a6b4-4c90489cdd42"); prepareServerInit(); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, nullptr); iox_server_offer(sut); const void* payload; EXPECT_THAT(iox_server_take_request(sut, &payload), Eq(ServerRequestResult_NO_PENDING_REQUESTS)); iox_server_deinit(sut); } TEST_F(iox_server_test, WhenOfferedAndRequestsPresentTakeSucceeds) { ::testing::Test::RecordProperty("TEST_ID", "42443d18-5a8f-40ee-938d-2b0a345f829b"); prepareServerInit(); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, nullptr); iox_server_offer(sut); constexpr int64_t REQUEST_VALUE = 64461001; receiveRequest(REQUEST_VALUE); const void* payload; ASSERT_THAT(iox_server_take_request(sut, &payload), Eq(ServerRequestResult_SUCCESS)); ASSERT_THAT(payload, Ne(nullptr)); EXPECT_THAT(*static_cast(payload), Eq(REQUEST_VALUE)); iox_server_deinit(sut); } TEST_F(iox_server_test, TakingToMuchRequestsInParallelLeadsToError) { ::testing::Test::RecordProperty("TEST_ID", "b1175d14-0268-42d9-a174-97713e622200"); prepareServerInit(); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, nullptr); iox_server_offer(sut); constexpr int64_t REQUEST_VALUE = 0; constexpr int64_t PAYLOAD_SIZE = 512; const void* payload; for (uint64_t i = 0U; i < MAX_REQUESTS_HOLD_IN_PARALLEL; ++i) { receiveRequest(REQUEST_VALUE, PAYLOAD_SIZE); payload = nullptr; ASSERT_THAT(iox_server_take_request(sut, &payload), Eq(ServerRequestResult_SUCCESS)); ASSERT_THAT(payload, Ne(nullptr)); } receiveRequest(REQUEST_VALUE, PAYLOAD_SIZE); payload = nullptr; ASSERT_THAT(iox_server_take_request(sut, &payload), Eq(ServerRequestResult_TOO_MANY_REQUESTS_HELD_IN_PARALLEL)); ASSERT_THAT(payload, Eq(nullptr)); iox_server_deinit(sut); } TEST_F(iox_server_test, ReleaseRequestWorks) { ::testing::Test::RecordProperty("TEST_ID", "98a7df98-5adf-4d16-81ea-ac5ebbdc7576"); prepareServerInit(); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, nullptr); iox_server_offer(sut); receiveRequest(); const void* payload; ASSERT_THAT(iox_server_take_request(sut, &payload), Eq(ServerRequestResult_SUCCESS)); EXPECT_THAT(memoryManager.getMemPoolInfo(0).m_usedChunks, Eq(1U)); iox_server_release_request(sut, payload); EXPECT_THAT(memoryManager.getMemPoolInfo(0).m_usedChunks, Eq(0U)); iox_server_deinit(sut); } TEST_F(iox_server_test, ReleaseQueuedRequestsWorks) { ::testing::Test::RecordProperty("TEST_ID", "d245c9e4-a18e-42ae-ab0a-375e1ebf24e7"); prepareServerInit(); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, nullptr); iox_server_offer(sut); receiveRequest(); receiveRequest(); EXPECT_THAT(memoryManager.getMemPoolInfo(0).m_usedChunks, Eq(2U)); iox_server_release_queued_requests(sut); EXPECT_THAT(memoryManager.getMemPoolInfo(0).m_usedChunks, Eq(0U)); iox_server_deinit(sut); } TEST_F(iox_server_test, HasClientsWorks) { ::testing::Test::RecordProperty("TEST_ID", "d7235e98-43b2-4c81-9939-5d774958bd56"); prepareServerInit(); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, nullptr); iox_server_offer(sut); EXPECT_FALSE(iox_server_has_clients(sut)); connectClient(); EXPECT_TRUE(iox_server_has_clients(sut)); iox_server_deinit(sut); } TEST_F(iox_server_test, HasRequestWorks) { ::testing::Test::RecordProperty("TEST_ID", "2d833165-8b7d-4364-9268-a0a38a31590d"); prepareServerInit(); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, nullptr); iox_server_offer(sut); EXPECT_FALSE(iox_server_has_requests(sut)); receiveRequest(); EXPECT_TRUE(iox_server_has_requests(sut)); iox_server_deinit(sut); } TEST_F(iox_server_test, HasMissedRequestWorks) { ::testing::Test::RecordProperty("TEST_ID", "3edbf3d6-ef45-49f5-98c9-57b7d1b6c987"); iox_server_options_t options; iox_server_options_init(&options); options.requestQueueCapacity = 1; ServerOptions cppOptions; cppOptions.requestQueueCapacity = 1; prepareServerInit(cppOptions); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, &options); iox_server_offer(sut); EXPECT_FALSE(iox_server_has_missed_requests(sut)); receiveRequest(); receiveRequest(); EXPECT_TRUE(iox_server_has_missed_requests(sut)); EXPECT_FALSE(iox_server_has_missed_requests(sut)); iox_server_deinit(sut); } TEST_F(iox_server_test, OfferReturnsCorrectOfferState) { ::testing::Test::RecordProperty("TEST_ID", "9537e4b5-27af-4646-83c5-90d5799011bc"); prepareServerInit(); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, nullptr); iox_server_offer(sut); EXPECT_TRUE(iox_server_is_offered(sut)); iox_server_stop_offer(sut); EXPECT_FALSE(iox_server_is_offered(sut)); iox_server_deinit(sut); } TEST_F(iox_server_test, GetServiceDescriptionWorks) { ::testing::Test::RecordProperty("TEST_ID", "a2542a9b-e49f-47ea-9fb9-6306208ca987"); prepareServerInit(); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, nullptr); auto serviceDescription = iox_server_get_service_description(sut); EXPECT_THAT(serviceDescription.serviceString, StrEq(SERVICE)); EXPECT_THAT(serviceDescription.instanceString, StrEq(INSTANCE)); EXPECT_THAT(serviceDescription.eventString, StrEq(EVENT)); iox_server_deinit(sut); } TEST_F(iox_server_test, LoanWorks) { ::testing::Test::RecordProperty("TEST_ID", "600a78ea-ee00-4179-a62e-b0a4adf9d54a"); prepareServerInit(); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, nullptr); connectClient(); receiveRequest(); const void* requestPayload; EXPECT_THAT(iox_server_take_request(sut, &requestPayload), Eq(ServerRequestResult_SUCCESS)); void* payload = nullptr; EXPECT_THAT(iox_server_loan_response(sut, requestPayload, &payload, sizeof(int64_t)), Eq(AllocationResult_SUCCESS)); EXPECT_THAT(payload, Ne(nullptr)); iox_server_deinit(sut); } TEST_F(iox_server_test, LoanFailsWhenNoMoreChunksAreAvailable) { ::testing::Test::RecordProperty("TEST_ID", "dac40907-aafb-40cd-9bbe-eeddf8916322"); prepareServerInit(); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, nullptr); connectClient(); receiveRequest(); const void* requestPayload; EXPECT_THAT(iox_server_take_request(sut, &requestPayload), Eq(ServerRequestResult_SUCCESS)); void* payload = nullptr; EXPECT_THAT(iox_server_loan_response(sut, requestPayload, &payload, sizeof(int64_t)), Eq(AllocationResult_SUCCESS)); EXPECT_THAT(iox_server_loan_response(sut, requestPayload, &payload, sizeof(int64_t)), Eq(AllocationResult_RUNNING_OUT_OF_CHUNKS)); iox_server_deinit(sut); } TEST_F(iox_server_test, LoanAlignedWorks) { ::testing::Test::RecordProperty("TEST_ID", "dfc4cbb1-e72d-470a-8e56-182446a16976"); prepareServerInit(); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, nullptr); connectClient(); receiveRequest(); const void* requestPayload; EXPECT_THAT(iox_server_take_request(sut, &requestPayload), Eq(ServerRequestResult_SUCCESS)); void* payload = nullptr; EXPECT_THAT(iox_server_loan_aligned_response(sut, requestPayload, &payload, sizeof(int64_t), 16), Eq(AllocationResult_SUCCESS)); EXPECT_THAT(payload, Ne(nullptr)); EXPECT_THAT(reinterpret_cast(payload) % 16, Eq(0U)); iox_server_deinit(sut); } TEST_F(iox_server_test, LoanAlignedFailsWhenNoChunksAreAvailable) { ::testing::Test::RecordProperty("TEST_ID", "2fad0479-1e87-4a0a-a52c-cb437091018d"); prepareServerInit(); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, nullptr); connectClient(); receiveRequest(); const void* requestPayload; EXPECT_THAT(iox_server_take_request(sut, &requestPayload), Eq(ServerRequestResult_SUCCESS)); void* payload = nullptr; EXPECT_THAT(iox_server_loan_aligned_response(sut, requestPayload, &payload, sizeof(int64_t), 16), Eq(AllocationResult_SUCCESS)); EXPECT_THAT(iox_server_loan_aligned_response(sut, requestPayload, &payload, sizeof(int64_t), 16), Eq(AllocationResult_RUNNING_OUT_OF_CHUNKS)); iox_server_deinit(sut); } TEST_F(iox_server_test, ReleaseResponseWorks) { ::testing::Test::RecordProperty("TEST_ID", "06d1e165-eb60-4bf7-94b5-290535c6541e"); prepareServerInit(); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, nullptr); connectClient(); receiveRequest(); const void* requestPayload; EXPECT_THAT(iox_server_take_request(sut, &requestPayload), Eq(ServerRequestResult_SUCCESS)); void* payload = nullptr; EXPECT_THAT(iox_server_loan_response(sut, requestPayload, &payload, sizeof(int64_t)), Eq(AllocationResult_SUCCESS)); EXPECT_THAT(memoryManager.getMemPoolInfo(0).m_usedChunks, Eq(2U)); iox_server_release_response(sut, payload); EXPECT_THAT(memoryManager.getMemPoolInfo(0).m_usedChunks, Eq(1U)); iox_server_release_request(sut, requestPayload); EXPECT_THAT(memoryManager.getMemPoolInfo(0).m_usedChunks, Eq(0U)); iox_server_deinit(sut); } TEST_F(iox_server_test, SendWorks) { ::testing::Test::RecordProperty("TEST_ID", "c7fe56ff-1678-449f-a3d3-fe5f9dc57d1b"); prepareServerInit(); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, nullptr); connectClient(); receiveRequest(); const void* requestPayload; EXPECT_THAT(iox_server_take_request(sut, &requestPayload), Eq(ServerRequestResult_SUCCESS)); void* payload = nullptr; EXPECT_THAT(iox_server_loan_response(sut, requestPayload, &payload, sizeof(int64_t)), Eq(AllocationResult_SUCCESS)); ASSERT_THAT(payload, Ne(nullptr)); *static_cast(payload) = 42424242; EXPECT_THAT(iox_server_send(sut, payload), Eq(ServerSendResult_SUCCESS)); clientResponseQueue.tryPop() .and_then( [&](auto& sharedChunk) { EXPECT_THAT(*static_cast(sharedChunk.getUserPayload()), Eq(42424242)); }) .or_else([&] { GTEST_FAIL() << "Expected response but got nothing"; }); iox_server_deinit(sut); } TEST_F(iox_server_test, SendWithNullptrReturnsError) { ::testing::Test::RecordProperty("TEST_ID", "af538f0b-ce37-48d2-8940-28d39d7a411e"); prepareServerInit(); iox_server_t sut = iox_server_init(&sutStorage, SERVICE, INSTANCE, EVENT, nullptr); connectClient(); EXPECT_THAT(iox_server_send(sut, nullptr), Eq(ServerSendResult_INVALID_RESPONSE)); iox_server_deinit(sut); } } // namespace