From 1b9cf547a438c44565351163208b8983bbc08688 Mon Sep 17 00:00:00 2001 From: brawner Date: Wed, 30 Sep 2020 15:59:10 -0700 Subject: [PATCH] Add unit tests for qos and qos_event files (#1352) * Add unit tests for qos and qos_event files Signed-off-by: Stephen Brawner * PR Feedback Signed-off-by: Stephen Brawner * Address PR Feedback Signed-off-by: Stephen Brawner * Fix windows CI Signed-off-by: Stephen Brawner --- rclcpp/include/rclcpp/qos.hpp | 1 + rclcpp/test/CMakeLists.txt | 1 + rclcpp/test/rclcpp/test_qos.cpp | 121 ++++++++++++++++++++++++++ rclcpp/test/rclcpp/test_qos_event.cpp | 105 +++++++++++++++++++++- 4 files changed, 226 insertions(+), 2 deletions(-) diff --git a/rclcpp/include/rclcpp/qos.hpp b/rclcpp/include/rclcpp/qos.hpp index ac016ef..c59514a 100644 --- a/rclcpp/include/rclcpp/qos.hpp +++ b/rclcpp/include/rclcpp/qos.hpp @@ -26,6 +26,7 @@ namespace rclcpp { +RCLCPP_PUBLIC std::string qos_policy_name_from_kind(rmw_qos_policy_kind_t policy_kind); /// QoS initialization values, cannot be created directly, use KeepAll or KeepLast instead. diff --git a/rclcpp/test/CMakeLists.txt b/rclcpp/test/CMakeLists.txt index 1d41492..a2d8ec2 100644 --- a/rclcpp/test/CMakeLists.txt +++ b/rclcpp/test/CMakeLists.txt @@ -361,6 +361,7 @@ if(TARGET test_qos_event) ) target_link_libraries(test_qos_event ${PROJECT_NAME} + mimick ) endif() ament_add_gtest(test_rate rclcpp/test_rate.cpp) diff --git a/rclcpp/test/rclcpp/test_qos.cpp b/rclcpp/test/rclcpp/test_qos.cpp index 7188119..944cc1a 100644 --- a/rclcpp/test/rclcpp/test_qos.cpp +++ b/rclcpp/test/rclcpp/test_qos.cpp @@ -16,6 +16,8 @@ #include "rclcpp/qos.hpp" +#include "rmw/types.h" + TEST(TestQoS, equality_history) { rclcpp::QoS a(10); rclcpp::QoS b(10); @@ -75,3 +77,122 @@ TEST(TestQoS, equality_namespace) { a.avoid_ros_namespace_conventions(true); EXPECT_NE(a, b); } + +TEST(TestQoS, setters) { + rclcpp::QoS qos(10); + + qos.keep_all(); + EXPECT_EQ(RMW_QOS_POLICY_HISTORY_KEEP_ALL, qos.get_rmw_qos_profile().history); + + qos.keep_last(20); + EXPECT_EQ(RMW_QOS_POLICY_HISTORY_KEEP_LAST, qos.get_rmw_qos_profile().history); + EXPECT_EQ(20u, qos.get_rmw_qos_profile().depth); + + qos.reliable(); + EXPECT_EQ(RMW_QOS_POLICY_RELIABILITY_RELIABLE, qos.get_rmw_qos_profile().reliability); + + qos.durability_volatile(); + EXPECT_EQ(RMW_QOS_POLICY_DURABILITY_VOLATILE, qos.get_rmw_qos_profile().durability); + + qos.transient_local(); + EXPECT_EQ(RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL, qos.get_rmw_qos_profile().durability); + + qos.history(RMW_QOS_POLICY_HISTORY_KEEP_ALL); + EXPECT_EQ(RMW_QOS_POLICY_HISTORY_KEEP_ALL, qos.get_rmw_qos_profile().history); + + constexpr size_t duration_ns = 12345; + constexpr std::chrono::nanoseconds duration(duration_ns); + qos.deadline(duration); + EXPECT_EQ(0u, qos.get_rmw_qos_profile().deadline.sec); + EXPECT_EQ(duration_ns, qos.get_rmw_qos_profile().deadline.nsec); + + const rmw_time_t rmw_time {0, 54321}; + qos.deadline(rmw_time); + EXPECT_EQ(rmw_time.sec, qos.get_rmw_qos_profile().deadline.sec); + EXPECT_EQ(rmw_time.nsec, qos.get_rmw_qos_profile().deadline.nsec); + + qos.lifespan(duration); + EXPECT_EQ(0u, qos.get_rmw_qos_profile().lifespan.sec); + EXPECT_EQ(duration_ns, qos.get_rmw_qos_profile().lifespan.nsec); + + qos.lifespan(rmw_time); + EXPECT_EQ(rmw_time.sec, qos.get_rmw_qos_profile().lifespan.sec); + EXPECT_EQ(rmw_time.nsec, qos.get_rmw_qos_profile().lifespan.nsec); + + qos.liveliness(RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC); + EXPECT_EQ(RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC, qos.get_rmw_qos_profile().liveliness); + + qos.liveliness_lease_duration(duration); + EXPECT_EQ(0u, qos.get_rmw_qos_profile().liveliness_lease_duration.sec); + EXPECT_EQ(duration_ns, qos.get_rmw_qos_profile().liveliness_lease_duration.nsec); + + qos.liveliness_lease_duration(rmw_time); + EXPECT_EQ(rmw_time.sec, qos.get_rmw_qos_profile().liveliness_lease_duration.sec); + EXPECT_EQ(rmw_time.nsec, qos.get_rmw_qos_profile().liveliness_lease_duration.nsec); + + qos.avoid_ros_namespace_conventions(true); + EXPECT_TRUE(qos.get_rmw_qos_profile().avoid_ros_namespace_conventions); + qos.avoid_ros_namespace_conventions(false); + EXPECT_FALSE(qos.get_rmw_qos_profile().avoid_ros_namespace_conventions); +} + +bool operator==(const rmw_qos_profile_t & lhs, const rmw_qos_profile_t & rhs) +{ + if (lhs.history != rhs.history) { + return false; + } + switch (lhs.history) { + case RMW_QOS_POLICY_HISTORY_KEEP_ALL: + return true; + case RMW_QOS_POLICY_HISTORY_KEEP_LAST: + case RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT: + case RMW_QOS_POLICY_HISTORY_UNKNOWN: + return lhs.depth == rhs.depth; + } + throw std::runtime_error("This line shouldn't be reached"); +} + +TEST(TestQoS, DerivedTypes) { + rclcpp::SensorDataQoS sensor_data_qos; + EXPECT_EQ(rmw_qos_profile_sensor_data, sensor_data_qos.get_rmw_qos_profile()); + + rclcpp::ParametersQoS parameter_qos; + EXPECT_EQ(rmw_qos_profile_parameters, parameter_qos.get_rmw_qos_profile()); + + rclcpp::ServicesQoS services_qos; + EXPECT_EQ(rmw_qos_profile_services_default, services_qos.get_rmw_qos_profile()); + + rclcpp::ParameterEventsQoS parameter_events_qos; + EXPECT_EQ(rmw_qos_profile_parameter_events, parameter_events_qos.get_rmw_qos_profile()); + + rclcpp::SystemDefaultsQoS system_default_qos; + const rclcpp::KeepLast expected_initialization(RMW_QOS_POLICY_DEPTH_SYSTEM_DEFAULT); + const rclcpp::QoS expected_default(expected_initialization); + EXPECT_EQ(expected_default.get_rmw_qos_profile(), system_default_qos.get_rmw_qos_profile()); +} + +TEST(TestQoS, policy_name_from_kind) { + EXPECT_EQ( + "DURABILITY_QOS_POLICY", + rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_DURABILITY)); + + EXPECT_EQ( + "DEADLINE_QOS_POLICY", + rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_DEADLINE)); + + EXPECT_EQ( + "LIVELINESS_QOS_POLICY", + rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_LIVELINESS)); + + EXPECT_EQ( + "RELIABILITY_QOS_POLICY", + rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_RELIABILITY)); + + EXPECT_EQ( + "HISTORY_QOS_POLICY", + rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_HISTORY)); + + EXPECT_EQ( + "LIFESPAN_QOS_POLICY", + rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_LIFESPAN)); +} diff --git a/rclcpp/test/rclcpp/test_qos_event.cpp b/rclcpp/test/rclcpp/test_qos_event.cpp index c0f6076..baf5bd7 100644 --- a/rclcpp/test/rclcpp/test_qos_event.cpp +++ b/rclcpp/test/rclcpp/test_qos_event.cpp @@ -25,6 +25,8 @@ #include "rmw/rmw.h" #include "test_msgs/msg/empty.hpp" +#include "../mocking_utils/patch.hpp" + class TestQosEvent : public ::testing::Test { protected: @@ -207,9 +209,14 @@ TEST_F(TestQosEvent, test_default_incompatible_qos_callbacks) rclcpp::executors::SingleThreadedExecutor ex; ex.add_node(node->get_node_base_interface()); - ex.spin_until_future_complete(log_msgs_future, std::chrono::seconds(10)); + // This future won't complete on fastrtps, so just timeout immediately + const auto timeout = (is_fastrtps) ? std::chrono::milliseconds(5) : std::chrono::seconds(10); + ex.spin_until_future_complete(log_msgs_future, timeout); - if (!is_fastrtps) { + if (is_fastrtps) { + EXPECT_EQ("", pub_log_msg); + EXPECT_EQ("", sub_log_msg); + } else { EXPECT_EQ( "New subscription discovered on this topic, requesting incompatible QoS. " "No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY", @@ -222,3 +229,97 @@ TEST_F(TestQosEvent, test_default_incompatible_qos_callbacks) rcutils_logging_set_output_handler(original_output_handler); } + +TEST_F(TestQosEvent, construct_destruct_rcl_error) { + auto publisher = node->create_publisher(topic_name, 10); + auto rcl_handle = publisher->get_publisher_handle(); + ASSERT_NE(nullptr, rcl_handle); + + // This callback requires some type of parameter, but it could be anything + auto callback = [](int) {}; + const rcl_publisher_event_type_t event_type = RCL_PUBLISHER_OFFERED_DEADLINE_MISSED; + + { + // Logs error and returns + auto mock = mocking_utils::patch_and_return( + "lib:rclcpp", rcl_publisher_event_init, RCL_RET_ERROR); + + auto throwing_statement = [callback, rcl_handle, event_type]() { + // reset() is not needed for the exception, but it handles unused return value warning + std::make_shared< + rclcpp::QOSEventHandler>>( + callback, rcl_publisher_event_init, rcl_handle, event_type).reset(); + }; + // This is done through a lambda because the compiler is having trouble parsing the templated + // function inside a macro. + EXPECT_THROW(throwing_statement(), rclcpp::exceptions::RCLError); + } + + { + // Logs error and returns + auto mock = mocking_utils::inject_on_return( + "lib:rclcpp", rcl_event_fini, RCL_RET_ERROR); + + auto throwing_statement = [callback, rcl_handle, event_type]() { + // reset() is needed for this exception + std::make_shared< + rclcpp::QOSEventHandler>>( + callback, rcl_publisher_event_init, rcl_handle, event_type).reset(); + }; + + // This is done through a lambda because the compiler is having trouble parsing the templated + // function inside a macro. + EXPECT_NO_THROW(throwing_statement()); + } +} + +TEST_F(TestQosEvent, execute) { + auto publisher = node->create_publisher(topic_name, 10); + auto rcl_handle = publisher->get_publisher_handle(); + + bool handler_callback_executed = false; + // This callback requires some type of parameter, but it could be anything + auto callback = [&handler_callback_executed](int) {handler_callback_executed = true;}; + rcl_publisher_event_type_t event_type = RCL_PUBLISHER_OFFERED_DEADLINE_MISSED; + + rclcpp::QOSEventHandler handler( + callback, rcl_publisher_event_init, rcl_handle, event_type); + + EXPECT_NO_THROW(handler.execute()); + EXPECT_TRUE(handler_callback_executed); + + { + handler_callback_executed = false; + // Logs error and returns early + auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_take_event, RCL_RET_ERROR); + EXPECT_NO_THROW(handler.execute()); + EXPECT_FALSE(handler_callback_executed); + } +} + +TEST_F(TestQosEvent, add_to_wait_set) { + auto publisher = node->create_publisher(topic_name, 10); + auto rcl_handle = publisher->get_publisher_handle(); + + // This callback requires some type of parameter, but it could be anything + auto callback = [](int) {}; + + rcl_publisher_event_type_t event_type = RCL_PUBLISHER_OFFERED_DEADLINE_MISSED; + rclcpp::QOSEventHandler handler( + callback, rcl_publisher_event_init, rcl_handle, event_type); + + EXPECT_EQ(1u, handler.get_number_of_ready_events()); + + { + rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set(); + auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_add_event, RCL_RET_OK); + EXPECT_TRUE(handler.add_to_wait_set(&wait_set)); + } + + { + rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set(); + auto mock = mocking_utils::patch_and_return( + "lib:rclcpp", rcl_wait_set_add_event, RCL_RET_ERROR); + EXPECT_THROW(handler.add_to_wait_set(&wait_set), rclcpp::exceptions::RCLError); + } +}