diff --git a/cpp_utils/include/cpp_utils/ros2_mangling.hpp b/cpp_utils/include/cpp_utils/ros2_mangling.hpp new file mode 100644 index 00000000..a17e5d3d --- /dev/null +++ b/cpp_utils/include/cpp_utils/ros2_mangling.hpp @@ -0,0 +1,345 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// 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. + +#pragma once + +#include +#include + +#include + +namespace eprosima { +namespace utils { + +/** + * @brief Remove a specified prefix from a string. + * + * This function takes a string and a prefix as input. It checks if the string starts with the specified prefix + * followed by a forward slash ('/'). If the prefix exists at the beginning of the string, it removes the prefix + * and the following slash and returns the modified string. If the prefix is not found at the beginning of the + * string, an empty string is returned. + * + * @param[in] name The input string from which the prefix will be removed. + * @param[in] prefix The prefix to be removed from the input string. + * + * @return The modified string with the specified prefix removed, or an empty string if the prefix is not present + * at the beginning of the input string. + */ +CPP_UTILS_DllAPI +std::string remove_prefix( + const std::string& name, + const std::string& prefix); + +/** + * @brief Add a specified prefix to a string. + * + * This function takes a string and a prefix as input. It concatenates the prefix with the input string + * and returns the resulting string with the prefix added. + * + * @param[in] name The input string to which the prefix will be added. + * @param[in] prefix The prefix to be added to the input string. + * + * @return The modified string with the specified prefix added. + */ +CPP_UTILS_DllAPI +std::string add_prefix( + const std::string& name, + const std::string& prefix); + +/** + * @brief Add a specified suffix to a string. + * + * This function takes a string and a suffix as input. It concatenates the suffix with the input string + * and returns the resulting string with the suffix added. + * + * @param[in] name The input string to which the suffix will be added. + * @param[in] suffix The suffix to be added to the input string. + * + * @return The modified string with the specified suffix added. + */ +CPP_UTILS_DllAPI +std::string add_suffix( + const std::string& name, + const std::string& suffix); + +/** + * @brief Get a ROS prefix if it exists at the beginning of a topic name. + * + * This function takes a topic name as input and checks if it starts with any of the ROS prefixes. + * If a matching ROS prefix is found at the beginning of the topic name, the prefix is returned. + * If no matching prefix is found, an empty string is returned. + * + * @param[in] topic_name The input topic name to be checked for a ROS prefix. + * + * @return The ROS prefix found at the beginning of the topic name, or an empty string if no prefix exists. + */ +CPP_UTILS_DllAPI +std::string get_ros_prefix_if_exists( + const std::string& topic_name); + +/** + * @brief Remove a ROS prefix if it exists at the beginning of a topic name. + * + * This function takes a topic name as input and checks if it starts with any of the ROS prefixes. + * If a matching ROS prefix is found at the beginning of the topic name, the prefix is removed, + * and the modified topic name is returned. If no matching prefix is found, the original topic name is returned unchanged. + * + * @param[in] topic_name The name of the topic to be processed. + * + * @return The topic name with the ROS prefix removed, or the original topic name if no prefix exists. + */ +CPP_UTILS_DllAPI +std::string remove_ros_prefix_if_exists( + const std::string& topic_name); + +/** + * @brief Add the ROS topic prefix to a given topic name. + * + * This function takes a topic name as input and adds the ROS topic prefix to it. + * The modified topic name with the prefix added is returned. + * + * @param[in] topic_name The input topic name to which the ROS topic prefix will be added. + * + * @return The modified topic name with the ROS topic prefix added. + */ +CPP_UTILS_DllAPI +std::string add_ros_topic_prefix( + const std::string& topic_name); + +/** + * @brief Add the ROS service requester prefix to a given topic name. + * + * This function takes a topic name as input and adds the ROS service requester prefix to it. + * The modified topic name with the prefix added is returned. + * + * @param[in] topic_name The input topic name to which the ROS service requester prefix will be added. + * + * @return The modified topic name with the ROS service requester prefix added. + */ +CPP_UTILS_DllAPI +std::string add_ros_service_requester_prefix( + const std::string& topic_name); + +/** + * @brief Add the ROS service response prefix to a given topic name. + * + * This function takes a topic name as input and adds the ROS service response prefix to it. + * The modified topic name with the prefix added is returned. + * + * @param[in] topic_name The input topic name to which the ROS service response prefix will be added. + * + * @return The modified topic name with the ROS service response prefix added. + */ +CPP_UTILS_DllAPI +std::string add_ros_service_response_prefix( + const std::string& topic_name); + +/** + * @brief Get a reference to the collection of all ROS prefixes. + * + * This function returns a reference to the collection of ROS prefixes stored in the `ros_prefixes_` variable. + * + * @return A reference to the collection of all ROS prefixes. + */ +CPP_UTILS_DllAPI +const std::vector& get_all_ros_prefixes(); + +/** + * @brief Demangle a ROS topic name by removing the ROS prefix if it exists. + * + * This function takes a ROS topic name as input and attempts to demangle it by removing the ROS prefix. + * If the input topic name contains a ROS prefix, it is removed. If there is no ROS prefix, the topic name + * is returned unchanged. + * + * @param[in] topic_name The name of the topic to be processed. + * + * @return Returns the demangle ROS topic or the original if not a ROS topic. + */ +CPP_UTILS_DllAPI +std::string demangle_if_ros_topic( + const std::string& topic_name); + +/** + * @brief Mangle a given \c topic_name if it starts with "/". + * + * If the topic name begins with "/", it adds the ROS topic prefix to the \c topic_name and then returns it. + * If the topic name does not start with "/", it is returned unchanged. + * + * @param[in] topic_name The name of the topic to be processed. + * + * @return Returns the mangle ROS topic or the original if not a ROS topic (start with "/"). + */ +CPP_UTILS_DllAPI +std::string mangle_if_ros_topic( + const std::string& topic_name); + +/** + * @brief Demangle a DDS type string if it is a ROS type. + * + * This function takes a DDS type string as input and checks if it represents a ROS type. + * If the input type string is not a ROS type, it is returned unchanged. + * If the input type string is a ROS type, it demangles the type string by converting DDS-specific namespace + * separators ("::") to "/", removing "dds" and returns the demangled result. + * + * @param[in] dds_type_string DDS type string to be processed. + * + * @return The demangled ROS type string or the original input if it is not a ROS type. + */ +CPP_UTILS_DllAPI +std::string demangle_if_ros_type( + const std::string& dds_type_string); + +/** + * @brief Mangle a ROS 2 type string into a DDS type string. + * + * This function takes a ROS 2 type string as input and mangles it into a DDS type string. + * If the input string does not contain a ROS 2-specific substring, it is returned unchanged. + * If the ROS 2 substring is found, this function extracts the type namespace and type name, + * converts namespace separators from "/" to "::", and adds the "dds_::" prefix and a trailing + * underscore to the type name. + * + * @param[in] ros2_type_string The input ROS 2 type string to be mangled. + * + * @return The mangled DDS type string or the original input if it does not contain the ROS 2 substring. + */ +CPP_UTILS_DllAPI +std::string mangle_if_ros_type( + const std::string& ros2_type_string); + +/** + * @brief Demangle a ROS topic name by removing the ROS topic prefix. + * + * This function takes a ROS topic name as input and removes the ROS topic prefix if it exists. + * + * @param[in] topic_name The input ROS topic name to be processed. + * + * @return The demangled ROS topic name with the ROS topic prefix removed, or "" if the prefix is not present. + */ +CPP_UTILS_DllAPI +std::string demangle_ros_topic_prefix_from_topic( + const std::string& topic_name); + +/** + * @brief Demangle a ROS service topic name by identifying its type and extracting the appropriate service name. + * + * This function takes a ROS service topic name as input and attempts to demangle it by identifying its type + * (whether it's a reply or request service topic). + * If neither function successfully demangles the input, an empty string is returned. + * + * @param[in] topic_name The input ROS service topic name to be processed. + * + * @return The demangled service name if successfully extracted, or an empty string if demangling fails. + */ +CPP_UTILS_DllAPI +std::string demangle_ros_service_prefix_from_topic( + const std::string& topic_name); +/** + * @brief Demangle a ROS service request topic name by removing the requester prefix and "Request" suffix. + * + * This function takes a ROS service request topic name as input and removes the specified requester prefix and the common "Request" suffix. + * If the input topic name does not contain both the requester prefix + * and the "Request" suffix, an empty string is returned. + * + * @param[in] topic_name The input ROS service request topic name to be processed. + * + * @return The demangled service name with the requester prefix and "Request" suffix removed, or an empty string + * if the necessary prefixes and suffixes are not present. + */ +CPP_UTILS_DllAPI +std::string demangle_ros_service_request_prefix_from_topic( + const std::string& topic_name); + +/** + * @brief Mangle a ROS service request topic name by adding the requester prefix and "Request" suffix. + * + * This function takes a ROS service request topic name as input and mangles it by adding the specified + * requester prefix and the common "Request" suffix. If the input topic name starts with "/", indicating + * it is part of a service request, the mangled service name is returned. Otherwise, an empty string is returned. + * + * @param[in] topic_name The input ROS service request topic name to be processed. + * + * @return The mangled service name with the requester prefix and "Request" suffix added if + * it is part of a service request (start with "/"), otherwise an empty string (""). + */ +CPP_UTILS_DllAPI +std::string mangle_ros_service_request_prefix_in_topic( + const std::string& topic_name); + +/** + * @brief Demangle a ROS service reply topic name by removing the response prefix and "Reply" suffix. + * + * This function takes a ROS service reply topic name as input and demangles it by removing the specified + * response prefix and the common "Reply" suffix. + * If the input topic name does not contain both the response prefix and the "Reply" suffix, an empty string is returned. + * + * @param[in] topic_name The input ROS service reply topic name to be processed. + * + * @return The demangled service name with the response prefix and "Reply" suffix removed, or an empty string + * if the necessary prefixes and suffixes are not present. + */ +CPP_UTILS_DllAPI +std::string demangle_ros_service_reply_prefix_from_topic( + const std::string& topic_name); + +/** + * @brief Mangle a ROS service reply topic name by adding the response prefix and "Reply" suffix. + * + * This function takes a ROS service reply topic name as input and mangles it by adding the specified + * response prefix and the common "Reply" suffix. If the input topic name does not start with "/", indicating + * it is part of a service response, an empty string is returned. + * + * @param[in] topic_name The input ROS service reply topic name to be processed. + * + * @return The mangled service name with the response prefix and "Reply" suffix added if it is part of + * a service response (starts with "/"), otherwise an empty string (""). + */ +CPP_UTILS_DllAPI +std::string mangle_ros_service_reply_prefix_in_topic( + const std::string& topic_name); + +/** + * @brief Demangle a ROS service type name to extract the core type in ROS 2 format. + * + * This function takes a ROS service type name in DDS format as input and demangles it to the ROS 2 format, + * extracting the core type. If the input type name does not match the expected ROS service type format, + * an empty string is returned. + * + * @param[in] dds_type_name The input ROS service type name in DDS format to be processed. + * + * @return The demangled core service type name in ROS 2 format, or an empty string if the input + * does not conform to the expected format. + */ +CPP_UTILS_DllAPI +std::string demangle_service_type_only( + const std::string& dds_type_name); + +/** + * @brief Mangle a ROS 2 service type name to DDS format. + * + * This function takes a ROS 2 service type name as input and mangles it to DDS format by adding necessary + * prefixes and separators. It identifies whether it's a service request or response type and handles the + * conversion accordingly. If the input type name is not in the expected ROS 2 service type format, an empty + * string is returned. + * + * @param[in] ros2_type_name The input ROS 2 service type name to be processed. + * + * @return The mangled ROS 2 service type name in DDS format, or an empty string if the input does not + * conform to the expected format. + */ +CPP_UTILS_DllAPI +std::string mangle_service_type_only( + const std::string& ros2_type_name); + +} /* namespace utils */ +} /* namespace eprosima */ diff --git a/cpp_utils/src/cpp/ros2_mangling.cpp b/cpp_utils/src/cpp/ros2_mangling.cpp new file mode 100644 index 00000000..ce4469cc --- /dev/null +++ b/cpp_utils/src/cpp/ros2_mangling.cpp @@ -0,0 +1,356 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// 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. + +#include + +#include +#include +#include + +#include + +namespace eprosima { +namespace utils { + +const char* const ros_topic_prefix = "rt"; +const char* const ros_service_requester_prefix = "rq"; +const char* const ros_service_response_prefix = "rr"; + +const std::vector ros_prefixes_ = +{ros_topic_prefix, ros_service_requester_prefix, ros_service_response_prefix}; + +const char* const ros2_msgs_format = "_msgs/msg/"; +const char* const ros2_srv_format = "/srv/"; + +std::string remove_prefix( + const std::string& name, + const std::string& prefix) +{ + if (name.rfind(prefix + "/", 0) == 0) + { + return name.substr(prefix.length()); + } + return ""; +} + +std::string add_prefix( + const std::string& name, + const std::string& prefix) +{ + return prefix + name; +} + +std::string add_suffix( + const std::string& name, + const std::string& suffix) +{ + return name + suffix; +} + +std::string get_ros_prefix_if_exists( + const std::string& topic_name) +{ + for (const auto& prefix : ros_prefixes_) + { + if (topic_name.rfind(prefix + "/", 0) == 0) + { + return prefix; + } + } + return ""; +} + +std::string remove_ros_prefix_if_exists( + const std::string& topic_name) +{ + for (const auto& prefix : ros_prefixes_) + { + if (topic_name.rfind(prefix + "/", 0) == 0) + { + return topic_name.substr(prefix.length()); + } + } + return topic_name; +} + +std::string add_ros_topic_prefix( + const std::string& topic_name) +{ + return add_prefix(topic_name, ros_topic_prefix); +} + +std::string add_ros_service_requester_prefix( + const std::string& topic_name) +{ + return add_prefix(topic_name, ros_service_requester_prefix); + +} + +std::string add_ros_service_response_prefix( + const std::string& topic_name) +{ + return add_prefix(topic_name, ros_service_response_prefix); +} + +const std::vector& get_all_ros_prefixes() +{ + return ros_prefixes_; +} + +std::string demangle_if_ros_topic( + const std::string& topic_name) +{ + return remove_ros_prefix_if_exists(topic_name); +} + +std::string mangle_if_ros_topic( + const std::string& topic_name) +{ + if (topic_name.rfind("/", 0) == 0) + { + return add_ros_topic_prefix(topic_name); + } + return topic_name; +} + +std::string demangle_if_ros_type( + const std::string& dds_type_string) +{ + if (dds_type_string[dds_type_string.size() - 1] != '_') + { + // not a ROS type + return dds_type_string; + } + + std::string substring = "dds_::"; + size_t substring_position = dds_type_string.find(substring); + if (substring_position == std::string::npos) + { + // not a ROS type + return dds_type_string; + } + + std::string type_namespace = dds_type_string.substr(0, substring_position); + + replace_all(type_namespace, "::", "/"); + + size_t start = substring_position + substring.size(); + std::string type_name = dds_type_string.substr(start, dds_type_string.length() - 1 - start); + + return type_namespace + type_name; +} + +std::string mangle_if_ros_type( + const std::string& ros2_type_string) +{ + std::string dds_type_string = ros2_type_string; + + size_t substring_position = dds_type_string.find(ros2_msgs_format); + if (substring_position == std::string::npos) + { + return dds_type_string; + } + + std::string substring = "dds_::"; + + std::string type_namespace = dds_type_string.substr(0, substring_position + strlen(ros2_msgs_format)); + std::string type_name = dds_type_string.substr(substring_position + strlen(ros2_msgs_format), + dds_type_string.length() - substring_position - strlen(ros2_msgs_format)); + + if (type_name.length() == 0) + { + return dds_type_string; + } + + replace_all(type_namespace, "/", "::"); + + return type_namespace + "dds_::" + type_name + "_"; +} + +std::string demangle_ros_topic_prefix_from_topic( + const std::string& topic_name) +{ + return remove_prefix(topic_name, ros_topic_prefix); +} + +std::string demangle_ros_service_prefix_from_topic( + const std::string& prefix, + const std::string& topic_name, + std::string suffix) +{ + std::string service_name = remove_prefix(topic_name, prefix); + if ("" == service_name) + { + return ""; + } + + size_t suffix_position = service_name.rfind(suffix); + if (suffix_position != std::string::npos) + { + if (service_name.length() - suffix_position - suffix.length() != 0) + { + logWarning(DDSPIPE_DEMANGLE, + "service topic has service prefix and a suffix, but not at the end" + ", report this: " << topic_name.c_str()); + return ""; + } + } + else + { + logWarning(DDSPIPE_DEMANGLE, + "service topic has prefix but no suffix" + ", report this: " << topic_name.c_str()); + return ""; + } + return service_name.substr(0, suffix_position); +} + +std::string mangle_ros_service_in_topic( + const std::string& prefix, + const std::string& topic_name, + const std::string suffix) +{ + size_t topic_name_position = topic_name.rfind("/"); + if (topic_name_position != 0) + { + return ""; + } + + std::string service_name = add_prefix(topic_name, prefix); + service_name = add_suffix(service_name, suffix); + + return service_name; +} + +std::string demangle_ros_service_prefix_from_topic( + const std::string& topic_name) +{ + const std::string demangled_topic = demangle_ros_service_reply_prefix_from_topic(topic_name); + if ("" != demangled_topic) + { + return demangled_topic; + } + return demangle_ros_service_request_prefix_from_topic(topic_name); +} + +std::string demangle_ros_service_request_prefix_from_topic( + const std::string& topic_name) +{ + return demangle_ros_service_prefix_from_topic(ros_service_requester_prefix, topic_name, "Request"); +} + +std::string mangle_ros_service_request_prefix_in_topic( + const std::string& topic_name) +{ + return mangle_ros_service_in_topic(ros_service_requester_prefix, topic_name, "Request"); +} + +std::string demangle_ros_service_reply_prefix_from_topic( + const std::string& topic_name) +{ + return demangle_ros_service_prefix_from_topic(ros_service_response_prefix, topic_name, "Reply"); +} + +std::string mangle_ros_service_reply_prefix_in_topic( + const std::string& topic_name) +{ + return mangle_ros_service_in_topic(ros_service_response_prefix, topic_name, "Reply"); +} + +std::string demangle_service_type_only( + const std::string& dds_type_name) +{ + std::string ns_substring = "dds_::"; + size_t ns_substring_position = dds_type_name.find(ns_substring); + if (std::string::npos == ns_substring_position) + { + // not a ROS service type + return ""; + } + auto suffixes = { + std::string("_Response_"), + std::string("_Request_"), + }; + std::string found_suffix = ""; + size_t suffix_position = 0; + for (auto suffix : suffixes) + { + suffix_position = dds_type_name.rfind(suffix); + if (suffix_position != std::string::npos) + { + if (dds_type_name.length() - suffix_position - suffix.length() != 0) + { + logWarning(DDSPIPE_DEMANGLE, + "service type contains 'dds_::' and a suffix, but not at the end" + ", report this: " << dds_type_name.c_str()); + continue; + } + found_suffix = suffix; + break; + } + } + if (std::string::npos == suffix_position) + { + logWarning(DDSPIPE_DEMANGLE, + "service type contains 'dds_::' but does not have a suffix" + ", report this: " << dds_type_name.c_str()); + return ""; + } + // everything checks out, reformat it from '[type_namespace::]dds_::' + // to '[type_namespace/]' + std::string type_namespace = dds_type_name.substr(0, ns_substring_position); + replace_all(type_namespace, "::", "/"); + size_t start = ns_substring_position + ns_substring.length(); + std::string type_name = dds_type_name.substr(start, suffix_position - start); + return type_namespace + type_name; +} + +std::string mangle_service_type_only( + const std::string& ros2_type_name) +{ + size_t ns_substring_srv_position = ros2_type_name.find(ros2_srv_format); + if (std::string::npos == ns_substring_srv_position) + { + return ""; + } + + std::string dds_type_name = + ros2_type_name.substr(0, ns_substring_srv_position + strlen(ros2_srv_format)) + "dds_::"; + dds_type_name = dds_type_name + ros2_type_name.substr(ns_substring_srv_position + strlen( + ros2_srv_format), + ros2_type_name.length() - ns_substring_srv_position - strlen(ros2_srv_format)); + + size_t ns_substring_rq_position = ros2_type_name.find("rq/"); + size_t ns_substring_rr_position = ros2_type_name.find("rr/"); + + if (0 == ns_substring_rq_position) + { + dds_type_name = dds_type_name + "_Request_"; + } + else if (0 == ns_substring_rr_position) + { + dds_type_name = dds_type_name + "_Response_"; + } + else + { + return ""; + } + + replace_all(dds_type_name, "/", "::"); + + return dds_type_name; +} + +} /* namespace utils */ +} /* namespace eprosima */ diff --git a/cpp_utils/test/labels/CMakeLists.txt b/cpp_utils/test/labels/CMakeLists.txt index a92ef04f..ccd8cc3c 100644 --- a/cpp_utils/test/labels/CMakeLists.txt +++ b/cpp_utils/test/labels/CMakeLists.txt @@ -19,5 +19,5 @@ set_test_label_file(${CMAKE_CURRENT_SOURCE_DIR}/XTSAN.list "xtsan") # Set list of tests that can fail in windows if (WIN32) - set_test_label_file(${CMAKE_CURRENT_SOURCE_DIR}/WINDOWS_TEST_XFAIL.list "xfail") + set_test_label_file(${CMAKE_CURRENT_SOURCE_DIR}/WINDOWS_XFAIL.list "xfail") endif() diff --git a/cpp_utils/test/unittest/CMakeLists.txt b/cpp_utils/test/unittest/CMakeLists.txt index e484e0ab..16588195 100644 --- a/cpp_utils/test/unittest/CMakeLists.txt +++ b/cpp_utils/test/unittest/CMakeLists.txt @@ -24,6 +24,7 @@ add_subdirectory(math) add_subdirectory(math/random) add_subdirectory(memory) add_subdirectory(return_code) +add_subdirectory(ros2_mangling) add_subdirectory(testing) add_subdirectory(thread_pool) add_subdirectory(time) diff --git a/cpp_utils/test/unittest/ros2_mangling/CMakeLists.txt b/cpp_utils/test/unittest/ros2_mangling/CMakeLists.txt new file mode 100644 index 00000000..b6b01dea --- /dev/null +++ b/cpp_utils/test/unittest/ros2_mangling/CMakeLists.txt @@ -0,0 +1,65 @@ +# Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +# +# 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. + +################# +# Demangle Test # +################# + +set(TEST_NAME ROS2ManglingTest) + +set(TEST_SOURCES + ROS2ManglingTest.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/exception/Exception.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/Formatter.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/math/math_extension.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/utils.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/ros2_mangling.cpp + ) + +set(TEST_LIST + remove_prefix + add_prefix + add_suffix + get_ros_prefix_if_exists + remove_ros_prefix_if_exists + add_ros_topic_prefix + add_ros_service_requester_prefix + add_ros_service_response_prefix + get_all_ros_prefixes + demangle_if_ros_topic + mangle_if_ros_topic + demangle_if_ros_type + mangle_if_ros_type + demangle_ros_topic_prefix_from_topic + demangle_ros_service_prefix_from_topic + demangle_ros_service_request_prefix_from_topic + mangle_ros_service_request_prefix_in_topic + demangle_ros_service_reply_prefix_from_topic + mangle_ros_service_reply_prefix_in_topic + demangle_service_type_only + mangle_service_type_only + ) + +set(TEST_EXTRA_LIBRARIES + fastcdr + fastrtps + $<$:iphlpapi$Shlwapi> + ) + +add_unittest_executable( + "${TEST_NAME}" + "${TEST_SOURCES}" + "${TEST_LIST}" + "${TEST_EXTRA_LIBRARIES}" + ) diff --git a/cpp_utils/test/unittest/ros2_mangling/ROS2ManglingTest.cpp b/cpp_utils/test/unittest/ros2_mangling/ROS2ManglingTest.cpp new file mode 100644 index 00000000..0bf367e6 --- /dev/null +++ b/cpp_utils/test/unittest/ros2_mangling/ROS2ManglingTest.cpp @@ -0,0 +1,510 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// 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. + +#include +#include + + +#include + +using namespace eprosima::utils; + +/** + * Test remove_prefix() method + * + * CASES: + * - Name: "hello" Prefix: "world" Return: "" + * - Name: "hello/rt" Prefix: "rt" Return: "" + * - Name: "rt/hello" Prefix: "rt/" Return: "" + * - Name: "rt/hello" Prefix: "rt" Return: "/hello" + */ +TEST(ROS2ManglingTest, remove_prefix) +{ + EXPECT_EQ("", remove_prefix("hello", "world")); + EXPECT_EQ("", remove_prefix("hello/rt", "rt")); + EXPECT_EQ("", remove_prefix("rt/hello", "rt/")); + EXPECT_EQ("/hello", remove_prefix("rt/hello", "rt")); +} + +/** + * Test add_prefix() method + * + * CASES: + * - Name: "hello" Prefix: "world" Return: "worldhello" + * - Name: "hello" Prefix: "rt" Return: "rthello" + * - Name: "/hello" Prefix: "rt/" Return: "rt//hello" + * - Name: "/hello" Prefix: "rt" Return: "rt/hello" + */ +TEST(ROS2ManglingTest, add_prefix) +{ + EXPECT_EQ("worldhello", add_prefix("hello", "world")); + EXPECT_EQ("rthello", add_prefix("hello", "rt")); + EXPECT_EQ("rt//hello", add_prefix("/hello", "rt/")); + EXPECT_EQ("rt/hello", add_prefix("/hello", "rt")); +} + +/** + * Test add_suffix() method + * + * CASES: + * - Name: "hello" Suffix: "world" Return: "helloworld" + * - Name: "hello" Suffix: "rt" Return: "hellort" + * - Name: "/hello" Suffix: "rt/" Return: "/hellort/" + * - Name: "/hello" Suffix: "rt" Return: "/hellort" + * - Name: "rt/" Suffix: "hello" Return: "rt/hello" + */ +TEST(ROS2ManglingTest, add_suffix) +{ + EXPECT_EQ("helloworld", add_suffix("hello", "world")); + EXPECT_EQ("hellort", add_suffix("hello", "rt")); + EXPECT_EQ("/hellort/", add_suffix("/hello", "rt/")); + EXPECT_EQ("/hellort", add_suffix("/hello", "rt")); + EXPECT_EQ("rt/hello", add_suffix("rt/", "hello")); +} + +/** + * Test get_ros_prefix_if_exists() method + * + * CASES: + * - ROS 2 Topic Name: "hello" Return: "" + * - ROS 2 Topic Name: "hello/rt" Return: "" + * - ROS 2 Topic Name: "rt/hello" Return: "rt" + * - ROS 2 Topic Name: "/rt/hello" Return: "" + * - ROS 2 Topic Name: "hello/rq" Return: "" + * - ROS 2 Topic Name: "rq/hello" Return: "rq" + * - ROS 2 Topic Name: "/rq/hello" Return: "" + * - ROS 2 Topic Name: "hello/rr" Return: "" + * - ROS 2 Topic Name: "rr/hello" Return: "rr" + * - ROS 2 Topic Name: "/rr/hello" Return: "" + */ +TEST(ROS2ManglingTest, get_ros_prefix_if_exists) +{ + EXPECT_EQ("", get_ros_prefix_if_exists("hello")); + + // ros_topic_prefix + EXPECT_EQ("", get_ros_prefix_if_exists("hello/rt")); + EXPECT_EQ("rt", get_ros_prefix_if_exists("rt/hello")); + EXPECT_EQ("", get_ros_prefix_if_exists("/rt/hello")); + + // ros_service_requester_prefix + EXPECT_EQ("", get_ros_prefix_if_exists("hello/rq")); + EXPECT_EQ("rq", get_ros_prefix_if_exists("rq/hello")); + EXPECT_EQ("", get_ros_prefix_if_exists("/rq/hello")); + + // ros_service_requester_prefix + EXPECT_EQ("", get_ros_prefix_if_exists("hello/rr")); + EXPECT_EQ("rr", get_ros_prefix_if_exists("rr/hello")); + EXPECT_EQ("", get_ros_prefix_if_exists("/rr/hello")); +} + +/** + * Test remove_ros_prefix_if_exists() method + * + * CASES: + * - ROS 2 Topic Name: "hello" Return: "" + * - ROS 2 Topic Name: "hello/rt" Return: "hello/rt" + * - ROS 2 Topic Name: "rt/hello" Return: "/hello" + * - ROS 2 Topic Name: "/rt/hello" Return: "/rt/hello" + * - ROS 2 Topic Name: "hello/rq" Return: "hello/rq" + * - ROS 2 Topic Name: "rq/hello" Return: "/hello" + * - ROS 2 Topic Name: "/rq/hello" Return: "/rq/hello" + * - ROS 2 Topic Name: "hello/rr" Return: "hello/rr" + * - ROS 2 Topic Name: "rr/hello" Return: "/hello" + * - ROS 2 Topic Name: "/rr/hello" Return: "/rr/hello" + */ +TEST(ROS2ManglingTest, remove_ros_prefix_if_exists) +{ + EXPECT_EQ("hello", remove_ros_prefix_if_exists("hello")); + + // ros_topic_prefix + EXPECT_EQ("hello/rt", remove_ros_prefix_if_exists("hello/rt")); + EXPECT_EQ("/hello", remove_ros_prefix_if_exists("rt/hello")); + EXPECT_EQ("/rt/hello", remove_ros_prefix_if_exists("/rt/hello")); + + // ros_service_requester_prefix + EXPECT_EQ("hello/rq", remove_ros_prefix_if_exists("hello/rq")); + EXPECT_EQ("/hello", remove_ros_prefix_if_exists("rq/hello")); + EXPECT_EQ("/rq/hello", remove_ros_prefix_if_exists("/rq/hello")); + + // ros_service_requester_prefix + EXPECT_EQ("hello/rr", remove_ros_prefix_if_exists("hello/rr")); + EXPECT_EQ("/hello", remove_ros_prefix_if_exists("rr/hello")); + EXPECT_EQ("/rr/hello", remove_ros_prefix_if_exists("/rr/hello")); +} + +/** + * Test add_ros_topic_prefix() method + * + * CASES: + * - ROS 2 Topic Name: "hello" Return: "rthello" + * - ROS 2 Topic Name: "hello/rt" Return: "rthello/rt" + * - ROS 2 Topic Name: "/hello" Return: "rt/hello" + * - ROS 2 Topic Name: "rt/hello" Return: "rtrt/hello" + */ +TEST(ROS2ManglingTest, add_ros_topic_prefix) +{ + EXPECT_EQ("rthello", add_ros_topic_prefix("hello")); + EXPECT_EQ("rthello/rt", add_ros_topic_prefix("hello/rt")); + EXPECT_EQ("rt/hello", add_ros_topic_prefix("/hello")); + EXPECT_EQ("rtrt/hello", add_ros_topic_prefix("rt/hello")); +} + +/** + * Test add_ros_service_requester_prefix() method + * + * CASES: + * - ROS 2 Topic Name: "hello" Return: "rqhello" + * - ROS 2 Topic Name: "hello/rq" Return: "rqhello/rq" + * - ROS 2 Topic Name: "/hello" Return: "rq/hello" + * - ROS 2 Topic Name: "rq/hello" Return: "rqrq/hello" + */ +TEST(ROS2ManglingTest, add_ros_service_requester_prefix) +{ + EXPECT_EQ("rqhello", add_ros_service_requester_prefix("hello")); + EXPECT_EQ("rqhello/rq", add_ros_service_requester_prefix("hello/rq")); + EXPECT_EQ("rq/hello", add_ros_service_requester_prefix("/hello")); + EXPECT_EQ("rqrq/hello", add_ros_service_requester_prefix("rq/hello")); +} + +/** + * Test add_ros_service_response_prefix() method + * + * CASES: + * - ROS 2 Topic Name: "hello" Return: "rrhello" + * - ROS 2 Topic Name: "hello/rr" Return: "rrhello/rr" + * - ROS 2 Topic Name: "/hello" Return: "rr/hello" + * - ROS 2 Topic Name: "rr/hello" Return: "rrrr/hello" + */ +TEST(ROS2ManglingTest, add_ros_service_response_prefix) +{ + EXPECT_EQ("rrhello", add_ros_service_response_prefix("hello")); + EXPECT_EQ("rrhello/rr", add_ros_service_response_prefix("hello/rr")); + EXPECT_EQ("rr/hello", add_ros_service_response_prefix("/hello")); + EXPECT_EQ("rrrr/hello", add_ros_service_response_prefix("rr/hello")); +} + +/** + * Test get_all_ros_prefixes() method + */ +TEST(ROS2ManglingTest, get_all_ros_prefixes) +{ + const std::vector ros_prefixes = {"rt", "rq", "rr"}; + EXPECT_EQ(ros_prefixes, get_all_ros_prefixes()); +} + +/** + * Test demangle_if_ros_topic() method + * + * CASES: + * - Topic Name: "hello" Return: "hello" + * - Topic Name: "hello/rt" Return: "hello/rt" + * - Topic Name: "rt/hello" Return: "/hello" + * - Topic Name: "/rt/hello" Return: "/rt/hello" + * - Topic Name: "hello/rq" Return: "hello/rq" + * - Topic Name: "rq/hello" Return: "/hello" + * - Topic Name: "/rq/hello" Return: "/rq/hello" + * - Topic Name: "hello/rr" Return: "hello/rr" + * - Topic Name: "rr/hello" Return: "/hello" + * - Topic Name: "/rr/hello" Return: "/rr/hello" + */ +TEST(ROS2ManglingTest, demangle_if_ros_topic) +{ + EXPECT_EQ("hello", demangle_if_ros_topic("hello")); + + // ros_topic_prefix + EXPECT_EQ("hello/rt", demangle_if_ros_topic("hello/rt")); + EXPECT_EQ("/hello", demangle_if_ros_topic("rt/hello")); + EXPECT_EQ("/rt/hello", demangle_if_ros_topic("/rt/hello")); + + // ros_service_requester_prefix + EXPECT_EQ("hello/rq", demangle_if_ros_topic("hello/rq")); + EXPECT_EQ("/hello", demangle_if_ros_topic("rq/hello")); + EXPECT_EQ("/rq/hello", demangle_if_ros_topic("/rq/hello")); + + // ros_service_requester_prefix + EXPECT_EQ("hello/rr", demangle_if_ros_topic("hello/rr")); + EXPECT_EQ("/hello", demangle_if_ros_topic("rr/hello")); + EXPECT_EQ("/rr/hello", demangle_if_ros_topic("/rr/hello")); +} + +/** + * Test mangle_if_ros_topic() method + * + * CASES: + * - Topic Name: "hello" Return: "hello" + * - Topic Name: "hello/rt" Return: "hello/rt" + * - Topic Name: "/hello" Return: "rt/hello" + * - Topic Name: "/rt/hello" Return: "rt/rt/hello" + */ +TEST(ROS2ManglingTest, mangle_if_ros_topic) +{ + EXPECT_EQ("hello", mangle_if_ros_topic("hello")); + EXPECT_EQ("hello/rt", mangle_if_ros_topic("hello/rt")); + EXPECT_EQ("rt/hello", mangle_if_ros_topic("/hello")); + EXPECT_EQ("rt/rt/hello", mangle_if_ros_topic("/rt/hello")); +} + +/** + * Test demangle_if_ros_type() method + * + * CASES: + * - Type Name: "hello" Return: "hello" + * - Type Name: "msg::dds_" Return: "msg::dds_" + * - Type Name: "msgs::msg::" Return: "msgs::msg::" + * - Type Name: "msgs::msg::dds_" Return: "msgs::msg::dds_" + * - Type Name: "msg::dds_::hello" Return: "msg::dds_::hello" + * - Type Name: "std_msgs::msg::dds_::String_" Return: "std_msgs/msg/String" + */ +TEST(ROS2ManglingTest, demangle_if_ros_type) +{ + // not a ROS type + EXPECT_EQ("hello", demangle_if_ros_type("hello")); + EXPECT_EQ("msg::dds_", demangle_if_ros_type("msg::dds_")); + EXPECT_EQ("msgs::msg::", demangle_if_ros_type("msgs::msg::")); + EXPECT_EQ("msgs::msg::dds_", demangle_if_ros_type("msgs::msg::dds_")); + EXPECT_EQ("msgs::msg::hello", demangle_if_ros_type("msgs::msg::hello")); + + // ROS type + EXPECT_EQ("std_msgs/msg/String", demangle_if_ros_type("std_msgs::msg::dds_::String_")); +} + +/** + * Test mangle_if_ros_type() method + * + * CASES: + * - Type Name: "hello" Return: "hello" + * - Type Name: "msg" Return: "msg" + * - Type Name: "std_msgs/msg/" Return: "std_msgs/msg/" + * - Type Name: "std_msgs/msg/String" Return: "std_msgs::msg::dds_::String_" + */ +TEST(ROS2ManglingTest, mangle_if_ros_type) +{ + // not a ROS type + EXPECT_EQ("hello", mangle_if_ros_type("hello")); + EXPECT_EQ("msg", mangle_if_ros_type("msg")); + EXPECT_EQ("std_msgs/msg/", mangle_if_ros_type("std_msgs/msg/")); + + // ROS type + EXPECT_EQ("std_msgs::msg::dds_::String_", mangle_if_ros_type("std_msgs/msg/String")); +} + +/** + * Test demangle_ros_topic_prefix_from_topic() method + * + * CASES: + * - Topic Name: "hello" Return: "" + * - Topic Name: "hello/rt" Return: "" + * - Topic Name: "rt/hello" Return: "/hello" + * - Topic Name: "hello/rq" Return: "" + * - Topic Name: "rq/hello" Return: "" + * - Topic Name: "hello/rr" Return: "" + * - Topic Name: "rr/hello" Return: "" + */ +TEST(ROS2ManglingTest, demangle_ros_topic_prefix_from_topic) +{ + EXPECT_EQ("", demangle_ros_topic_prefix_from_topic("hello")); + EXPECT_EQ("", demangle_ros_topic_prefix_from_topic("hello/rt")); + EXPECT_EQ("/hello", demangle_ros_topic_prefix_from_topic("rt/hello")); + + EXPECT_EQ("", demangle_ros_topic_prefix_from_topic("hello/rq")); + EXPECT_EQ("", demangle_ros_topic_prefix_from_topic("rq/hello")); + + EXPECT_EQ("", demangle_ros_topic_prefix_from_topic("hello/rr")); + EXPECT_EQ("", demangle_ros_topic_prefix_from_topic("rr/hello")); +} + +/** + * Test demangle_ros_service_prefix_from_topic() method + * + * CASES: + * - Service Name: "hello" Return: "" + * - Service Name: "rq/hello" Return: "" + * - Service Name: "rr/hello" Return: "" + * - Service Name: "rq/hello/worldRequest" Return: "/hello/world" + * - Service Name: "rr/hello/worldReply" Return: "/hello/world" + * - Service Name: "Request/hello/worldrq" Return: "" + * - Service Name: "Reply/hello/worldrr" Return: "" + */ +TEST(ROS2ManglingTest, demangle_ros_service_prefix_from_topic) +{ + EXPECT_EQ("", demangle_ros_service_prefix_from_topic("hello")); + + EXPECT_EQ("", demangle_ros_service_prefix_from_topic("rq/hello")); + EXPECT_EQ("", demangle_ros_service_prefix_from_topic("rr/hello")); + + EXPECT_EQ("/hello/world", demangle_ros_service_prefix_from_topic("rq/hello/worldRequest")); + EXPECT_EQ("/hello/world", demangle_ros_service_prefix_from_topic("rr/hello/worldReply")); + + EXPECT_EQ("", demangle_ros_service_prefix_from_topic("Request/hello/worldrq")); + EXPECT_EQ("", demangle_ros_service_prefix_from_topic("Reply/hello/worldrr")); +} + +/** + * Test demangle_ros_service_request_prefix_from_topic() method + * + * CASES: + * - Service Request Name: "hello" Return: "" + * - Service Request Name: "rq/hello" Return: "" + * - Service Request Name: "rr/hello" Return: "" + * - Service Request Name: "rq/hello/worldRequest" Return: "/hello/world" + * - Service Request Name: "rr/hello/worldReply" Return: "" + * - Service Request Name: "Request/hello/worldrq" Return: "" + * - Service Request Name: "Reply/hello/worldrr" Return: "" + */ +TEST(ROS2ManglingTest, demangle_ros_service_request_prefix_from_topic) +{ + EXPECT_EQ("", demangle_ros_service_request_prefix_from_topic("hello")); + + EXPECT_EQ("", demangle_ros_service_request_prefix_from_topic("rq/hello")); + EXPECT_EQ("", demangle_ros_service_request_prefix_from_topic("rr/hello")); + + EXPECT_EQ("/hello/world", demangle_ros_service_request_prefix_from_topic("rq/hello/worldRequest")); + EXPECT_EQ("", demangle_ros_service_request_prefix_from_topic("rr/hello/worldReply")); + + EXPECT_EQ("", demangle_ros_service_request_prefix_from_topic("Request/hello/worldrq")); + EXPECT_EQ("", demangle_ros_service_request_prefix_from_topic("Reply/hello/worldrr")); +} + +/** + * Test mangle_ros_service_request_prefix_in_topic() method + * + * CASES: + * - Service Request Name: "hello" Return: "" + * - Service Request Name: "rq/hello" Return: "" + * - Service Request Name: "rr/hello" Return: "" + * - Service Request Name: "/hello" Return: "rq/helloRequest" + */ +TEST(ROS2ManglingTest, mangle_ros_service_request_prefix_in_topic) +{ + EXPECT_EQ("", mangle_ros_service_request_prefix_in_topic("hello")); + + EXPECT_EQ("", mangle_ros_service_request_prefix_in_topic("rq/hello")); + EXPECT_EQ("", mangle_ros_service_request_prefix_in_topic("rr/hello")); + + EXPECT_EQ("rq/helloRequest", mangle_ros_service_request_prefix_in_topic("/hello")); +} + +/** + * Test demangle_ros_service_reply_prefix_from_topic() method + * + * CASES: + * - Service Reply Name: "hello" Return: "" + * - Service Reply Name: "rq/hello" Return: "" + * - Service Reply Name: "rr/hello" Return: "" + * - Service Reply Name: "rq/hello/worldRequest" Return: "" + * - Service Reply Name: "rr/hello/worldReply" Return: "/hello/world" + * - Service Reply Name: "Request/hello/worldrq" Return: "" + * - Service Reply Name: "Reply/hello/worldrr" Return: "" + */ +TEST(ROS2ManglingTest, demangle_ros_service_reply_prefix_from_topic) +{ + EXPECT_EQ("", demangle_ros_service_reply_prefix_from_topic("hello")); + + EXPECT_EQ("", demangle_ros_service_reply_prefix_from_topic("rq/hello")); + EXPECT_EQ("", demangle_ros_service_reply_prefix_from_topic("rr/hello")); + + EXPECT_EQ("", demangle_ros_service_reply_prefix_from_topic("rq/hello/worldRequest")); + EXPECT_EQ("/hello/world", demangle_ros_service_reply_prefix_from_topic("rr/hello/worldReply")); + + EXPECT_EQ("", demangle_ros_service_reply_prefix_from_topic("Request/hello/worldrq")); + EXPECT_EQ("", demangle_ros_service_reply_prefix_from_topic("Reply/hello/worldrr")); +} + +/** + * Test mangle_ros_service_reply_prefix_in_topic() method + * + * CASES: + * - Service Reply Name: "hello" Return: "" + * - Service Reply Name: "rq/hello" Return: "" + * - Service Reply Name: "rr/hello" Return: "" + * - Service Reply Name: "/hello" Return: "rr/helloReply" + */ +TEST(ROS2ManglingTest, mangle_ros_service_reply_prefix_in_topic) +{ + EXPECT_EQ("", mangle_ros_service_reply_prefix_in_topic("hello")); + + EXPECT_EQ("", mangle_ros_service_reply_prefix_in_topic("rq/hello")); + EXPECT_EQ("", mangle_ros_service_reply_prefix_in_topic("rr/hello")); + + EXPECT_EQ("rr/helloReply", mangle_ros_service_reply_prefix_in_topic("/hello")); +} + +/** + * Test demangle_service_type_only() method + * + * CASES: + * - DDS Service Name: "hello" Return: "" + * - DDS Service Name: "rq/hello" Return: "" + * - DDS Service Name: "rr/hello" Return: "" + * - DDS Service Name: "rt/hello" Return: "" + * - DDS Service Name: "rq::dds_::hello" Return: "" + * - DDS Service Name: "rr::dds_::hello" Return: "" + * - DDS Service Name: "rt::dds_::hello" Return: "" + * - DDS Service Name: "rq::srv::dds_::hello_Request_" Return: "rq/srv/hello" + * - DDS Service Name: "rr::srv::dds_::hello_Request_" Return: "rr/srv/hello" + */ +TEST(ROS2ManglingTest, demangle_service_type_only) +{ + // not a ROS service type + EXPECT_EQ("", demangle_service_type_only("hello")); + EXPECT_EQ("", demangle_service_type_only("rq/hello")); + EXPECT_EQ("", demangle_service_type_only("rr/hello")); + EXPECT_EQ("", demangle_service_type_only("rt/hello")); + + EXPECT_EQ("", demangle_service_type_only("rq::dds_::hello")); + EXPECT_EQ("", demangle_service_type_only("rr::dds_::hello")); + EXPECT_EQ("", demangle_service_type_only("rt::dds_::hello")); + + // ROS service type + EXPECT_EQ("rq/srv/hello", demangle_service_type_only("rq::srv::dds_::hello_Request_")); + EXPECT_EQ("rr/srv/hello", demangle_service_type_only("rr::srv::dds_::hello_Response_")); +} + +/** + * Test mangle_service_type_only() method + * + * CASES: + * - DDS Service Name: "hello" Return: "" + * - DDS Service Name: "rt/hello" Return: "" + * - DDS Service Name: "rq/srv/hello" Return: "rq::srv::dds_::hello_Request_" + * - DDS Service Name: "rr/srv/hello" Return: "rr::srv::dds_::hello_Response_" + * - DDS Service Name: "/srv/hello" Return: "" + * - DDS Service Name: "/srv/hello" Return: "" + * - DDS Service Name: "rq::dds_::hello" Return: "" + * - DDS Service Name: "rr::dds_::hello" Return: "" + * - DDS Service Name: "rt::dds_::hello" Return: "" + */ +TEST(ROS2ManglingTest, mangle_service_type_only) +{ + EXPECT_EQ("", mangle_service_type_only("hello")); + EXPECT_EQ("", mangle_service_type_only("rt/hello")); + + EXPECT_EQ("rq::srv::dds_::hello_Request_", mangle_service_type_only("rq/srv/hello")); + EXPECT_EQ("rr::srv::dds_::hello_Response_", mangle_service_type_only("rr/srv/hello")); + + EXPECT_EQ("", mangle_service_type_only("/srv/hello")); + EXPECT_EQ("", mangle_service_type_only("/srv/hello")); + + EXPECT_EQ("", mangle_service_type_only("rq::dds_::hello")); + EXPECT_EQ("", mangle_service_type_only("rr::dds_::hello")); + EXPECT_EQ("", mangle_service_type_only("rt::dds_::hello")); +} + +int main( + int argc, + char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/versions.md b/versions.md index df1d3edc..edfc39e1 100644 --- a/versions.md +++ b/versions.md @@ -3,6 +3,9 @@ This file includes the released versions of **dev-utils** project along with their contributions to the project. The *Forthcoming* section includes those features added in `main` branch that are not yet in a stable release. +## Forthcoming + +* Add ROS 2 mangling methods. ## Version 0.4.0