Skip to content

Commit

Permalink
Test load and lookup functionality. (#135)
Browse files Browse the repository at this point in the history
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
  • Loading branch information
hidmic authored and ahcorde committed Oct 23, 2020
1 parent 9284cbf commit 1faae76
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 39 deletions.
18 changes: 18 additions & 0 deletions rmw_implementation/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,30 @@ else()
"rmw")
target_compile_definitions(${PROJECT_NAME}
PUBLIC "DEFAULT_RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION}")

# Causes the visibility macros to use dllexport rather than dllimport,
# which is appropriate when building the dll but not consuming it.
target_compile_definitions(${PROJECT_NAME} PRIVATE "RMW_IMPLEMENTATION_BUILDING_DLL")

if(BUILD_TESTING)
# Causes symbols to be exposed for tests to use.
target_compile_definitions(${PROJECT_NAME} PRIVATE
"RMW_IMPLEMENTATION_DEFAULT_VISIBILITY=RMW_IMPLEMENTATION_PUBLIC")
endif()

configure_rmw_library(${PROJECT_NAME})

ament_export_libraries(${PROJECT_NAME})
ament_export_targets(${PROJECT_NAME})
ament_export_dependencies(rcpputils rcutils)

if(BUILD_TESTING)
find_package(ament_cmake_gtest REQUIRED)
ament_add_gtest(test_functions test/test_functions.cpp)
ament_target_dependencies(test_functions rcutils rmw)
target_link_libraries(test_functions ${PROJECT_NAME})
endif()

install(
TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}
ARCHIVE DESTINATION lib
Expand Down
1 change: 1 addition & 0 deletions rmw_implementation/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

<depend>rmw_implementation_cmake</depend>

<test_depend>ament_cmake_gtest</test_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>

Expand Down
105 changes: 66 additions & 39 deletions rmw_implementation/src/functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#include "./functions.hpp"

#include <cstddef>
#include <stdexcept>

Expand Down Expand Up @@ -39,67 +41,92 @@
#define STRINGIFY_(s) #s
#define STRINGIFY(s) STRINGIFY_(s)

std::shared_ptr<rcpputils::SharedLibrary>
load_library()
{
std::string env_var;
try {
env_var = rcpputils::get_env_var("RMW_IMPLEMENTATION");
} catch (const std::exception & e) {
RMW_SET_ERROR_MSG_WITH_FORMAT_STRING(
"failed to fetch RMW_IMPLEMENTATION "
"from environment due to %s", e.what());
return nullptr;
}

if (env_var.empty()) {
env_var = STRINGIFY(DEFAULT_RMW_IMPLEMENTATION);
}

std::string library_path;
try {
library_path = rcpputils::find_library_path(env_var);
} catch (const std::exception & e) {
RMW_SET_ERROR_MSG_WITH_FORMAT_STRING(
"failed to find shared library due to %s", e.what());
return nullptr;
}

if (library_path.empty()) {
RMW_SET_ERROR_MSG_WITH_FORMAT_STRING(
"failed to find shared library '%s'",
env_var.c_str());
return nullptr;
}

try {
return std::make_shared<rcpputils::SharedLibrary>(library_path.c_str());
} catch (const std::exception & e) {
RMW_SET_ERROR_MSG_WITH_FORMAT_STRING(
"failed to load shared library '%s' due to %s",
library_path.c_str(), e.what());
return nullptr;
}
}

std::shared_ptr<rcpputils::SharedLibrary>
get_library()
{
static std::shared_ptr<rcpputils::SharedLibrary> lib;

if (!lib) {
std::string env_var = rcpputils::get_env_var("RMW_IMPLEMENTATION");
if (env_var.empty()) {
env_var = STRINGIFY(DEFAULT_RMW_IMPLEMENTATION);
}
std::string library_path = rcpputils::find_library_path(env_var);
if (library_path.empty()) {
RMW_SET_ERROR_MSG(
("failed to find shared library of rmw implementation. Searched " + env_var).c_str());
return nullptr;
}

try {
lib = std::make_shared<rcpputils::SharedLibrary>(library_path.c_str());
} catch (const std::runtime_error & e) {
RMW_SET_ERROR_MSG(
("failed to load shared library of rmw implementation: " + library_path +
" Exception: " + std::string(e.what())).c_str());
return nullptr;
} catch (const std::bad_alloc & e) {
RMW_SET_ERROR_MSG(
("failed to load shared library of rmw implementation " + library_path + ": " +
std::string(e.what())).c_str());
return nullptr;
}
lib = load_library();
}
return lib;
}

void *
get_symbol(const char * symbol_name)
lookup_symbol(std::shared_ptr<rcpputils::SharedLibrary> lib, const char * symbol_name)
{
std::shared_ptr<rcpputils::SharedLibrary> lib = get_library();

if (!lib) {
// error message set by get_library()
if (!rmw_error_is_set()) {
RMW_SET_ERROR_MSG("no shared library to lookup");
} // else assume library loading failed
return nullptr;
}

if (!lib->has_symbol(symbol_name)) {
rcutils_allocator_t allocator = rcutils_get_default_allocator();
char * msg = rcutils_format_string(
allocator,
"failed to resolve symbol '%s' in shared library '%s'", symbol_name,
lib->get_library_path().c_str());
if (msg) {
RMW_SET_ERROR_MSG(msg);
allocator.deallocate(msg, allocator.state);
} else {
RMW_SET_ERROR_MSG("failed to allocate memory for error message");
try {
std::string library_path = lib->get_library_path();
RMW_SET_ERROR_MSG_WITH_FORMAT_STRING(
"failed to resolve symbol '%s' in shared library '%s'",
symbol_name, library_path.c_str());
} catch (const std::runtime_error &) {
RMW_SET_ERROR_MSG_WITH_FORMAT_STRING(
"failed to resolve symbol '%s' in shared library",
symbol_name);
}
return nullptr;
}

return lib->get_symbol(symbol_name);
}

void *
get_symbol(const char * symbol_name)
{
return lookup_symbol(get_library(), symbol_name);
}

#ifdef __cplusplus
extern "C"
{
Expand Down
30 changes: 30 additions & 0 deletions rmw_implementation/src/functions.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// 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 FUNCTIONS_HPP_
#define FUNCTIONS_HPP_

#include <memory>

#include "rcpputils/shared_library.hpp"

#include "./visibility_control.h"

RMW_IMPLEMENTATION_DEFAULT_VISIBILITY
std::shared_ptr<rcpputils::SharedLibrary> load_library();

RMW_IMPLEMENTATION_DEFAULT_VISIBILITY
void * lookup_symbol(std::shared_ptr<rcpputils::SharedLibrary> lib, const char * symbol_name);

#endif // FUNCTIONS_HPP_
62 changes: 62 additions & 0 deletions rmw_implementation/src/visibility_control.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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 VISIBILITY_CONTROL_H_
#define VISIBILITY_CONTROL_H_

#ifdef __cplusplus
extern "C"
{
#endif

// This logic was borrowed (then namespaced) from the examples on the gcc wiki:
// https://gcc.gnu.org/wiki/Visibility

#if defined _WIN32 || defined __CYGWIN__
#ifdef __GNUC__
#define RMW_IMPLEMENTATION_EXPORT __attribute__ ((dllexport))
#define RMW_IMPLEMENTATION_IMPORT __attribute__ ((dllimport))
#else
#define RMW_IMPLEMENTATION_EXPORT __declspec(dllexport)
#define RMW_IMPLEMENTATION_IMPORT __declspec(dllimport)
#endif
#ifdef RMW_IMPLEMENTATION_BUILDING_DLL
#define RMW_IMPLEMENTATION_PUBLIC RMW_IMPLEMENTATION_EXPORT
#else
#define RMW_IMPLEMENTATION_PUBLIC RMW_IMPLEMENTATION_IMPORT
#endif
#define RMW_IMPLEMENTATION_PUBLIC_TYPE RMW_IMPLEMENTATION_PUBLIC
#define RMW_IMPLEMENTATION_LOCAL
#else
#define RMW_IMPLEMENTATION_EXPORT __attribute__ ((visibility("default")))
#define RMW_IMPLEMENTATION_IMPORT
#if __GNUC__ >= 4
#define RMW_IMPLEMENTATION_PUBLIC __attribute__ ((visibility("default")))
#define RMW_IMPLEMENTATION_LOCAL __attribute__ ((visibility("hidden")))
#else
#define RMW_IMPLEMENTATION_PUBLIC
#define RMW_IMPLEMENTATION_LOCAL
#endif
#define RMW_IMPLEMENTATION_PUBLIC_TYPE
#endif

#ifndef RMW_IMPLEMENTATION_DEFAULT_VISIBILITY
#define RMW_IMPLEMENTATION_DEFAULT_VISIBILITY
#endif

#ifdef __cplusplus
}
#endif

#endif // VISIBILITY_CONTROL_H_
57 changes: 57 additions & 0 deletions rmw_implementation/test/test_functions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// 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.

#include <gtest/gtest.h>

#include <memory>

#include "rcutils/env.h"
#include "rcutils/get_env.h"
#include "rcutils/testing/fault_injection.h"

#include "rmw/error_handling.h"

#include "../src/functions.hpp"

TEST(Functions, bad_load) {
const char * rmw_implementation = nullptr;
ASSERT_EQ(nullptr, rcutils_get_env("RMW_IMPLEMENTATION", &rmw_implementation));
EXPECT_TRUE(rcutils_set_env("RMW_IMPLEMENTATION", "not_an_rmw_implementation"));

EXPECT_EQ(nullptr, load_library());
EXPECT_TRUE(rmw_error_is_set());
rmw_reset_error();

EXPECT_TRUE(rcutils_set_env("RMW_IMPLEMENTATION", rmw_implementation));
}

TEST(Functions, nominal_load_and_lookup) {
std::shared_ptr<rcpputils::SharedLibrary> lib = load_library();
ASSERT_NE(nullptr, lib) << rmw_get_error_string().str;
EXPECT_NE(nullptr, lookup_symbol(lib, "rmw_init")) << rmw_get_error_string().str;
EXPECT_EQ(nullptr, lookup_symbol(lib, "not_an_rmw_function"));
EXPECT_TRUE(rmw_error_is_set());
rmw_reset_error();
}

TEST(Functions, load_and_lookup_with_internal_errors) {
RCUTILS_FAULT_INJECTION_TEST(
{
void * symbol = lookup_symbol(load_library(), "rmw_init");
if (!symbol) {
EXPECT_TRUE(rmw_error_is_set());
rmw_reset_error();
}
});
}

0 comments on commit 1faae76

Please sign in to comment.