implement tests for rcl
This commit is contained in:
parent
a022299568
commit
0ae4ca735d
5 changed files with 241 additions and 8 deletions
|
@ -46,11 +46,17 @@ extern "C"
|
|||
* be ignored.
|
||||
* If argc is 0 and argv is NULL no parameters will be parsed.
|
||||
*
|
||||
* This function allocates heap memory.
|
||||
* This function is not thread-safe.
|
||||
* This function is lock-free so long as the C11's stdatomic.h function
|
||||
* atomic_is_lock_free() returns true for atomic_uint_least64_t.
|
||||
*
|
||||
* \param[in] argc number of strings in argv
|
||||
* \param[in] argv command line arguments; rcl specific arguments are removed
|
||||
* \param[in] allocator allocator to be used during rcl_init and rcl_shutdown
|
||||
* \return RCL_RET_OK if initialization is successful, or
|
||||
* RCL_RET_ALREADY_INIT if rcl_init has already been called, or
|
||||
* RCL_RET_BAD_ALLOC if allocating memory failed, or
|
||||
* RCL_RET_ERROR if an unspecified error occurs.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
|
@ -72,21 +78,39 @@ rcl_init(int argc, char ** argv, rcl_allocator_t allocator);
|
|||
* - No new work (executing callbacks) will be done in executors.
|
||||
* - Currently running work in executors will be finished.
|
||||
*
|
||||
* \return RCL_RET_OK if shutdown is successful, otherwise RCL_RET_ERROR or
|
||||
* RCL_RET_NOT_INIT if rcl_init has not yet been called
|
||||
* This function does not allocate heap memory.
|
||||
* This function is thread-safe, except with rcl_init().
|
||||
* This function is lock-free so long as the C11's stdatomic.h function
|
||||
* atomic_is_lock_free() returns true for atomic_uint_least64_t.
|
||||
*
|
||||
* \return RCL_RET_OK if the shutdown was completed successfully, or
|
||||
* RCL_RET_NOT_INIT if rcl is not currently initialized, or
|
||||
* RCL_RET_ERROR if an unspecified error occur.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
rcl_ret_t
|
||||
rcl_shutdown();
|
||||
|
||||
/// Returns an uint64_t number that is unique for the latest rcl_init call.
|
||||
/* If called before rcl_init or after rcl_shutdown then 0 will be returned. */
|
||||
/* If called before rcl_init or after rcl_shutdown then 0 will be returned.
|
||||
*
|
||||
* This function does not allocate memory.
|
||||
* This function is thread-safe.
|
||||
* This function is lock-free so long as the C11's stdatomic.h function
|
||||
* atomic_is_lock_free() returns true for atomic_uint_least64_t.
|
||||
*
|
||||
* \return a unique id specific to this rcl instance, or 0 if not initialized.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
uint64_t
|
||||
rcl_get_instance_id();
|
||||
|
||||
/// Return true until rcl_shutdown is called, then false.
|
||||
/* This function is thread safe. */
|
||||
/// Return true if rcl is currently initialized, otherwise false.
|
||||
/* This function does not allocate memory.
|
||||
* This function is thread-safe.
|
||||
* This function is lock-free so long as the C11's stdatomic.h function
|
||||
* atomic_is_lock_free() returns true for atomic_uint_least64_t.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
bool
|
||||
rcl_ok();
|
||||
|
|
|
@ -21,6 +21,7 @@ extern "C"
|
|||
|
||||
#include <string.h>
|
||||
|
||||
#include "./common.h"
|
||||
#include "./stdatomic_helper.h"
|
||||
#include "rcl/error_handling.h"
|
||||
|
||||
|
@ -38,11 +39,15 @@ __clean_up_init()
|
|||
size_t i;
|
||||
for (i = 0; i < __rcl_argc; ++i) {
|
||||
if (__rcl_argv[i]) {
|
||||
// Use the old allocator.
|
||||
__rcl_allocator.deallocate(__rcl_argv[i], __rcl_allocator.state);
|
||||
}
|
||||
}
|
||||
// Use the old allocator.
|
||||
__rcl_allocator.deallocate(__rcl_argv, __rcl_allocator.state);
|
||||
}
|
||||
__rcl_argc = 0;
|
||||
__rcl_argv = NULL;
|
||||
rcl_atomic_store(&__rcl_instance_id, 0);
|
||||
rcl_atomic_store(&__rcl_is_initialized, false);
|
||||
}
|
||||
|
@ -50,16 +55,28 @@ __clean_up_init()
|
|||
rcl_ret_t
|
||||
rcl_init(int argc, char ** argv, rcl_allocator_t allocator)
|
||||
{
|
||||
rcl_ret_t fail_ret = RCL_RET_ERROR;
|
||||
if (argc > 0) {
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(argv, RCL_RET_INVALID_ARGUMENT);
|
||||
}
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
allocator.allocate,
|
||||
"invalid allocator, allocate not set", return RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
allocator.deallocate,
|
||||
"invalid allocator, deallocate not set", return RCL_RET_INVALID_ARGUMENT);
|
||||
if (rcl_atomic_exchange_bool(&__rcl_is_initialized, true)) {
|
||||
RCL_SET_ERROR_MSG("rcl_init called while already initialized");
|
||||
return RCL_RET_ALREADY_INIT;
|
||||
}
|
||||
// TODO(wjwwood): Remove rcl specific command line arguments.
|
||||
// For now just copy the argc and argv.
|
||||
__rcl_allocator = allocator; // Set the new allocator.
|
||||
__rcl_argc = argc;
|
||||
__rcl_argv = (char **)__rcl_allocator.allocate(sizeof(char *) * argc, __rcl_allocator.state);
|
||||
if (!__rcl_argv) {
|
||||
RCL_SET_ERROR_MSG("allocation failed");
|
||||
fail_ret = RCL_RET_BAD_ALLOC;
|
||||
goto fail;
|
||||
}
|
||||
memset(__rcl_argv, 0, sizeof(char **) * argc);
|
||||
|
@ -78,7 +95,7 @@ rcl_init(int argc, char ** argv, rcl_allocator_t allocator)
|
|||
return RCL_RET_OK;
|
||||
fail:
|
||||
__clean_up_init();
|
||||
return RCL_RET_ERROR;
|
||||
return fail_ret;
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
|
|
|
@ -79,4 +79,16 @@ if(AMENT_ENABLE_TESTING)
|
|||
endif()
|
||||
target_link_libraries(test_common ${PROJECT_NAME} ${extra_test_libraries})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_rcl rcl/test_rcl.cpp ENV ${extra_memory_tools_env})
|
||||
if(TARGET test_rcl)
|
||||
target_include_directories(test_rcl PUBLIC
|
||||
${rcl_interfaces_INCLUDE_DIRS}
|
||||
${rmw_INCLUDE_DIRS}
|
||||
)
|
||||
if(NOT WIN32)
|
||||
set_target_properties(test_rcl PROPERTIES COMPILE_FLAGS "-std=c++11")
|
||||
endif()
|
||||
target_link_libraries(test_rcl ${PROJECT_NAME} ${extra_test_libraries})
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
@ -63,7 +63,7 @@ custom_malloc(size_t size)
|
|||
}
|
||||
void * memory = malloc(size);
|
||||
MALLOC_PRINTF(
|
||||
"malloc expected(%s): %p %zu\n", malloc_expected ? "true " : "false", memory, size);
|
||||
"malloc expected(%s): %p %llu\n", malloc_expected ? "true " : "false", memory, size);
|
||||
return memory;
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ custom_realloc(void * memory_in, size_t size)
|
|||
}
|
||||
void * memory = realloc(memory_in, size);
|
||||
MALLOC_PRINTF(
|
||||
"realloc expected(%s): %p %p %zu\n",
|
||||
"realloc expected(%s): %p %p %llu\n",
|
||||
malloc_expected ? "true " : "false", memory_in, memory, size);
|
||||
return memory;
|
||||
}
|
||||
|
|
180
rcl/test/rcl/test_rcl.cpp
Normal file
180
rcl/test/rcl/test_rcl.cpp
Normal file
|
@ -0,0 +1,180 @@
|
|||
// Copyright 2015 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 <gtest/gtest.h>
|
||||
|
||||
#include "rcl/rcl.h"
|
||||
|
||||
#include "../memory_tools.hpp"
|
||||
#include "rcl/error_handling.h"
|
||||
|
||||
class TestRCLFixture : public::testing::Test
|
||||
{
|
||||
public:
|
||||
void SetUp()
|
||||
{
|
||||
set_on_unepexcted_malloc_callback([]() {ASSERT_FALSE(true) << "UNEXPECTED MALLOC";});
|
||||
set_on_unepexcted_realloc_callback([]() {ASSERT_FALSE(true) << "UNEXPECTED REALLOC";});
|
||||
set_on_unepexcted_free_callback([]() {ASSERT_FALSE(true) << "UNEXPECTED FREE";});
|
||||
start_memory_checking();
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
assert_no_malloc_end();
|
||||
assert_no_realloc_end();
|
||||
assert_no_free_end();
|
||||
stop_memory_checking();
|
||||
set_on_unepexcted_malloc_callback(nullptr);
|
||||
set_on_unepexcted_realloc_callback(nullptr);
|
||||
set_on_unepexcted_free_callback(nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
void *
|
||||
failing_malloc(size_t size, void * state)
|
||||
{
|
||||
(void)(size);
|
||||
(void)(state);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *
|
||||
failing_realloc(void * pointer, size_t size, void * state)
|
||||
{
|
||||
(void)(pointer);
|
||||
(void)(size);
|
||||
(void)(state);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
failing_free(void * pointer, void * state)
|
||||
{
|
||||
(void)pointer;
|
||||
(void)state;
|
||||
return;
|
||||
}
|
||||
|
||||
struct FakeTestArgv
|
||||
{
|
||||
FakeTestArgv() : argc(2)
|
||||
{
|
||||
this->argv = (char **)malloc(2 * sizeof(char *));
|
||||
if (!this->argv) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
this->argv[0] = (char *)malloc(10 * sizeof(char));
|
||||
sprintf(this->argv[0], "foo");
|
||||
this->argv[1] = (char *)malloc(10 * sizeof(char));
|
||||
sprintf(this->argv[1], "bar");
|
||||
}
|
||||
|
||||
~FakeTestArgv()
|
||||
{
|
||||
if (this->argv) {
|
||||
if (this->argv > 0) {
|
||||
size_t unsigned_argc = this->argc;
|
||||
for (size_t i = 0; i < unsigned_argc; --i) {
|
||||
free(this->argv[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(this->argv);
|
||||
}
|
||||
|
||||
int argc;
|
||||
char ** argv;
|
||||
};
|
||||
|
||||
/* Tests the rcl_init() and rcl_shutdown() functions.
|
||||
*/
|
||||
TEST_F(TestRCLFixture, test_rcl_init_and_shutdown)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
// A shutdown before any init has been called should fail.
|
||||
ret = rcl_shutdown();
|
||||
EXPECT_EQ(RCL_RET_NOT_INIT, ret);
|
||||
rcl_reset_error();
|
||||
ASSERT_EQ(false, rcl_ok());
|
||||
// If argc is not 0, but argv is, it should be an invalid argument.
|
||||
ret = rcl_init(42, nullptr, rcl_get_default_allocator());
|
||||
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret);
|
||||
rcl_reset_error();
|
||||
ASSERT_EQ(false, rcl_ok());
|
||||
// If either the allocate or deallocate function pointers are not set, it should be invalid arg.
|
||||
rcl_allocator_t invalid_allocator = rcl_get_default_allocator();
|
||||
invalid_allocator.allocate = nullptr;
|
||||
ret = rcl_init(0, nullptr, invalid_allocator);
|
||||
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret);
|
||||
rcl_reset_error();
|
||||
ASSERT_EQ(false, rcl_ok());
|
||||
invalid_allocator.allocate = rcl_get_default_allocator().allocate;
|
||||
invalid_allocator.deallocate = nullptr;
|
||||
ret = rcl_init(0, nullptr, invalid_allocator);
|
||||
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret);
|
||||
rcl_reset_error();
|
||||
ASSERT_EQ(false, rcl_ok());
|
||||
// If the malloc call fails (with some valid arguments to copy), it should be a bad alloc.
|
||||
{
|
||||
FakeTestArgv test_args;
|
||||
rcl_allocator_t failing_allocator = rcl_get_default_allocator();
|
||||
failing_allocator.allocate = &failing_malloc;
|
||||
failing_allocator.deallocate = failing_free;
|
||||
failing_allocator.reallocate = failing_realloc;
|
||||
ret = rcl_init(test_args.argc, test_args.argv, failing_allocator);
|
||||
EXPECT_EQ(RCL_RET_BAD_ALLOC, ret);
|
||||
rcl_reset_error();
|
||||
ASSERT_EQ(false, rcl_ok());
|
||||
}
|
||||
// If argc is 0 and argv is nullptr and the allocator is valid, it should succeed.
|
||||
ret = rcl_init(0, nullptr, rcl_get_default_allocator());
|
||||
EXPECT_EQ(RCL_RET_OK, ret);
|
||||
ASSERT_EQ(true, rcl_ok());
|
||||
// Then shutdown should work.
|
||||
ret = rcl_shutdown();
|
||||
EXPECT_EQ(ret, RCL_RET_OK);
|
||||
ASSERT_EQ(false, rcl_ok());
|
||||
// Valid argc/argv values and a valid allocator should succeed.
|
||||
{
|
||||
FakeTestArgv test_args;
|
||||
ret = rcl_init(test_args.argc, test_args.argv, rcl_get_default_allocator());
|
||||
EXPECT_EQ(RCL_RET_OK, ret);
|
||||
ASSERT_EQ(true, rcl_ok());
|
||||
}
|
||||
// Then shutdown should work.
|
||||
ret = rcl_shutdown();
|
||||
EXPECT_EQ(RCL_RET_OK, ret);
|
||||
ASSERT_EQ(false, rcl_ok());
|
||||
// A repeat call to shutdown should not work.
|
||||
ret = rcl_shutdown();
|
||||
EXPECT_EQ(RCL_RET_NOT_INIT, ret);
|
||||
rcl_reset_error();
|
||||
ASSERT_EQ(false, rcl_ok());
|
||||
// Repeat, but valid, calls to rcl_init() should fail.
|
||||
{
|
||||
FakeTestArgv test_args;
|
||||
ret = rcl_init(test_args.argc, test_args.argv, rcl_get_default_allocator());
|
||||
EXPECT_EQ(RCL_RET_OK, ret);
|
||||
ASSERT_EQ(true, rcl_ok());
|
||||
ret = rcl_init(test_args.argc, test_args.argv, rcl_get_default_allocator());
|
||||
EXPECT_EQ(RCL_RET_ALREADY_INIT, ret);
|
||||
rcl_reset_error();
|
||||
ASSERT_EQ(true, rcl_ok());
|
||||
}
|
||||
// But shutdown should still work.
|
||||
ret = rcl_shutdown();
|
||||
EXPECT_EQ(ret, RCL_RET_OK);
|
||||
ASSERT_EQ(false, rcl_ok());
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue