diff --git a/rmw/CMakeLists.txt b/rmw/CMakeLists.txt index 261b8f4e..089d5cdf 100644 --- a/rmw/CMakeLists.txt +++ b/rmw/CMakeLists.txt @@ -37,6 +37,7 @@ set(rmw_sources "src/qos_string_conversions.c" "src/sanity_checks.c" "src/security_options.c" + "src/subscription_content_filter_options.c" "src/subscription_options.c" "src/time.c" "src/topic_endpoint_info_array.c" diff --git a/rmw/include/rmw/rmw.h b/rmw/include/rmw/rmw.h index c98011e5..f25c7b07 100644 --- a/rmw/include/rmw/rmw.h +++ b/rmw/include/rmw/rmw.h @@ -91,6 +91,7 @@ extern "C" #include #include +#include "rcutils/allocator.h" #include "rcutils/macros.h" #include "rcutils/types.h" @@ -1117,6 +1118,70 @@ rmw_subscription_get_actual_qos( const rmw_subscription_t * subscription, rmw_qos_profile_t * qos); +/// Set the content filter options for the subscription. +/** + * This function will set a filter expression and an array of expression parameters + * for the given subscription. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | No + * Thread-Safe | No + * Uses Atomics | Maybe [1] + * Lock-Free | Maybe [1] + * [1] implementation defined, check the implementation documentation + * + * \param[in] subscription The subscription to set content filter options. + * \param[in] options The content filter options. + * Use `options.filter_expression` with an empty("") string to + * reset/clean content filtered topic for the subscription. + * \return `RMW_RET_OK` if successful, or + * \return `RMW_RET_INVALID_ARGUMENT` if an argument is null, or + * \return `RMW_RET_INCORRECT_RMW_IMPLEMENTATION` if the `subscription` implementation + * identifier does not match this implementation, or + * \return `RMW_RET_UNSUPPORTED` if the implementation does not support content filtered topic, or + * \return `RMW_RET_ERROR` if an unspecified error occurs. + */ +RMW_PUBLIC +RMW_WARN_UNUSED +rmw_ret_t +rmw_subscription_set_content_filter( + rmw_subscription_t * subscription, + const rmw_subscription_content_filter_options_t * options); + +/// Retrieve the content filter options of the subscription. +/** + * This function will return a content filter options by the given subscription. + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes + * Thread-Safe | No + * Uses Atomics | Maybe [1] + * Lock-Free | Maybe [1] + * [1] implementation defined, check the implementation documentation + * + * \param[in] subscription The subscription object to inspect. + * \param[in] allocator Allocator to be used when populating the content filter options. + * \param[out] options The content filter options. + * \return `RMW_RET_OK` if successful, or + * \return `RMW_RET_INVALID_ARGUMENT` if an argument is null, or + * \return `RMW_RET_INCORRECT_RMW_IMPLEMENTATION` if the `subscription` implementation + * identifier does not match this implementation, or + * \return `RMW_RET_BAD_ALLOC` if memory allocation fails, or + * \return `RMW_RET_UNSUPPORTED` if the implementation does not support content filtered topic, or + * \return `RMW_RET_ERROR` if an unspecified error occurs. + */ +RMW_PUBLIC +RMW_WARN_UNUSED +rmw_ret_t +rmw_subscription_get_content_filter( + const rmw_subscription_t * subscription, + rcutils_allocator_t * allocator, + rmw_subscription_content_filter_options_t * options); + /// Take an incoming ROS message. /** * Take a ROS message already received by the given subscription, removing it from internal queues. diff --git a/rmw/include/rmw/subscription_content_filter_options.h b/rmw/include/rmw/subscription_content_filter_options.h new file mode 100644 index 00000000..b0a28a01 --- /dev/null +++ b/rmw/include/rmw/subscription_content_filter_options.h @@ -0,0 +1,130 @@ +// Copyright 2021 Open Source Robotics Foundation, Inc. +// +// 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. + +#ifndef RMW__SUBSCRIPTION_CONTENT_FILTER_OPTIONS_H_ +#define RMW__SUBSCRIPTION_CONTENT_FILTER_OPTIONS_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "rcutils/allocator.h" +#include "rcutils/types.h" + +#include "rmw/macros.h" +#include "rmw/ret_types.h" +#include "rmw/visibility_control.h" + +typedef struct RMW_PUBLIC_TYPE rmw_subscription_content_filter_options_s +{ + /** + * Specify the criteria to select the data samples of interest. + * + * It is similar to the WHERE part of an SQL clause. + */ + char * filter_expression; + /** + * Give values to the tokens placeholder ‘parameters’ (i.e., "%n" tokens begin from 0) in the + * filter_expression. The number of supplied parameters must fit with the requested values. + * + * It can be NULL if there is no "%n" tokens placeholder in filter_expression. + * The maximum index number must be smaller than 100. + */ + rcutils_string_array_t expression_parameters; +} rmw_subscription_content_filter_options_t; + + +/// Get zero initialized content filter options. +RMW_PUBLIC +rmw_subscription_content_filter_options_t +rmw_get_zero_initialized_content_filter_options(); + + +/// Initialize the given content filter options. +/** + * \param[in] filter_expression The filter expression. + * \param[in] expression_parameters_argc The expression parameters argc. + * \param[in] expression_parameter_argv The expression parameters argv. + * \param[in] allocator The allocator used when copying data to the content filter options. + * \param[out] options The content filter options to be set. + * \returns RMW_RET_INVALID_ARGUMENT, or + * \returns RMW_RET_BAD_ALLOC, or + * \returns RMW_RET_OK + */ +RMW_PUBLIC +rmw_ret_t +rmw_subscription_content_filter_options_init( + const char * filter_expression, + size_t expression_parameters_argc, + const char * expression_parameter_argv[], + const rcutils_allocator_t * allocator, + rmw_subscription_content_filter_options_t * options); + +/// Set the given content filter options. +/** + * \param[in] filter_expression The filter expression. + * \param[in] expression_parameters_argc The expression parameters argc. + * \param[in] expression_parameter_argv The expression parameters argv. + * \param[in] allocator The allocator used when copying data to the content filter options. + * \param[out] options The content filter options to be set. + * \returns RMW_RET_INVALID_ARGUMENT, or + * \returns RMW_RET_BAD_ALLOC, or + * \returns RMW_RET_OK + */ +RMW_PUBLIC +rmw_ret_t +rmw_subscription_content_filter_options_set( + const char * filter_expression, + size_t expression_parameters_argc, + const char * expression_parameter_argv[], + const rcutils_allocator_t * allocator, + rmw_subscription_content_filter_options_t * options); + +/// Copy the given content filter options. +/** + * \param[in] src content filter options to be copied. + * \param[in] allocator allocator used when copying data to the new content filter options. + * \param[out] dst content filter options to be set. + * \returns RMW_RET_INVALID_ARGUMENT, or + * \returns RMW_RET_BAD_ALLOC, or + * \returns RMW_RET_OK + */ +RMW_PUBLIC +rmw_ret_t +rmw_subscription_content_filter_options_copy( + const rmw_subscription_content_filter_options_t * src, + const rcutils_allocator_t * allocator, + rmw_subscription_content_filter_options_t * dst); + + +/// Finalize the content filter options. +/** + * \param[in] options content filter options to be finalized. + * \param[in] allocator allocator used to deallocate the content filter options. + * \returns RMW_RET_INVALID_ARGUMENT, or + * \returns RMW_RET_ERROR, or + * \returns RMW_RET_OK + */ +RMW_PUBLIC +rmw_ret_t +rmw_subscription_content_filter_options_fini( + rmw_subscription_content_filter_options_t * options, + const rcutils_allocator_t * allocator); + +#ifdef __cplusplus +} +#endif + +#endif // RMW__SUBSCRIPTION_CONTENT_FILTER_OPTIONS_H_ diff --git a/rmw/include/rmw/types.h b/rmw/include/rmw/types.h index 0ba286f7..17d0ea89 100644 --- a/rmw/include/rmw/types.h +++ b/rmw/include/rmw/types.h @@ -33,6 +33,7 @@ extern "C" #include "rmw/ret_types.h" #include "rmw/security_options.h" #include "rmw/serialized_message.h" +#include "rmw/subscription_content_filter_options.h" #include "rmw/time.h" #include "rmw/visibility_control.h" @@ -177,6 +178,9 @@ typedef struct RMW_PUBLIC_TYPE rmw_subscription_options_s * Default value is RMW_UNIQUE_NETWORK_FLOW_ENDPOINTS_NOT_REQUIRED. */ rmw_unique_network_flow_endpoints_requirement_t require_unique_network_flow_endpoints; + + /// Used to create a content filter options during subscription creation. + rmw_subscription_content_filter_options_t * content_filter_options; } rmw_subscription_options_t; typedef struct RMW_PUBLIC_TYPE rmw_subscription_s @@ -203,6 +207,9 @@ typedef struct RMW_PUBLIC_TYPE rmw_subscription_s /// Indicates whether this subscription can loan messages bool can_loan_messages; + + /// Indicates whether content filtered topic of this subscription is enabled + bool is_cft_enabled; } rmw_subscription_t; /// A handle to an rmw service diff --git a/rmw/src/subscription_content_filter_options.c b/rmw/src/subscription_content_filter_options.c new file mode 100644 index 00000000..44051745 --- /dev/null +++ b/rmw/src/subscription_content_filter_options.c @@ -0,0 +1,155 @@ +// Copyright 2021 Open Source Robotics Foundation, Inc. +// +// 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 "rcutils/strdup.h" + +#include "rmw/error_handling.h" +#include "rmw/subscription_content_filter_options.h" + +rmw_subscription_content_filter_options_t +rmw_get_zero_initialized_content_filter_options() +{ + return (const rmw_subscription_content_filter_options_t) { + .filter_expression = NULL, + .expression_parameters = rcutils_get_zero_initialized_string_array() + }; // NOLINT(readability/braces): false positive +} + +rmw_ret_t +rmw_subscription_content_filter_options_init( + const char * filter_expression, + size_t expression_parameters_argc, + const char * expression_parameter_argv[], + const rcutils_allocator_t * allocator, + rmw_subscription_content_filter_options_t * options) +{ + RMW_CHECK_ARGUMENT_FOR_NULL(filter_expression, RMW_RET_INVALID_ARGUMENT); + if (expression_parameters_argc > 0) { + RMW_CHECK_ARGUMENT_FOR_NULL(expression_parameter_argv, RMW_RET_INVALID_ARGUMENT); + } + RCUTILS_CHECK_ALLOCATOR(allocator, return RMW_RET_INVALID_ARGUMENT); + RMW_CHECK_ARGUMENT_FOR_NULL(options, RMW_RET_INVALID_ARGUMENT); + + rmw_ret_t ret = RMW_RET_OK; + rcutils_ret_t rcutils_ret; + char * new_filter_expression = NULL; + size_t i; + + new_filter_expression = rcutils_strdup(filter_expression, *allocator); + if (!new_filter_expression) { + RMW_SET_ERROR_MSG("failed to copy filter expression"); + ret = RMW_RET_BAD_ALLOC; + goto failed; + } + + if (expression_parameters_argc > 0) { + rcutils_ret_t rcutils_ret = rcutils_string_array_init( + &options->expression_parameters, expression_parameters_argc, allocator); + if (RCUTILS_RET_OK != rcutils_ret) { + RMW_SET_ERROR_MSG("failed to init string array for expression parameters"); + ret = RMW_RET_BAD_ALLOC; + goto failed; + } + + for (i = 0; i < expression_parameters_argc; i++) { + options->expression_parameters.data[i] = + rcutils_strdup(expression_parameter_argv[i], *allocator); + if (!options->expression_parameters.data[i]) { + RMW_SET_ERROR_MSG("failed to copy expression parameter"); + ret = RMW_RET_BAD_ALLOC; + goto clear_expression_parameters; + } + } + } + + options->filter_expression = new_filter_expression; + + return RMW_RET_OK; + +clear_expression_parameters: + rcutils_ret = rcutils_string_array_fini(&options->expression_parameters); + if (RCUTILS_RET_OK != rcutils_ret) { + RCUTILS_SAFE_FWRITE_TO_STDERR("Failed to fini string array.\n"); + } + +failed: + allocator->deallocate(new_filter_expression, allocator->state); + + return ret; +} + +rmw_ret_t +rmw_subscription_content_filter_options_set( + const char * filter_expression, + size_t expression_parameters_argc, + const char * expression_parameter_argv[], + const rcutils_allocator_t * allocator, + rmw_subscription_content_filter_options_t * options) +{ + rmw_ret_t ret = rmw_subscription_content_filter_options_fini(options, allocator); + if (ret != RMW_RET_OK) { + return ret; + } + + return rmw_subscription_content_filter_options_init( + filter_expression, + expression_parameters_argc, + expression_parameter_argv, + allocator, + options + ); +} + +rmw_ret_t +rmw_subscription_content_filter_options_copy( + const rmw_subscription_content_filter_options_t * src, + const rcutils_allocator_t * allocator, + rmw_subscription_content_filter_options_t * dst) +{ + RMW_CHECK_ARGUMENT_FOR_NULL(src, RMW_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ALLOCATOR(allocator, return RMW_RET_INVALID_ARGUMENT); + RMW_CHECK_ARGUMENT_FOR_NULL(dst, RMW_RET_INVALID_ARGUMENT); + + return rmw_subscription_content_filter_options_set( + src->filter_expression, + src->expression_parameters.size, + (const char **)src->expression_parameters.data, + allocator, + dst + ); +} + +rmw_ret_t +rmw_subscription_content_filter_options_fini( + rmw_subscription_content_filter_options_t * options, + const rcutils_allocator_t * allocator) +{ + RMW_CHECK_ARGUMENT_FOR_NULL(options, RMW_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ALLOCATOR(allocator, return RMW_RET_INVALID_ARGUMENT); + + if (options->filter_expression) { + allocator->deallocate(options->filter_expression, allocator->state); + options->filter_expression = NULL; + } + + rcutils_ret_t ret = rcutils_string_array_fini(&options->expression_parameters); + if (RCUTILS_RET_OK != ret) { + RCUTILS_SAFE_FWRITE_TO_STDERR("Failed to fini string array.\n"); + return RMW_RET_ERROR; + } + + return RMW_RET_OK; +} diff --git a/rmw/src/subscription_options.c b/rmw/src/subscription_options.c index a968ccda..e9efda3f 100644 --- a/rmw/src/subscription_options.c +++ b/rmw/src/subscription_options.c @@ -26,6 +26,7 @@ rmw_get_default_subscription_options(void) .rmw_specific_subscription_payload = NULL, .ignore_local_publications = false, .require_unique_network_flow_endpoints = RMW_UNIQUE_NETWORK_FLOW_ENDPOINTS_NOT_REQUIRED, + .content_filter_options = NULL, }; return subscription_options; } diff --git a/rmw/test/CMakeLists.txt b/rmw/test/CMakeLists.txt index 2ff4486e..e7733c95 100644 --- a/rmw/test/CMakeLists.txt +++ b/rmw/test/CMakeLists.txt @@ -211,3 +211,12 @@ if(TARGET test_network_flow_endpoint_array) target_link_libraries(test_network_flow_endpoint_array ${PROJECT_NAME} osrf_testing_tools_cpp::memory_tools) endif() + +ament_add_gmock(test_subscription_content_filter_options + test_subscription_content_filter_options.cpp + # Append the directory of librmw so it is found at test time. + APPEND_LIBRARY_DIRS "$" +) +if(TARGET test_subscription_content_filter_options) + target_link_libraries(test_subscription_content_filter_options ${PROJECT_NAME}) +endif() diff --git a/rmw/test/test_subscription_content_filter_options.cpp b/rmw/test/test_subscription_content_filter_options.cpp new file mode 100644 index 00000000..7cf689e5 --- /dev/null +++ b/rmw/test/test_subscription_content_filter_options.cpp @@ -0,0 +1,251 @@ +// Copyright 2021 Open Source Robotics Foundation, Inc. +// +// 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 "gmock/gmock.h" + +#include "rcutils/strdup.h" + +#include "./time_bomb_allocator_testing_utils.h" +#include "rmw/error_handling.h" +#include "rmw/subscription_content_filter_options.h" + +TEST(rmw_subscription_content_filter_options, get_zero_init) +{ + rmw_subscription_content_filter_options_t options = + rmw_get_zero_initialized_content_filter_options(); + EXPECT_EQ(options.filter_expression, nullptr); + EXPECT_EQ(options.expression_parameters.size, 0u); + EXPECT_EQ(options.expression_parameters.data, nullptr); +} + +TEST(rmw_subscription_content_filter_options, options_init) +{ + rmw_subscription_content_filter_options_t options = + rmw_get_zero_initialized_content_filter_options(); + EXPECT_EQ(options.filter_expression, nullptr); + EXPECT_EQ(options.expression_parameters.size, 0u); + EXPECT_EQ(options.expression_parameters.data, nullptr); + + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + EXPECT_EQ( + RMW_RET_INVALID_ARGUMENT, + rmw_subscription_content_filter_options_init( + nullptr, 0, nullptr, &allocator, &options)); + rmw_reset_error(); + + EXPECT_EQ( + RMW_RET_INVALID_ARGUMENT, + rmw_subscription_content_filter_options_init( + "filter='p'", 1, nullptr, &allocator, &options)); + rmw_reset_error(); + + EXPECT_EQ( + RMW_RET_INVALID_ARGUMENT, + rmw_subscription_content_filter_options_init( + "filter='p'", 0, nullptr, &allocator, nullptr)); + rmw_reset_error(); + + { + EXPECT_EQ( + RMW_RET_OK, rmw_subscription_content_filter_options_init( + "filter='p'", 0, nullptr, &allocator, &options)); + EXPECT_STREQ(options.filter_expression, "filter='p'"); + + EXPECT_EQ( + RMW_RET_OK, + rmw_subscription_content_filter_options_fini(&options, &allocator)); + } + + { + const char * filter_expression = "(filter1=%0 OR filter1=%1) AND filter2=%2"; + const char * expression_parameters[] = { + "1", "2", "3", + }; + size_t expression_parameters_count = sizeof(expression_parameters) / sizeof(char *); + EXPECT_EQ( + RMW_RET_OK, rmw_subscription_content_filter_options_init( + filter_expression, expression_parameters_count, expression_parameters, &allocator, + &options)); + + EXPECT_STREQ(options.filter_expression, filter_expression); + ASSERT_EQ(expression_parameters_count, options.expression_parameters.size); + for (size_t i = 0; i < expression_parameters_count; ++i) { + EXPECT_STREQ(options.expression_parameters.data[i], expression_parameters[i]); + } + + EXPECT_EQ( + RMW_RET_OK, rmw_subscription_content_filter_options_fini(&options, &allocator)); + } +} + +TEST(rmw_subscription_content_filter_options, options_set) +{ + rmw_subscription_content_filter_options_t options = + rmw_get_zero_initialized_content_filter_options(); + EXPECT_EQ(options.filter_expression, nullptr); + EXPECT_EQ(options.expression_parameters.size, 0u); + EXPECT_EQ(options.expression_parameters.data, nullptr); + + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + + EXPECT_EQ( + RMW_RET_OK, rmw_subscription_content_filter_options_init( + "filter='p'", 0, nullptr, &allocator, &options)); + EXPECT_STREQ(options.filter_expression, "filter='p'"); + + EXPECT_EQ( + RMW_RET_INVALID_ARGUMENT, + rmw_subscription_content_filter_options_set( + nullptr, 0, nullptr, &allocator, &options)); + rmw_reset_error(); + + EXPECT_EQ( + RMW_RET_INVALID_ARGUMENT, + rmw_subscription_content_filter_options_set( + "filter='p'", 1, nullptr, &allocator, &options)); + rmw_reset_error(); + + EXPECT_EQ( + RMW_RET_INVALID_ARGUMENT, + rmw_subscription_content_filter_options_set( + "filter='p'", 0, nullptr, &allocator, nullptr)); + rmw_reset_error(); + + { + EXPECT_EQ( + RMW_RET_OK, rmw_subscription_content_filter_options_set( + "filter='p1'", 0, nullptr, &allocator, &options)); + EXPECT_STREQ(options.filter_expression, "filter='p1'"); + } + + { + const char * filter_expression = "(filter1=%0 OR filter1=%1) AND filter2=%2"; + const char * expression_parameters[] = { + "1", "2", "3", + }; + size_t expression_parameters_count = sizeof(expression_parameters) / sizeof(char *); + EXPECT_EQ( + RMW_RET_OK, rmw_subscription_content_filter_options_set( + filter_expression, expression_parameters_count, expression_parameters, &allocator, + &options)); + + EXPECT_STREQ(options.filter_expression, filter_expression); + ASSERT_EQ(expression_parameters_count, options.expression_parameters.size); + for (size_t i = 0; i < expression_parameters_count; ++i) { + EXPECT_STREQ(options.expression_parameters.data[i], expression_parameters[i]); + } + + EXPECT_EQ( + RMW_RET_OK, rmw_subscription_content_filter_options_fini(&options, &allocator)); + } + + EXPECT_EQ(RMW_RET_OK, rmw_subscription_content_filter_options_fini(&options, &allocator)); +} + +TEST(rmw_subscription_content_filter_options, options_copy) { + rmw_subscription_content_filter_options_t source = + rmw_get_zero_initialized_content_filter_options(); + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + + const char * filter_expression = "(filter1=%0 OR filter1=%1) AND filter2=%2"; + const char * expression_parameters[] = { + "1", "2", "3", + }; + size_t expression_parameters_count = sizeof(expression_parameters) / sizeof(char *); + + EXPECT_EQ( + RMW_RET_OK, rmw_subscription_content_filter_options_init( + filter_expression, expression_parameters_count, expression_parameters, &allocator, &source)); + + rmw_subscription_content_filter_options_t destination = + rmw_get_zero_initialized_content_filter_options(); + EXPECT_EQ( + RMW_RET_INVALID_ARGUMENT, + rmw_subscription_content_filter_options_copy(nullptr, &allocator, &destination)); + rcutils_reset_error(); + + EXPECT_EQ( + RMW_RET_INVALID_ARGUMENT, + rmw_subscription_content_filter_options_copy(&source, nullptr, &destination)); + rcutils_reset_error(); + + EXPECT_EQ( + RMW_RET_INVALID_ARGUMENT, + rmw_subscription_content_filter_options_copy(&source, &allocator, nullptr)); + rcutils_reset_error(); + + rcutils_allocator_t failing_allocator = get_time_bomb_allocator(); + constexpr int expected_num_malloc_calls = 4; + for (int i = 0; i < expected_num_malloc_calls; ++i) { + set_time_bomb_allocator_malloc_count(failing_allocator, i); + EXPECT_EQ( + RMW_RET_BAD_ALLOC, + rmw_subscription_content_filter_options_copy( + &source, &failing_allocator, &destination)); + rcutils_reset_error(); + } + + EXPECT_EQ( + RMW_RET_OK, + rmw_subscription_content_filter_options_copy(&source, &allocator, &destination)); + + EXPECT_STREQ(source.filter_expression, destination.filter_expression); + int res = 0; + EXPECT_EQ( + RCUTILS_RET_OK, + rcutils_string_array_cmp( + &source.expression_parameters, &destination.expression_parameters, &res)); + EXPECT_EQ(res, 0); + + { + // second copy operation + rmw_subscription_content_filter_options_t source2 = + rmw_get_zero_initialized_content_filter_options(); + const char * filter_expression = "filter='p1'"; + EXPECT_EQ( + RMW_RET_OK, rmw_subscription_content_filter_options_init( + filter_expression, 0, nullptr, &allocator, &source2)); + EXPECT_STREQ(source2.filter_expression, filter_expression); + EXPECT_EQ( + RMW_RET_OK, + rmw_subscription_content_filter_options_copy(&source2, &allocator, &destination)); + EXPECT_STREQ(source2.filter_expression, destination.filter_expression); + EXPECT_EQ(source2.expression_parameters.size, 0u); + EXPECT_EQ(source2.expression_parameters.data, nullptr); + EXPECT_EQ( + RMW_RET_OK, rmw_subscription_content_filter_options_fini(&source2, &allocator)); + } + + EXPECT_EQ(RMW_RET_OK, rmw_subscription_content_filter_options_fini(&source, &allocator)); + EXPECT_EQ( + RMW_RET_OK, + rmw_subscription_content_filter_options_fini(&destination, &allocator)); +} + +TEST(rmw_subscription_content_filter_options, content_filter_options_fini) { + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + EXPECT_EQ( + RMW_RET_INVALID_ARGUMENT, + rmw_subscription_content_filter_options_fini(nullptr, &allocator)); + rcutils_reset_error(); + + rmw_subscription_content_filter_options_t options = + rmw_get_zero_initialized_content_filter_options(); + EXPECT_EQ( + RMW_RET_INVALID_ARGUMENT, + rmw_subscription_content_filter_options_fini(&options, nullptr)); + rcutils_reset_error(); + + EXPECT_EQ(RMW_RET_OK, rmw_subscription_content_filter_options_fini(&options, &allocator)); +} diff --git a/rmw/test/test_subscription_options.cpp b/rmw/test/test_subscription_options.cpp index 78990c12..42660467 100644 --- a/rmw/test/test_subscription_options.cpp +++ b/rmw/test/test_subscription_options.cpp @@ -24,4 +24,5 @@ TEST(rmw_subscription_options, get_default_subscription_options) EXPECT_EQ( options.require_unique_network_flow_endpoints, RMW_UNIQUE_NETWORK_FLOW_ENDPOINTS_NOT_REQUIRED); + EXPECT_EQ(options.content_filter_options, nullptr); }