-
Notifications
You must be signed in to change notification settings - Fork 125
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add introspection typesupport tests for C/C++ messages (#651)
Includes testing infrastructure. Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com> Co-authored-by: Geoffrey Biggs <gbiggs@killbots.net>
- Loading branch information
Showing
22 changed files
with
4,692 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
cmake_minimum_required(VERSION 3.8) | ||
project(rosidl_typesupport_introspection_tests) | ||
|
||
find_package(ament_cmake REQUIRED) | ||
|
||
if(BUILD_TESTING) | ||
find_package(ament_lint_auto REQUIRED) | ||
ament_lint_auto_find_test_dependencies() | ||
|
||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") | ||
add_compile_options(-Wall -Wextra -Wpedantic) | ||
endif() | ||
|
||
find_package(rosidl_cmake REQUIRED) | ||
find_package(rosidl_generator_c REQUIRED) | ||
find_package(rosidl_generator_cpp REQUIRED) | ||
find_package(rosidl_typesupport_introspection_c REQUIRED) | ||
find_package(rosidl_typesupport_introspection_cpp REQUIRED) | ||
find_package(test_interface_files REQUIRED) | ||
|
||
rosidl_generate_interfaces(${PROJECT_NAME} | ||
${test_interface_files_MSG_FILES} | ||
${test_interface_files_SRV_FILES} | ||
# ${test_interface_files_ACTION_FILES} | ||
SKIP_INSTALL | ||
) | ||
|
||
find_package(rcutils REQUIRED) | ||
find_package(rcpputils REQUIRED) | ||
find_package(rosidl_typesupport_interface REQUIRED) | ||
|
||
add_library(${PROJECT_NAME}_library INTERFACE) | ||
target_include_directories(${PROJECT_NAME}_library INTERFACE include/) | ||
target_link_libraries(${PROJECT_NAME}_library INTERFACE | ||
rcutils::rcutils | ||
rcpputils::rcpputils | ||
rosidl_typesupport_interface::rosidl_typesupport_interface | ||
rosidl_typesupport_introspection_c::rosidl_typesupport_introspection_c | ||
rosidl_typesupport_introspection_cpp::rosidl_typesupport_introspection_cpp) | ||
target_compile_features(${PROJECT_NAME}_library INTERFACE cxx_std_17) | ||
|
||
find_package(ament_cmake_gtest REQUIRED) | ||
file(GLOB test_files test/test_*.cpp) | ||
foreach(test_file ${test_files}) | ||
get_filename_component(target ${test_file} NAME_WE) | ||
|
||
ament_add_gtest(${target} ${test_file} | ||
# Ensure typesupport introspection libraries can be found | ||
APPEND_LIBRARY_DIRS "${CMAKE_CURRENT_BINARY_DIR}" | ||
) | ||
|
||
if(TARGET ${target}) | ||
target_include_directories(${target} PRIVATE test) | ||
target_link_libraries(${target} | ||
${PROJECT_NAME}_library | ||
${PROJECT_NAME}__rosidl_generator_c | ||
${PROJECT_NAME}__rosidl_generator_cpp | ||
${PROJECT_NAME}__rosidl_typesupport_introspection_c | ||
${PROJECT_NAME}__rosidl_typesupport_introspection_cpp) | ||
endif() | ||
endforeach() | ||
endif() | ||
|
||
ament_package() |
312 changes: 312 additions & 0 deletions
312
...dl_typesupport_introspection_tests/include/rosidl_typesupport_introspection_tests/api.hpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,312 @@ | ||
// Copyright 2022 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 ROSIDL_TYPESUPPORT_INTROSPECTION_TESTS__API_HPP_ | ||
#define ROSIDL_TYPESUPPORT_INTROSPECTION_TESTS__API_HPP_ | ||
|
||
#include <rosidl_runtime_c/message_initialization.h> | ||
#include <rosidl_typesupport_introspection_c/message_introspection.h> | ||
|
||
#include <rcpputils/shared_library.hpp> | ||
#include <rosidl_runtime_cpp/message_initialization.hpp> | ||
#include <rosidl_typesupport_introspection_cpp/message_introspection.hpp> | ||
|
||
#include "rosidl_typesupport_introspection_tests/type_traits.hpp" | ||
#include "rosidl_typesupport_introspection_tests/types.hpp" | ||
|
||
namespace rosidl_typesupport_introspection_tests | ||
{ | ||
|
||
|
||
/// Initialize a type erased C++ `message` based on its `message_descriptor`. | ||
inline void * initialize_message( | ||
void * message, | ||
const rosidl_typesupport_introspection_cpp::MessageMembers * message_descriptor) | ||
{ | ||
using rosidl_runtime_cpp::MessageInitialization; | ||
message_descriptor->init_function(message, MessageInitialization::ALL); | ||
return message; | ||
} | ||
|
||
/// Initialize a type erased C `message` based on its `message_descriptor`. | ||
inline void * initialize_message( | ||
void * message, | ||
const rosidl_typesupport_introspection_c__MessageMembers * message_descriptor) | ||
{ | ||
message_descriptor->init_function(message, ROSIDL_RUNTIME_C_MSG_INIT_ALL); | ||
return message; | ||
} | ||
|
||
/// Finalize a type erased `message` based on its `message_descriptor`. | ||
template<typename MessageDescriptorT> | ||
void finalize_message( | ||
void * message, | ||
const MessageDescriptorT * message_descriptor) | ||
{ | ||
message_descriptor->fini_function(message); | ||
} | ||
|
||
/// Get a message's namespace from its `message_descriptor`. | ||
template<typename MessageDescriptorT> | ||
const char * | ||
get_message_namespace(const MessageDescriptorT * message_descriptor) | ||
{ | ||
return message_descriptor->message_namespace_; | ||
} | ||
|
||
/// Get a message's name from its `message_descriptor`. | ||
template<typename MessageDescriptorT> | ||
const char * get_message_name(const MessageDescriptorT * message_descriptor) | ||
{ | ||
return message_descriptor->message_name_; | ||
} | ||
|
||
/// Get a message's (base) size from its `message_descriptor`. | ||
template<typename MessageDescriptorT> | ||
size_t get_message_size(const MessageDescriptorT * message_descriptor) | ||
{ | ||
return message_descriptor->size_of_; | ||
} | ||
|
||
/// Get a message's member count from its `message_descriptor`. | ||
template<typename MessageDescriptorT> | ||
size_t get_member_count(const MessageDescriptorT * message_descriptor) | ||
{ | ||
return message_descriptor->member_count_; | ||
} | ||
|
||
/// Get a mutable type erased reference to a member | ||
/// from a type erased `message` given its `member_descriptor`. | ||
template<typename MemberDescriptorT> | ||
void * get_member(void * message, const MemberDescriptorT * member_descriptor) | ||
{ | ||
return reinterpret_cast<uint8_t *>(message) + | ||
member_descriptor->offset_; | ||
} | ||
|
||
/// Get a mutable reference to a member from a | ||
/// type erased `message` given its `member_descriptor`. | ||
template<typename MemberT, typename MemberDescriptorT> | ||
MemberT & | ||
get_member(void * message, const MemberDescriptorT * member_descriptor) | ||
{ | ||
return *reinterpret_cast<MemberT *>(get_member(message, member_descriptor)); | ||
} | ||
|
||
/// Get the member descriptor for the `i`th member | ||
/// of a message given its `message_descriptor`. | ||
template<typename MessageDescriptorT> | ||
auto get_member_descriptor( | ||
const MessageDescriptorT * message_descriptor, | ||
const size_t i) | ||
{ | ||
return &(message_descriptor->members_[i]); | ||
} | ||
|
||
/// Check if a member has a simple structure | ||
/// (i.e not an array nor a sequence) given its | ||
/// `member_descriptor`. | ||
template<typename MemberDescriptorT> | ||
bool has_simple_structure(const MemberDescriptorT * member_descriptor) | ||
{ | ||
return !member_descriptor->is_array_ && | ||
member_descriptor->size_function == nullptr && | ||
member_descriptor->get_function == nullptr && | ||
member_descriptor->get_const_function == nullptr && | ||
member_descriptor->fetch_function == nullptr && | ||
member_descriptor->assign_function == nullptr && | ||
member_descriptor->resize_function == nullptr; | ||
} | ||
|
||
/// Check if a member has an iterable structure (i.e either an array or a | ||
/// sequence) given its `member_descriptor`. | ||
template<typename MemberDescriptorT> | ||
bool has_iterable_structure(const MemberDescriptorT * member_descriptor) | ||
{ | ||
return member_descriptor->is_array_ && | ||
member_descriptor->size_function != nullptr && | ||
member_descriptor->fetch_function != nullptr && | ||
member_descriptor->assign_function != nullptr; | ||
} | ||
|
||
/// Check if a member has an array structure given its `member_descriptor`. | ||
template<typename MemberDescriptorT> | ||
bool has_array_structure(const MemberDescriptorT * member_descriptor) | ||
{ | ||
return has_iterable_structure(member_descriptor) && | ||
member_descriptor->get_function != nullptr && | ||
member_descriptor->get_const_function != nullptr && | ||
member_descriptor->resize_function == nullptr; | ||
} | ||
|
||
/// Check if a member has an array structure of `size` elements given its | ||
/// `member_descriptor`. | ||
template<typename MemberDescriptorT> | ||
bool has_array_structure( | ||
const MemberDescriptorT * member_descriptor, const size_t size) | ||
{ | ||
return has_array_structure(member_descriptor) && | ||
member_descriptor->array_size_ == size; | ||
} | ||
|
||
/// Check if a member has an unbounded sequence structure given its | ||
/// `member_descriptor`. | ||
template<typename MemberDescriptorT> | ||
bool has_sequence_structure(const MemberDescriptorT * member_descriptor) | ||
{ | ||
return has_iterable_structure(member_descriptor) && | ||
!member_descriptor->is_upper_bound_ && | ||
member_descriptor->array_size_ == 0u && | ||
member_descriptor->resize_function != nullptr; | ||
} | ||
|
||
/// Check if a member has a sequence structure bounded to `size` | ||
/// elements given its `member_descriptor`. | ||
template<typename MemberDescriptorT> | ||
bool has_bounded_sequence_structure( | ||
const MemberDescriptorT * member_descriptor, const size_t size) | ||
{ | ||
return has_iterable_structure(member_descriptor) && | ||
member_descriptor->is_upper_bound_ && | ||
member_descriptor->array_size_ == size && | ||
member_descriptor->resize_function != nullptr; | ||
} | ||
|
||
/// Check if a member is of `MessageT` type given its `member_descriptor`. | ||
template<typename MessageT, typename MemberDescriptorT> | ||
bool is_message_type_member(const MemberDescriptorT * member_descriptor) | ||
{ | ||
using TypeSupportLibraryT = | ||
typename introspection_traits<MessageT>::TypeSupportLibraryT; | ||
const auto & typesupport = introspection_traits<MessageT>::typesupport; | ||
rcpputils::SharedLibrary library{ | ||
rcpputils::get_platform_library_name(TypeSupportLibraryT::name)}; | ||
auto message_typesupport_fetch = | ||
reinterpret_cast<MessageTypeSupportFetchFunctionT>( | ||
library.get_symbol(typesupport.symbol)); | ||
if (!message_typesupport_fetch) { | ||
return false; | ||
} | ||
const rosidl_message_type_support_t * message_typesupport = | ||
get_message_typesupport_handle( | ||
message_typesupport_fetch(), | ||
TypeSupportLibraryT::identifier); | ||
return member_descriptor->type_id_ == ROS_TYPE_MESSAGE && | ||
member_descriptor->members_ == message_typesupport; | ||
} | ||
|
||
/// Check if a member is of `type_id` type given its `member_descriptor`. | ||
template<typename MemberDescriptorT> | ||
bool is_base_type_member(const MemberDescriptorT * member_descriptor, int type_id) | ||
{ | ||
return member_descriptor->type_id_ == type_id && | ||
member_descriptor->members_ == nullptr; | ||
} | ||
|
||
/// Check if a member is of string type, optionally bounded to `upper_bound` | ||
/// characters, given its `member_descriptor`. | ||
template<typename MemberDescriptorT> | ||
bool is_string_member( | ||
const MemberDescriptorT * member_descriptor, | ||
const size_t upper_bound = 0u) | ||
{ | ||
return member_descriptor->type_id_ == ROS_TYPE_STRING && | ||
member_descriptor->members_ == nullptr && | ||
member_descriptor->string_upper_bound_ == upper_bound; | ||
} | ||
|
||
/// Get an immutable type erased reference to a member | ||
/// from a type erased message given its `member_descriptor`. | ||
template<typename MemberDescriptorT> | ||
const void * get_const_member( | ||
const void * message, | ||
const MemberDescriptorT * member_descriptor) | ||
{ | ||
return reinterpret_cast<const uint8_t *>(message) + | ||
member_descriptor->offset_; | ||
} | ||
|
||
/// Get an immutable reference to a member from a type | ||
/// erased message given its `member_descriptor`. | ||
template<typename MemberT, typename MemberDescriptorT> | ||
const MemberT & get_const_member( | ||
const void * message, | ||
const MemberDescriptorT * member_descriptor) | ||
{ | ||
return *reinterpret_cast<const MemberT *>( | ||
get_const_member(message, member_descriptor)); | ||
} | ||
|
||
/// Get a member's name from its `member_descriptor`. | ||
template<typename MemberDescriptorT> | ||
const char * get_member_name(const MemberDescriptorT * member_descriptor) | ||
{ | ||
return member_descriptor->name_; | ||
} | ||
|
||
/// Get a member's type id from its `member_descriptor`. | ||
template<typename MemberDescriptorT> | ||
int get_member_base_type(const MemberDescriptorT * member_descriptor) | ||
{ | ||
return member_descriptor->type_id_; | ||
} | ||
|
||
/// Fetch the `ith` item value of a type erased, iterable | ||
/// `member` given its `member_descriptor`. | ||
template<typename ItemT, typename MemberDescriptorT> | ||
ItemT fetch_member_item( | ||
const void * member, | ||
const MemberDescriptorT * member_descriptor, | ||
const size_t i) | ||
{ | ||
ItemT value; | ||
member_descriptor->fetch_function(member, i, &value); | ||
return value; | ||
} | ||
|
||
/// Assign a `value` to the `ith` item of a type erased, | ||
/// iterable `member` given its `member_descriptor`. | ||
template<typename ItemT, typename MemberDescriptorT> | ||
void assign_member_item( | ||
void * member, | ||
const MemberDescriptorT * member_descriptor, | ||
const size_t i, ItemT && value) | ||
{ | ||
member_descriptor->assign_function(member, i, &value); | ||
} | ||
|
||
/// Get the size of an iterable, type erased `member` | ||
/// given its `member_descriptor`. | ||
template<typename MemberDescriptorT> | ||
size_t get_member_size( | ||
const void * member, | ||
const MemberDescriptorT * member_descriptor) | ||
{ | ||
return member_descriptor->size_function(member); | ||
} | ||
|
||
/// Resize a sequence, type erased `member` | ||
/// to `size` given its `member_descriptor`. | ||
template<typename MemberDescriptorT> | ||
void resize_member( | ||
void * member, | ||
const MemberDescriptorT * member_descriptor, | ||
const size_t size) | ||
{ | ||
member_descriptor->resize_function(member, size); | ||
} | ||
|
||
} // namespace rosidl_typesupport_introspection_tests | ||
|
||
#endif // ROSIDL_TYPESUPPORT_INTROSPECTION_TESTS__API_HPP_ |
Oops, something went wrong.