diff --git a/rcl/test/rcl/allocator_testing_utils.h b/rcl/test/rcl/allocator_testing_utils.h new file mode 100644 index 000000000..cdc707d68 --- /dev/null +++ b/rcl/test/rcl/allocator_testing_utils.h @@ -0,0 +1,94 @@ +// Copyright 2020 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 RCL__ALLOCATOR_TESTING_UTILS_H_ +#define RCL__ALLOCATOR_TESTING_UTILS_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +#include "rcutils/allocator.h" + +typedef struct __failing_allocator_state +{ + bool is_failing; +} __failing_allocator_state; + +void * +failing_malloc(size_t size, void * state) +{ + if (((__failing_allocator_state *)state)->is_failing) { + return nullptr; + } + return rcutils_get_default_allocator().allocate(size, rcutils_get_default_allocator().state); +} + +void * +failing_realloc(void * pointer, size_t size, void * state) +{ + if (((__failing_allocator_state *)state)->is_failing) { + return nullptr; + } + return rcutils_get_default_allocator().reallocate( + pointer, size, rcutils_get_default_allocator().state); +} + +void +failing_free(void * pointer, void * state) +{ + if (((__failing_allocator_state *)state)->is_failing) { + return; + } + rcutils_get_default_allocator().deallocate(pointer, rcutils_get_default_allocator().state); +} + +void * +failing_calloc(size_t number_of_elements, size_t size_of_element, void * state) +{ + if (((__failing_allocator_state *)state)->is_failing) { + return nullptr; + } + return rcutils_get_default_allocator().zero_allocate( + number_of_elements, size_of_element, rcutils_get_default_allocator().state); +} + +static inline rcutils_allocator_t +get_failing_allocator(void) +{ + static __failing_allocator_state state; + state.is_failing = true; + auto failing_allocator = rcutils_get_default_allocator(); + failing_allocator.allocate = failing_malloc; + failing_allocator.deallocate = failing_free; + failing_allocator.reallocate = failing_realloc; + failing_allocator.zero_allocate = failing_calloc; + failing_allocator.state = &state; + return failing_allocator; +} + +static inline void +set_failing_allocator_is_failing(rcutils_allocator_t & failing_allocator, bool state) +{ + ((__failing_allocator_state *)failing_allocator.state)->is_failing = state; +} + +#ifdef __cplusplus +} +#endif + +#endif // RCL__ALLOCATOR_TESTING_UTILS_H_ diff --git a/rcl/test/rcl/test_time.cpp b/rcl/test/rcl/test_time.cpp index 2b552ea68..f267f0871 100644 --- a/rcl/test/rcl/test_time.cpp +++ b/rcl/test/rcl/test_time.cpp @@ -268,6 +268,18 @@ TEST(CLASSNAME(rcl_time, RMW_IMPLEMENTATION), specific_clock_instantiation) { EXPECT_EQ(uninitialized_clock.type, RCL_CLOCK_UNINITIALIZED) << "Expected time source of type RCL_CLOCK_UNINITIALIZED"; EXPECT_TRUE(rcutils_allocator_is_valid(&(uninitialized_clock.allocator))); + ret = rcl_clock_fini(&uninitialized_clock); + EXPECT_EQ(ret, RCL_RET_INVALID_ARGUMENT) << rcl_get_error_string().str; + rcl_reset_error(); + EXPECT_EQ( + rcl_ros_clock_fini(&uninitialized_clock), RCL_RET_ERROR) << rcl_get_error_string().str; + rcl_reset_error(); + EXPECT_EQ( + rcl_steady_clock_fini(&uninitialized_clock), RCL_RET_ERROR) << rcl_get_error_string().str; + rcl_reset_error(); + EXPECT_EQ( + rcl_system_clock_fini(&uninitialized_clock), RCL_RET_ERROR) << rcl_get_error_string().str; + rcl_reset_error(); } { rcl_clock_t ros_clock; @@ -296,6 +308,12 @@ TEST(CLASSNAME(rcl_time, RMW_IMPLEMENTATION), specific_clock_instantiation) { ret = rcl_clock_fini(&steady_clock); EXPECT_EQ(ret, RCL_RET_OK) << rcl_get_error_string().str; } + { + rcl_clock_t fail_clock; + rcl_clock_type_t undefined_type = (rcl_clock_type_t) 130; + rcl_ret_t ret = rcl_clock_init(undefined_type, &fail_clock, &allocator); + EXPECT_EQ(ret, RCL_RET_INVALID_ARGUMENT) << rcl_get_error_string().str; + } } TEST(CLASSNAME(rcl_time, RMW_IMPLEMENTATION), rcl_time_difference) { @@ -333,6 +351,9 @@ TEST(CLASSNAME(rcl_time, RMW_IMPLEMENTATION), rcl_time_difference) { ret = rcl_difference_times(&b, &a, &d); EXPECT_EQ(ret, RCL_RET_OK) << rcl_get_error_string().str; EXPECT_EQ(d.nanoseconds, -1000); + + b.clock_type = RCL_SYSTEM_TIME; + EXPECT_EQ(rcl_difference_times(&a, &b, &d), RCL_RET_ERROR) << rcl_get_error_string().str; } TEST(CLASSNAME(rcl_time, RMW_IMPLEMENTATION), rcl_time_difference_signed) { @@ -484,6 +505,33 @@ TEST(CLASSNAME(rcl_time, RMW_IMPLEMENTATION), rcl_time_clock_change_callbacks) { reset_callback_triggers(); } +TEST(CLASSNAME(rcl_time, RMW_IMPLEMENTATION), rcl_time_fail_set_jump_callbacks) { + rcl_allocator_t allocator = rcl_get_default_allocator(); + rcl_clock_t fail_clock; + rcl_time_jump_t time_jump; + rcl_jump_threshold_t threshold; + rcl_ret_t ret = rcl_clock_init(RCL_CLOCK_UNINITIALIZED, &fail_clock, &allocator); + EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + + threshold.on_clock_change = false; + threshold.min_forward.nanoseconds = -1; + threshold.min_backward.nanoseconds = 0; + + EXPECT_EQ( + RCL_RET_INVALID_ARGUMENT, + rcl_clock_add_jump_callback(&fail_clock, threshold, clock_callback, &time_jump)) << + rcl_get_error_string().str; + rcl_reset_error(); + + threshold.min_forward.nanoseconds = 0; + threshold.min_backward.nanoseconds = 1; + EXPECT_EQ( + RCL_RET_INVALID_ARGUMENT, + rcl_clock_add_jump_callback(&fail_clock, threshold, clock_callback, &time_jump)) << + rcl_get_error_string().str; + rcl_reset_error(); +} + TEST(CLASSNAME(rcl_time, RMW_IMPLEMENTATION), rcl_time_forward_jump_callbacks) { rcl_allocator_t allocator = rcl_get_default_allocator(); auto * ros_clock = @@ -730,3 +778,29 @@ TEST(CLASSNAME(rcl_time, RMW_IMPLEMENTATION), add_remove_add_jump_callback) { rcl_get_error_string().str; EXPECT_EQ(1u, clock->num_jump_callbacks); } + +TEST(CLASSNAME(rcl_time, RMW_IMPLEMENTATION), failed_get_now) { + rcl_allocator_t allocator = rcl_get_default_allocator(); + rcl_clock_t uninitialized_clock; + rcl_time_point_value_t query_now; + rcl_ret_t ret = rcl_clock_init(RCL_CLOCK_UNINITIALIZED, &uninitialized_clock, &allocator); + EXPECT_EQ(ret, RCL_RET_OK) << rcl_get_error_string().str; + EXPECT_EQ(uninitialized_clock.type, RCL_CLOCK_UNINITIALIZED); + uninitialized_clock.get_now = NULL; + EXPECT_EQ(RCL_RET_ERROR, rcl_clock_get_now(&uninitialized_clock, &query_now)); +} + +TEST(CLASSNAME(rcl_time, RMW_IMPLEMENTATION), fail_override) { + rcl_clock_t ros_clock; + rcl_allocator_t allocator = rcl_get_default_allocator(); + bool result; + rcl_time_point_value_t set_point = 1000000000ull; + ASSERT_EQ( + RCL_RET_OK, rcl_clock_init( + RCL_CLOCK_UNINITIALIZED, &ros_clock, &allocator)) << rcl_get_error_string().str; + + EXPECT_EQ(RCL_RET_ERROR, rcl_enable_ros_time_override(&ros_clock)); + EXPECT_EQ(RCL_RET_ERROR, rcl_disable_ros_time_override(&ros_clock)); + EXPECT_EQ(RCL_RET_ERROR, rcl_is_enabled_ros_time_override(&ros_clock, &result)); + EXPECT_EQ(RCL_RET_ERROR, rcl_set_ros_time_override(&ros_clock, set_point)); +} diff --git a/rcl/test/rcl/test_timer.cpp b/rcl/test/rcl/test_timer.cpp index 746f575b6..07edd5c97 100644 --- a/rcl/test/rcl/test_timer.cpp +++ b/rcl/test/rcl/test_timer.cpp @@ -23,6 +23,8 @@ #include "osrf_testing_tools_cpp/scope_exit.hpp" #include "rcl/error_handling.h" +#include "./allocator_testing_utils.h" + class TestTimerFixture : public ::testing::Test { public: @@ -72,6 +74,12 @@ static void callback_function(rcl_timer_t * timer, int64_t last_call) (void) last_call; times_called++; } +static void callback_function_changed(rcl_timer_t * timer, int64_t last_call) +{ + (void) timer; + (void) last_call; + times_called--; +} class TestPreInitTimer : public TestTimerFixture { @@ -80,6 +88,7 @@ class TestPreInitTimer : public TestTimerFixture rcl_allocator_t allocator; rcl_timer_t timer; rcl_timer_callback_t timer_callback_test = &callback_function; + rcl_timer_callback_t timer_callback_changed = &callback_function_changed; void SetUp() override { @@ -559,3 +568,115 @@ TEST_F(TestPreInitTimer, test_timer_get_allocator) { EXPECT_EQ(NULL, rcl_timer_get_allocator(nullptr)); } + +TEST_F(TestPreInitTimer, test_timer_clock) { + rcl_clock_t * clock_impl = nullptr; + EXPECT_EQ(RCL_RET_OK, rcl_timer_clock(&timer, &clock_impl)) << rcl_get_error_string().str; + EXPECT_EQ(clock_impl, &clock); +} + +TEST_F(TestPreInitTimer, test_timer_call) { + int64_t next_call_start = 0; + int64_t next_call_end = 0; + int64_t old_period = 0; + times_called = 0; + + EXPECT_EQ(RCL_RET_OK, rcl_timer_get_time_until_next_call(&timer, &next_call_start)); + ASSERT_EQ(RCL_RET_OK, rcl_timer_call(&timer)) << rcl_get_error_string().str; + EXPECT_EQ(times_called, 1); + + ASSERT_EQ(RCL_RET_OK, rcl_timer_call(&timer)) << rcl_get_error_string().str; + ASSERT_EQ(RCL_RET_OK, rcl_timer_call(&timer)) << rcl_get_error_string().str; + EXPECT_EQ(times_called, 3); + EXPECT_EQ(RCL_RET_OK, rcl_timer_get_time_until_next_call(&timer, &next_call_end)); + EXPECT_GT(next_call_end, next_call_start); + + next_call_start = next_call_end; + ASSERT_EQ(RCL_RET_OK, rcl_timer_exchange_period(&timer, 0, &old_period)); + EXPECT_EQ(RCL_S_TO_NS(1), old_period); + ASSERT_EQ(RCL_RET_OK, rcl_timer_call(&timer)) << rcl_get_error_string().str; + EXPECT_EQ(times_called, 4); + EXPECT_EQ(RCL_RET_OK, rcl_timer_get_time_until_next_call(&timer, &next_call_end)); + EXPECT_GT(next_call_start, next_call_end); + + EXPECT_EQ(RCL_RET_OK, rcl_timer_cancel(&timer)) << rcl_get_error_string().str; + EXPECT_EQ(RCL_RET_TIMER_CANCELED, rcl_timer_call(&timer)); + EXPECT_EQ(times_called, 4); +} + +TEST_F(TestPreInitTimer, test_get_callback) { + ASSERT_EQ(timer_callback_test, rcl_timer_get_callback(&timer)) << rcl_get_error_string().str; +} + +TEST_F(TestPreInitTimer, test_timer_reset) { + int64_t next_call_start = 0; + int64_t next_call_end = 0; + times_called = 0; + + ASSERT_EQ(RCL_RET_OK, rcl_timer_call(&timer)) << rcl_get_error_string().str; + ASSERT_EQ(RCL_RET_OK, rcl_timer_call(&timer)) << rcl_get_error_string().str; + EXPECT_EQ(times_called, 2); + EXPECT_EQ(RCL_RET_OK, rcl_timer_get_time_until_next_call(&timer, &next_call_start)); + + ASSERT_EQ(RCL_RET_OK, rcl_timer_reset(&timer)); + EXPECT_EQ(RCL_RET_OK, rcl_timer_get_time_until_next_call(&timer, &next_call_end)); + EXPECT_GT(next_call_start, next_call_end); + + ASSERT_EQ(RCL_RET_OK, rcl_timer_cancel(&timer)) << rcl_get_error_string().str; + EXPECT_EQ(RCL_RET_TIMER_CANCELED, rcl_timer_call(&timer)); + EXPECT_EQ(times_called, 2); + ASSERT_EQ(RCL_RET_OK, rcl_timer_reset(&timer)); + EXPECT_EQ(RCL_RET_OK, rcl_timer_call(&timer)) << rcl_get_error_string().str; + EXPECT_EQ(times_called, 3); +} + +TEST_F(TestPreInitTimer, test_timer_exchange_callback) { + times_called = 0; + ASSERT_EQ(RCL_RET_OK, rcl_timer_call(&timer)) << rcl_get_error_string().str; + EXPECT_EQ(times_called, 1); + ASSERT_EQ( + timer_callback_test, rcl_timer_exchange_callback( + &timer, timer_callback_changed)) << rcl_get_error_string().str; + + ASSERT_EQ(RCL_RET_OK, rcl_timer_call(&timer)) << rcl_get_error_string().str; + EXPECT_EQ(times_called, 0); +} + +TEST_F(TestPreInitTimer, test_invalid_get_guard) { + ASSERT_EQ(NULL, rcl_timer_get_guard_condition(nullptr)); +} + +TEST_F(TestPreInitTimer, test_invalid_init_fini) { + rcl_allocator_t bad_allocator = get_failing_allocator(); + rcl_timer_t timer_fail = rcl_get_zero_initialized_timer(); + + EXPECT_EQ( + RCL_RET_ALREADY_INIT, rcl_timer_init( + &timer, &clock, this->context_ptr, 500, nullptr, + rcl_get_default_allocator())) << rcl_get_error_string().str; + + ASSERT_EQ( + RCL_RET_BAD_ALLOC, rcl_timer_init( + &timer_fail, &clock, this->context_ptr, RCL_S_TO_NS(1), timer_callback_test, + bad_allocator)) << rcl_get_error_string().str; + + EXPECT_EQ(RCL_RET_OK, rcl_timer_fini(nullptr)); +} + +TEST_F(TestPreInitTimer, test_timer_get_period) { + int64_t period = 0; + ASSERT_EQ(RCL_RET_OK, rcl_timer_get_period(&timer, &period)); + EXPECT_EQ(RCL_S_TO_NS(1), period); + + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, rcl_timer_get_period(nullptr, &period)); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, rcl_timer_get_period(&timer, nullptr)); +} + +TEST_F(TestPreInitTimer, test_time_since_last_call) { + rcl_time_point_value_t time_sice_next_call_start = 0u; + rcl_time_point_value_t time_sice_next_call_end = 0u; + + ASSERT_EQ(RCL_RET_OK, rcl_timer_get_time_since_last_call(&timer, &time_sice_next_call_start)); + ASSERT_EQ(RCL_RET_OK, rcl_timer_get_time_since_last_call(&timer, &time_sice_next_call_end)); + EXPECT_GT(time_sice_next_call_end, time_sice_next_call_start); +}