// Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved. // Copyright (c) 2021 - 2022 by Apex.AI. 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_hoofs/cxx/helplets.hpp" #include "iceoryx_hoofs/cxx/optional.hpp" #include "iceoryx_hoofs/cxx/string.hpp" #include "iceoryx_posh/internal/roudi/service_registry.hpp" #include "test.hpp" #include #include #include namespace { using namespace ::testing; using namespace iox::roudi; using iox::capro::IdString_t; using iox::cxx::optional; using iox::capro::ServiceDescription; using SearchResult_t = std::vector; struct PublisherTest { auto add(const ServiceDescription& sd) { return registry.addPublisher(sd); } auto otherAdd(const ServiceDescription& sd) { return registry.addServer(sd); } auto remove(const ServiceDescription& sd) { return registry.removePublisher(sd); } auto count(ServiceRegistry::ServiceDescriptionEntry& entry) { return entry.publisherCount; } auto filter(const SearchResult_t& result) { SearchResult_t filtered; for (auto& entry : result) { if (entry.publisherCount > 0) { filtered.emplace_back(entry); } } return filtered; } auto operator->() { return ®istry; } iox::roudi::ServiceRegistry registry; }; struct ServerTest { auto add(const ServiceDescription& sd) { return registry.addServer(sd); } auto otherAdd(const ServiceDescription& sd) { return registry.addPublisher(sd); } auto remove(const ServiceDescription& sd) { return registry.removeServer(sd); } auto count(ServiceRegistry::ServiceDescriptionEntry& entry) { return entry.serverCount; } auto filter(const SearchResult_t& result) { SearchResult_t filtered; for (auto& entry : result) { if (entry.serverCount > 0) { filtered.emplace_back(entry); } } return filtered; } auto operator->() { return ®istry; } iox::roudi::ServiceRegistry registry; }; template class ServiceRegistry_test : public Test { public: using ServiceDescription = iox::capro::ServiceDescription; virtual void SetUp() { internal::CaptureStdout(); } virtual void TearDown() { std::string output = internal::GetCapturedStdout(); if (Test::HasFailure()) { std::cout << output << std::endl; } } uint64_t countServices() { uint64_t count = 0; sut->forEach([&](const ServiceRegistry::ServiceDescriptionEntry&) { ++count; }); return count; } void find(const optional& service, const optional& instance, const optional& event) { searchResult.clear(); auto findHandler = [&](const ServiceRegistry::ServiceDescriptionEntry& entry) { searchResult.push_back(entry); }; sut->find(service, instance, event, findHandler); } SearchResult_t searchResult; Sut sut; }; constexpr auto CAPACITY = ServiceRegistry::CAPACITY; typedef ::testing::Types TestTypes; TYPED_TEST_SUITE(ServiceRegistry_test, TestTypes); TYPED_TEST(ServiceRegistry_test, AddNoServiceDescriptionsAndWildcardSearchReturnsNothing) { ::testing::Test::RecordProperty("TEST_ID", "3a050209-01d8-4d0e-9e70-c0662b9dbe76"); this->find(iox::capro::Wildcard, iox::capro::Wildcard, iox::capro::Wildcard); EXPECT_THAT(this->searchResult.size(), Eq(0)); } TYPED_TEST(ServiceRegistry_test, AddMaximumNumberOfServiceDescriptionsWorks) { ::testing::Test::RecordProperty("TEST_ID", "fa7d6416-4183-4942-a323-01f78c1bb6c1"); iox::cxx::vector services; for (uint64_t i = 0U; i < CAPACITY; i++) { services.push_back(iox::capro::ServiceDescription( "Foo", "Bar", iox::capro::IdString_t(iox::cxx::TruncateToCapacity, iox::cxx::convert::toString(i)))); } for (auto& service : services) { auto result = this->sut.add(service); ASSERT_FALSE(result.has_error()); } } TYPED_TEST(ServiceRegistry_test, AddMoreThanMaximumNumberOfServiceDescriptionsFails) { ::testing::Test::RecordProperty("TEST_ID", "a911f654-8314-4ea3-b9b2-1afa121a2b21"); iox::cxx::vector services; for (uint64_t i = 0U; i < CAPACITY; i++) { services.push_back(iox::capro::ServiceDescription( "Foo", "Bar", iox::capro::IdString_t(iox::cxx::TruncateToCapacity, iox::cxx::convert::toString(i)))); } for (auto& service : services) { auto result = this->sut.add(service); ASSERT_FALSE(result.has_error()); } auto result = this->sut.add(iox::capro::ServiceDescription("Foo", "Bar", "Baz")); ASSERT_TRUE(result.has_error()); EXPECT_THAT(result.get_error(), Eq(ServiceRegistry::Error::SERVICE_REGISTRY_FULL)); } TYPED_TEST(ServiceRegistry_test, AddServiceDescriptionsWhichWasAlreadyAddedAndReturnsOneResult) { ::testing::Test::RecordProperty("TEST_ID", "79234425-98ce-49eb-bf04-82883ee22a92"); auto result1 = this->sut.add(ServiceDescription("Li", "La", "Launebaer")); ASSERT_FALSE(result1.has_error()); auto result2 = this->sut.add(ServiceDescription("Li", "La", "Launebaer")); ASSERT_FALSE(result2.has_error()); this->find(iox::capro::Wildcard, iox::capro::Wildcard, iox::capro::Wildcard); ASSERT_THAT(this->searchResult.size(), Eq(1)); EXPECT_THAT(this->searchResult[0].serviceDescription, Eq(ServiceDescription("Li", "La", "Launebaer"))); EXPECT_THAT(this->sut.count(this->searchResult[0]), Eq(2)); } TYPED_TEST(ServiceRegistry_test, AddServiceDescriptionsTwiceAndRemoveOnceAndReturnsOneResult) { ::testing::Test::RecordProperty("TEST_ID", "6f8193ea-2d36-423f-a658-1dba30c1868d"); auto result1 = this->sut.add(ServiceDescription("Li", "La", "Launebaerli")); ASSERT_FALSE(result1.has_error()); auto result2 = this->sut.add(ServiceDescription("Li", "La", "Launebaerli")); ASSERT_FALSE(result2.has_error()); this->sut.remove(ServiceDescription("Li", "La", "Launebaerli")); this->find(iox::capro::Wildcard, iox::capro::Wildcard, iox::capro::Wildcard); ASSERT_THAT(this->searchResult.size(), Eq(1)); EXPECT_THAT(this->searchResult[0].serviceDescription, Eq(ServiceDescription("Li", "La", "Launebaerli"))); EXPECT_THAT(this->sut.count(this->searchResult[0]), Eq(1)); } TYPED_TEST(ServiceRegistry_test, AddServiceDescriptionsTwiceAndPurgeReturnsNoResult) { ::testing::Test::RecordProperty("TEST_ID", "3185b67f-b891-4a82-8f91-047e059ed68f"); auto result1 = this->sut.add(ServiceDescription("Li", "La", "Launebaerli")); ASSERT_FALSE(result1.has_error()); auto result2 = this->sut.add(ServiceDescription("Li", "La", "Launebaerli")); ASSERT_FALSE(result2.has_error()); this->sut->purge(ServiceDescription("Li", "La", "Launebaerli")); this->find(iox::capro::Wildcard, iox::capro::Wildcard, iox::capro::Wildcard); EXPECT_THAT(this->searchResult.size(), Eq(0)); } TYPED_TEST(ServiceRegistry_test, AddEmptyServiceDescriptionsWorks) { ::testing::Test::RecordProperty("TEST_ID", "3cff55b0-d12f-48f5-8f0c-6501d0c2bf79"); auto result = this->sut.add(ServiceDescription()); ASSERT_FALSE(result.has_error()); } TYPED_TEST(ServiceRegistry_test, RemovingServiceDescriptionsWhichWasntAddedFails) { ::testing::Test::RecordProperty("TEST_ID", "cf3e39f5-c29d-4b5f-8e01-af69681b2ea8"); this->sut.remove(ServiceDescription("Sim", "Sa", "Lambim")); EXPECT_THAT(this->countServices(), Eq(0)); } TYPED_TEST(ServiceRegistry_test, RemovingEmptyServiceDescriptionsWorks) { ::testing::Test::RecordProperty("TEST_ID", "523f2320-c0e5-4590-a1b0-604e756ecaa5"); ASSERT_FALSE(this->sut.add(ServiceDescription()).has_error()); this->sut.remove(ServiceDescription()); EXPECT_THAT(this->countServices(), Eq(0)); } TYPED_TEST(ServiceRegistry_test, SingleEmptyServiceDescriptionsCanBeFoundWithWildcardSearch) { ::testing::Test::RecordProperty("TEST_ID", "be3e4b13-d930-47b2-aeaa-95c65f06deed"); ASSERT_FALSE(this->sut.add(ServiceDescription()).has_error()); this->find(iox::capro::Wildcard, iox::capro::Wildcard, iox::capro::Wildcard); ASSERT_THAT(this->searchResult.size(), Eq(1)); EXPECT_THAT(this->searchResult[0].serviceDescription, Eq(ServiceDescription())); } TYPED_TEST(ServiceRegistry_test, SingleEmptyServiceDescriptionsCanBeFoundWithEmptyString) { ::testing::Test::RecordProperty("TEST_ID", "1af0137f-6bf5-422e-a6ec-513f7d3f6191"); ASSERT_FALSE(this->sut.add(ServiceDescription()).has_error()); this->find(iox::capro::IdString_t(""), iox::capro::IdString_t(""), iox::capro::IdString_t("")); ASSERT_THAT(this->searchResult.size(), Eq(1)); EXPECT_THAT(this->searchResult[0].serviceDescription, Eq(ServiceDescription())); } TYPED_TEST(ServiceRegistry_test, SingleServiceDescriptionCanBeFoundWithWildcardSearch) { ::testing::Test::RecordProperty("TEST_ID", "a12c9d39-6379-4296-8987-df56a87169f7"); auto result = this->sut.add(ServiceDescription("Foo", "Bar", "Baz")); ASSERT_FALSE(result.has_error()); this->find(iox::capro::Wildcard, iox::capro::Wildcard, iox::capro::Wildcard); ASSERT_THAT(this->searchResult.size(), Eq(1)); EXPECT_THAT(this->searchResult[0].serviceDescription, Eq(ServiceDescription("Foo", "Bar", "Baz"))); } TYPED_TEST(ServiceRegistry_test, SingleServiceDescriptionCanBeFoundWithEventName) { ::testing::Test::RecordProperty("TEST_ID", "6df8fd7d-e4d1-4c51-8ad7-2fbe82e4ed09"); iox::capro::ServiceDescription service("a", "b", "c"); ASSERT_FALSE(this->sut.add(service).has_error()); this->find(iox::capro::Wildcard, iox::capro::Wildcard, iox::capro::IdString_t("c")); ASSERT_THAT(this->searchResult.size(), Eq(1)); EXPECT_THAT(this->searchResult[0].serviceDescription, Eq(service)); } TYPED_TEST(ServiceRegistry_test, ServiceDescriptionNotFoundWhenEventDoesNotMatch) { ::testing::Test::RecordProperty("TEST_ID", "ba7785d1-08ec-4f7c-b341-dff033dae2c7"); iox::capro::ServiceDescription service("Besser", "Wisser", "Girl"); ASSERT_FALSE(this->sut.add(service).has_error()); this->find(iox::capro::IdString_t("Besser"), iox::capro::IdString_t("Wisser"), iox::capro::IdString_t("Boy")); EXPECT_THAT(this->searchResult.size(), Eq(0)); } TYPED_TEST(ServiceRegistry_test, SingleServiceDescriptionCanBeFoundWithInstanceName) { ::testing::Test::RecordProperty("TEST_ID", "1dfaa836-f1da-466f-a794-a7ac1b599ce3"); auto result = this->sut.add(ServiceDescription("Baz", "Bar", "Foo")); ASSERT_FALSE(result.has_error()); this->find(iox::capro::Wildcard, iox::capro::IdString_t("Bar"), iox::capro::Wildcard); ASSERT_THAT(this->searchResult.size(), Eq(1)); EXPECT_THAT(this->searchResult[0].serviceDescription, Eq(ServiceDescription("Baz", "Bar", "Foo"))); } TYPED_TEST(ServiceRegistry_test, SingleServiceDescriptionCanBeFoundWithServiceName) { ::testing::Test::RecordProperty("TEST_ID", "0890013c-e14b-4ae2-89cb-757624c12b4e"); iox::capro::ServiceDescription service("a", "b", "c"); ASSERT_FALSE(this->sut.add(service).has_error()); this->find(iox::capro::IdString_t("a"), iox::capro::Wildcard, iox::capro::Wildcard); ASSERT_THAT(this->searchResult.size(), Eq(1)); EXPECT_THAT(this->searchResult[0].serviceDescription, Eq(service)); } TYPED_TEST(ServiceRegistry_test, EmptyAndNotEmptyServiceDescriptionsCanAllBeFoundWithWildcardSearch) { ::testing::Test::RecordProperty("TEST_ID", "4f34e604-5217-4e47-9c6f-26c5cbdcd3ec"); ServiceDescription service1; ServiceDescription service2("alpha", "bravo", "charlie"); ASSERT_FALSE(this->sut.add(service1).has_error()); ASSERT_FALSE(this->sut.add(service2).has_error()); this->find(iox::capro::Wildcard, iox::capro::Wildcard, iox::capro::Wildcard); ASSERT_THAT(this->searchResult.size(), Eq(2)); EXPECT_THAT(this->searchResult[0].serviceDescription, Eq(service1)); EXPECT_THAT(this->searchResult[1].serviceDescription, Eq(service2)); } TYPED_TEST(ServiceRegistry_test, MultipleServiceDescriptionWithSameServiceNameCanAllBeFound) { ::testing::Test::RecordProperty("TEST_ID", "251fe262-2e8f-4e32-a5f1-a4b1aaa812fd"); iox::capro::ServiceDescription service1("a", "b", "b"); iox::capro::ServiceDescription service2("a", "c", "c"); iox::capro::ServiceDescription service3("a", "d", "d"); ASSERT_FALSE(this->sut.add(service1).has_error()); ASSERT_FALSE(this->sut.add(service2).has_error()); ASSERT_FALSE(this->sut.add(service3).has_error()); this->find(iox::capro::IdString_t("a"), iox::capro::Wildcard, iox::capro::Wildcard); ASSERT_THAT(this->searchResult.size(), Eq(3)); bool hasFoundB = false; bool hasFoundC = false; bool hasFoundD = false; for (auto& e : this->searchResult) { if (e.serviceDescription == service1) hasFoundB = true; if (e.serviceDescription == service2) hasFoundC = true; if (e.serviceDescription == service3) hasFoundD = true; } EXPECT_THAT(hasFoundB && hasFoundC && hasFoundD, Eq(true)); } TYPED_TEST(ServiceRegistry_test, MultipleServiceDescriptionWithDifferentServiceNameCanAllBeFound) { ::testing::Test::RecordProperty("TEST_ID", "9b7a0897-46a7-457e-8746-0c3899e96653"); iox::capro::ServiceDescription service1("a", "b", "b"); iox::capro::ServiceDescription service2("c", "d", "d"); ASSERT_FALSE(this->sut.add(service1).has_error()); ASSERT_FALSE(this->sut.add(service2).has_error()); this->find(iox::capro::IdString_t("a"), iox::capro::Wildcard, iox::capro::Wildcard); ASSERT_THAT(this->searchResult.size(), Eq(1)); EXPECT_THAT(this->searchResult[0].serviceDescription, Eq(service1)); this->searchResult.clear(); this->find(iox::capro::IdString_t("c"), iox::capro::Wildcard, iox::capro::Wildcard); ASSERT_THAT(this->searchResult.size(), Eq(1)); EXPECT_THAT(this->searchResult[0].serviceDescription, Eq(service2)); } TYPED_TEST(ServiceRegistry_test, MultipleServiceDescriptionWithSameServiceNameFindsSpecificService) { ::testing::Test::RecordProperty("TEST_ID", "557d6533-25a3-4c0c-adc0-e8ebb74785a0"); iox::capro::ServiceDescription service1("a", "b", "b"); iox::capro::ServiceDescription service2("a", "c", "c"); iox::capro::ServiceDescription service3("a", "d", "d"); ASSERT_FALSE(this->sut.add(service1).has_error()); ASSERT_FALSE(this->sut.add(service2).has_error()); ASSERT_FALSE(this->sut.add(service3).has_error()); this->find(iox::capro::IdString_t("a"), iox::capro::IdString_t("c"), iox::capro::IdString_t("c")); ASSERT_THAT(this->searchResult.size(), Eq(1)); EXPECT_THAT(this->searchResult[0].serviceDescription, Eq(service2)); } TYPED_TEST(ServiceRegistry_test, MultipleServiceDescriptionAddedInNonLinearOrderFindsCorrectServices) { ::testing::Test::RecordProperty("TEST_ID", "26187db1-299a-494c-bd85-eb646b8cf67b"); iox::capro::ServiceDescription service1("a", "1", "moep"); iox::capro::ServiceDescription service2("b", "2", "moep"); iox::capro::ServiceDescription service3("c", "3", "moep"); iox::capro::ServiceDescription service4("d", "4", "moep"); iox::capro::ServiceDescription service5("e", "5", "moep"); ASSERT_FALSE(this->sut.add(service5).has_error()); ASSERT_FALSE(this->sut.add(service3).has_error()); ASSERT_FALSE(this->sut.add(service4).has_error()); ASSERT_FALSE(this->sut.add(service2).has_error()); ASSERT_FALSE(this->sut.add(service1).has_error()); this->sut.remove(service5); this->sut.remove(service1); EXPECT_THAT(this->countServices(), Eq(3)); this->find(iox::capro::IdString_t("a"), iox::capro::Wildcard, iox::capro::Wildcard); EXPECT_THAT(this->searchResult.size(), Eq(0)); } TYPED_TEST(ServiceRegistry_test, FindSpecificNonExistingServiceDescriptionFails) { ::testing::Test::RecordProperty("TEST_ID", "183600f6-3d2d-4c0f-8c1d-d74acfb7fc50"); iox::capro::ServiceDescription service1("a", "b", "b"); iox::capro::ServiceDescription service2("a", "c", "c"); iox::capro::ServiceDescription service3("a", "d", "d"); ASSERT_FALSE(this->sut.add(service1).has_error()); ASSERT_FALSE(this->sut.add(service2).has_error()); ASSERT_FALSE(this->sut.add(service3).has_error()); this->find(iox::capro::IdString_t("a"), iox::capro::IdString_t("g"), iox::capro::IdString_t("f")); EXPECT_THAT(this->searchResult.size(), Eq(0)); } TYPED_TEST(ServiceRegistry_test, AddingMultipleServiceDescriptionWithSameServicesAndRemovingSpecificDoesNotFindSpecific) { ::testing::Test::RecordProperty("TEST_ID", "b8230904-c14e-4e9f-a324-f92c67522271"); iox::capro::ServiceDescription service1("a", "b", "b"); iox::capro::ServiceDescription service2("a", "c", "c"); iox::capro::ServiceDescription service3("a", "d", "d"); ASSERT_FALSE(this->sut.add(service1).has_error()); ASSERT_FALSE(this->sut.add(service2).has_error()); ASSERT_FALSE(this->sut.add(service3).has_error()); this->sut.remove(service2); EXPECT_THAT(this->countServices(), Eq(2)); this->find(iox::capro::IdString_t("a"), iox::capro::IdString_t("c"), iox::capro::IdString_t("c")); EXPECT_THAT(this->searchResult.size(), Eq(0)); } TYPED_TEST(ServiceRegistry_test, ServiceNotFoundAfterAddingAndRemovingToServiceRegistry) { ::testing::Test::RecordProperty("TEST_ID", "c5e67b1f-65f2-4973-9f76-73479dd1de9e"); iox::capro::ServiceDescription service1("a", "b", "b"); iox::capro::ServiceDescription service2("b", "c", "c"); iox::capro::ServiceDescription service3("c", "d", "d"); ASSERT_FALSE(this->sut.add(service1).has_error()); ASSERT_FALSE(this->sut.add(service2).has_error()); ASSERT_FALSE(this->sut.add(service3).has_error()); this->sut.remove(service2); EXPECT_THAT(this->countServices(), Eq(2)); this->find(iox::capro::IdString_t("b"), iox::capro::IdString_t("c"), iox::capro::IdString_t("c")); EXPECT_THAT(this->searchResult.size(), Eq(0)); } TYPED_TEST(ServiceRegistry_test, AddingMultipleServiceDescriptionAndRemovingAllDoesNotFindAnything) { ::testing::Test::RecordProperty("TEST_ID", "e7a7e160-813c-4daf-8c55-d0a85bb5f642"); iox::capro::ServiceDescription service1("a", "b", "b"); iox::capro::ServiceDescription service2("a", "c", "c"); iox::capro::ServiceDescription service3("a", "d", "d"); ASSERT_FALSE(this->sut.add(service1).has_error()); ASSERT_FALSE(this->sut.add(service2).has_error()); ASSERT_FALSE(this->sut.add(service3).has_error()); this->sut.remove(service1); this->sut.remove(service2); this->sut.remove(service3); this->find(iox::capro::IdString_t("a"), iox::capro::Wildcard, iox::capro::Wildcard); EXPECT_THAT(this->searchResult.size(), Eq(0)); } template T uniform(T max) { static auto seed = std::random_device()(); static std::mt19937 mt(seed); std::uniform_int_distribution dist(0, max); return dist(mt); } using string_t = iox::capro::IdString_t; string_t randomString(uint64_t size = string_t::capacity()) { // deliberately contains no `0` (need to exclude some char) static const char chars[] = "123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"; constexpr auto N = string_t::capacity(); size = std::min(N, size); char a[N + 1U]; for (uint64_t i = 0U; i < size; ++i) { // -2 is needed to avoid generating the '\0' terminator of chars a[i] = chars[uniform(sizeof(chars) - 2U)]; } a[size] = '\0'; return string_t(a); } TYPED_TEST(ServiceRegistry_test, CanAddMaximumNumberOfDifferentServiceDescriptions) { ::testing::Test::RecordProperty("TEST_ID", "76aef6cb-7886-4d64-9188-09bd1be2d335"); uint32_t numEntriesAdded = 0U; do { // may (rarely) generate duplicates to be counted internally auto id = randomString(); ServiceDescription sd(id, id, id); auto result = this->sut.add(sd); if (result.has_error()) { break; } numEntriesAdded++; } while (true); // duplicates do not count to the max and may be generated randomly, // but for the contract we only need to guarantee that we can at least addPublisher // the configured max constexpr auto MAX = ServiceRegistry::CAPACITY; EXPECT_GE(numEntriesAdded, MAX); } TYPED_TEST(ServiceRegistry_test, SearchInFullRegistryWorks) { ::testing::Test::RecordProperty("TEST_ID", "5c4519d4-1873-4837-a3eb-0367106fb9b5"); constexpr auto CAP = string_t::capacity(); string_t fixedId(iox::cxx::TruncateToCapacity, std::string(CAP, '0')); ServiceDescription lastAdded; do { auto id = randomString(); ServiceDescription sd(fixedId, fixedId, id); auto result = this->sut.add(sd); if (result.has_error()) { break; } lastAdded = sd; } while (true); // removePublisher the last and replace it with a unique service description this->sut->purge(lastAdded); // is unique (random does not generate 0s) and last if a vector is used internally // for almost worst case search (search on last string will terminate early whp) auto id = randomString(CAP - 1); id.unsafe_append("0"); ServiceDescription uniqueSd(fixedId, fixedId, id); auto result = this->sut.add(uniqueSd); EXPECT_FALSE(result.has_error()); auto& service = uniqueSd.getServiceIDString(); auto& instance = uniqueSd.getInstanceIDString(); auto& event = uniqueSd.getEventIDString(); // This is close to a worst case search but not quite due to randomness in the last string. // Different strings are required as we need different strings to create a full registry, // and randomness is the easiest way to achieve this. // It could also be achieved with determinstic string enumeration instead of randomness, // but it is more cumbersome and not required here. this->find(service, instance, event); ASSERT_EQ(this->searchResult.size(), 1); } TYPED_TEST(ServiceRegistry_test, FunctionIsAppliedToAllEntriesInSearchResult) { ::testing::Test::RecordProperty("TEST_ID", "b7828085-d879-43b7-9fee-e5e88cf36995"); iox::capro::ServiceDescription service1("a", "b", "b"); iox::capro::ServiceDescription service2("b", "c", "c"); iox::capro::ServiceDescription service3("a", "b", "d"); ASSERT_FALSE(this->sut.add(service1).has_error()); ASSERT_FALSE(this->sut.add(service2).has_error()); ASSERT_FALSE(this->sut.add(service3).has_error()); this->find(iox::capro::IdString_t("a"), iox::capro::IdString_t("b"), iox::capro::Wildcard); ASSERT_THAT(this->searchResult.size(), Eq(2)); EXPECT_THAT(this->searchResult[0].serviceDescription, Eq(service1)); EXPECT_THAT(this->searchResult[1].serviceDescription, Eq(service3)); } TYPED_TEST(ServiceRegistry_test, NoFunctionIsAppliedToEmptySearchResult) { ::testing::Test::RecordProperty("TEST_ID", "220213cb-8fdf-4fd0-b8e2-24a96f11bfbc"); iox::capro::ServiceDescription service1("a", "b", "b"); iox::capro::ServiceDescription service2("b", "c", "c"); iox::capro::ServiceDescription service3("a", "b", "d"); ASSERT_FALSE(this->sut.add(service1).has_error()); ASSERT_FALSE(this->sut.add(service2).has_error()); ASSERT_FALSE(this->sut.add(service3).has_error()); this->find(iox::capro::Wildcard, iox::capro::IdString_t("a"), iox::capro::Wildcard); EXPECT_THAT(this->searchResult.size(), Eq(0)); } TYPED_TEST(ServiceRegistry_test, FindWithEmptyCallableDoesNotDie) { ::testing::Test::RecordProperty("TEST_ID", "4ac27528-8650-4d8a-8440-4c9bbcbee4fb"); iox::capro::ServiceDescription service("ninjababy", "pow", "pow"); ASSERT_FALSE(this->sut.add(service).has_error()); this->find(iox::capro::Wildcard, iox::capro::Wildcard, iox::capro::Wildcard); } TYPED_TEST(ServiceRegistry_test, FindWithMixOfPublishersAndServersWorks) { ::testing::Test::RecordProperty("TEST_ID", "3e8a9647-69cc-4cad-afb1-9188927aff04"); iox::capro::ServiceDescription service1("a", "a", "a"); iox::capro::ServiceDescription service2("a", "b", "b"); iox::capro::ServiceDescription service3("a", "a", "c"); iox::capro::ServiceDescription service4("a", "a", "d"); iox::capro::ServiceDescription service5("b", "b", "d"); ASSERT_FALSE(this->sut.add(service1).has_error()); ASSERT_FALSE(this->sut.otherAdd(service2).has_error()); ASSERT_FALSE(this->sut.add(service3).has_error()); ASSERT_FALSE(this->sut.otherAdd(service4).has_error()); ASSERT_FALSE(this->sut.add(service5).has_error()); this->find(iox::capro::Wildcard, iox::capro::IdString_t("a"), iox::capro::Wildcard); EXPECT_THAT(this->searchResult.size(), Eq(3U)); auto filtered = this->sut.filter(this->searchResult); // only service1 and service3 match the category (server or publisher), // the other match (service 4) is of a different category ASSERT_EQ(filtered.size(), 2U); EXPECT_EQ(filtered[0].serviceDescription, service1); EXPECT_EQ(filtered[1].serviceDescription, service3); } } // namespace