Skip to content

Commit

Permalink
Add tests for time.c and timer.c for rcl package (#599)
Browse files Browse the repository at this point in the history
* Add tests for time.c module
* Add test for function rcl_timer_clock
* Fix linter style issues
* Add test for rcl_timer_call
* Add callback test and improve call test
* Add fixture class to reuse timer init/fini code
* Change error code returned to match function being tested
* Add tests for timer_reset function
* Add tests for exchange_callback function
* Add null parameter test for get guard condition
* Add allocator testing tools auxiliar file
* Add tests for failing ini/fini conditions
* Add test for get_period function
* Add test to get_time_since_last_call
* Remove comment related to variable names
* Fix return value on test case for fini clock
* Address peer review comments
* Fix CLang warning, replacing NULL with false

Signed-off-by: Jorge Perez <jjperez@ekumenlabs.com>
  • Loading branch information
Blast545 authored May 13, 2020
1 parent 7f57396 commit c64ca63
Show file tree
Hide file tree
Showing 3 changed files with 289 additions and 0 deletions.
94 changes: 94 additions & 0 deletions rcl/test/rcl/allocator_testing_utils.h
Original file line number Diff line number Diff line change
@@ -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 <stddef.h>

#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_
74 changes: 74 additions & 0 deletions rcl/test/rcl/test_time.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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));
}
121 changes: 121 additions & 0 deletions rcl/test/rcl/test_timer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
{
Expand All @@ -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
{
Expand Down Expand Up @@ -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);
}

0 comments on commit c64ca63

Please sign in to comment.