Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complete rcutils API coverage #272

Merged
merged 20 commits into from
Aug 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ if(BUILD_TESTING)
osrf_testing_tools_cpp::memory_tools LIBRARY_PRELOAD_ENVIRONMENT_IS_AVAILABLE)

ament_add_gtest(test_logging test/test_logging.cpp)
target_link_libraries(test_logging ${PROJECT_NAME} osrf_testing_tools_cpp::memory_tools)
target_link_libraries(test_logging ${PROJECT_NAME} mimick osrf_testing_tools_cpp::memory_tools)
Blast545 marked this conversation as resolved.
Show resolved Hide resolved

add_executable(test_logging_long_messages test/test_logging_long_messages.cpp)
target_link_libraries(test_logging_long_messages ${PROJECT_NAME})
Expand Down Expand Up @@ -234,7 +234,7 @@ if(BUILD_TESTING)
test/test_char_array.cpp
)
if(TARGET test_char_array)
target_link_libraries(test_char_array ${PROJECT_NAME})
target_link_libraries(test_char_array ${PROJECT_NAME} mimick)
endif()

# Can't use C++ with stdatomic_helper.h
Expand Down Expand Up @@ -275,7 +275,7 @@ if(BUILD_TESTING)
test/test_split.cpp
)
if(TARGET test_split)
target_link_libraries(test_split ${PROJECT_NAME})
target_link_libraries(test_split ${PROJECT_NAME} mimick)
endif()

rcutils_custom_add_gtest(test_find
Expand Down Expand Up @@ -322,7 +322,7 @@ if(BUILD_TESTING)
)
if(TARGET test_filesystem)
ament_target_dependencies(test_filesystem "osrf_testing_tools_cpp")
target_link_libraries(test_filesystem ${PROJECT_NAME})
target_link_libraries(test_filesystem ${PROJECT_NAME} mimick)
target_compile_definitions(test_filesystem PRIVATE BUILD_DIR="${CMAKE_CURRENT_BINARY_DIR}")
endif()

Expand All @@ -337,7 +337,7 @@ if(BUILD_TESTING)
test/test_format_string.cpp
)
if(TARGET test_format_string)
target_link_libraries(test_format_string ${PROJECT_NAME})
target_link_libraries(test_format_string ${PROJECT_NAME} mimick)
endif()

rcutils_custom_add_gtest(test_string_map
Expand Down Expand Up @@ -375,14 +375,14 @@ if(BUILD_TESTING)
# which is appropriate when building the dll but not consuming it.
target_compile_definitions(dummy_shared_library PRIVATE "DUMMY_SHARED_LIBRARY_BUILDING_DLL")
endif()
target_link_libraries(test_shared_library ${PROJECT_NAME})
target_link_libraries(test_shared_library ${PROJECT_NAME} mimick)
endif()

rcutils_custom_add_gtest(test_time
test/test_time.cpp
ENV ${memory_tools_test_env_vars})
if(TARGET test_time)
target_link_libraries(test_time ${PROJECT_NAME} osrf_testing_tools_cpp::memory_tools)
target_link_libraries(test_time ${PROJECT_NAME} mimick osrf_testing_tools_cpp::memory_tools)
endif()

rcutils_custom_add_gtest(test_snprintf
Expand Down Expand Up @@ -436,7 +436,7 @@ if(BUILD_TESTING)
RCUTILS_COLORIZED_OUTPUT=1
)
if(TARGET test_logging_custom_env)
target_link_libraries(test_logging_custom_env ${PROJECT_NAME} osrf_testing_tools_cpp::memory_tools)
target_link_libraries(test_logging_custom_env ${PROJECT_NAME} osrf_testing_tools_cpp::memory_tools mimick)
endif()

# RCUTILS_LOGGING_MAX_OUTPUT_FORMAT_LEN is defined as 2048, truncation should occur
Expand All @@ -452,7 +452,7 @@ if(BUILD_TESTING)
RCUTILS_COLORIZED_OUTPUT=0
)
if(TARGET test_logging_custom_env2)
target_link_libraries(test_logging_custom_env2 ${PROJECT_NAME} osrf_testing_tools_cpp::memory_tools)
target_link_libraries(test_logging_custom_env2 ${PROJECT_NAME} osrf_testing_tools_cpp::memory_tools mimick)
endif()

rcutils_custom_add_gtest(test_logging_bad_env test/test_logging_bad_env.cpp
Expand Down
251 changes: 251 additions & 0 deletions test/mocking_utils/filesystem.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
// 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 MOCKING_UTILS__FILESYSTEM_HPP_
#define MOCKING_UTILS__FILESYSTEM_HPP_

#include <stdio.h>
#include <string.h>
#include <sys/stat.h>

#ifndef _WIN32
#include <sys/types.h>
#include <dirent.h>
#else
#include <windows.h>
#endif

#include <map>
#include <string>
#include <type_traits>

#include "rcutils/macros.h"

#include "patch.hpp"

namespace mocking_utils
{
namespace filesystem
{

/// Platform-independent set of file type constants.
namespace file_types
{
constexpr auto REGULAR_FILE = S_IFREG;
constexpr auto DIRECTORY = S_IFDIR;
} // namespace file_types

/// Platform-independent set of file permission constants.
namespace permissions
{
#ifndef _WIN32
constexpr auto USER_READABLE = S_IRUSR;
constexpr auto USER_WRITABLE = S_IWUSR;
#else
constexpr auto USER_READABLE = _S_IREAD;
constexpr auto USER_WRITABLE = _S_IWRITE;
#endif
} // namespace permissions

// Deal with binary API quirks in 64 bit MacOS.
#if defined(__MACH__) && defined(_DARWIN_FEATURE_64_BIT_INODE)
#define MOCKING_UTILS_FILESYSTEM_PATCH_TARGET(scope, function) \
(std::string(RCUTILS_STRINGIFY(function) "$INODE64") + "@" + (scope))
#else
#define MOCKING_UTILS_FILESYSTEM_PATCH_TARGET MOCKING_UTILS_PATCH_TARGET
#endif

#if !defined(_WIN32)

/// Helper class for patching the filesystem API.
/**
* \tparam ID Numerical identifier for this patches. Ought to be unique.
*/
template<size_t ID>
class FileSystem
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it's a bit subjective and I suggested the opposite on a different file, but this class doesn't seem to reuse much code across #ifdef blocks. Since it's the filesystem and each OS handles it differently, I think this file makes sense define the full class in single ifdef blocks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See 83140ec.

{
public:
/// Construct mocked filesystem.
/**
* \param[in] scope Scope target string, using Mimick syntax.
* \see mocking_utils::Patch documentation for further reference.
*/
explicit FileSystem(const std::string & scope)
: opendir_mock_(
MOCKING_UTILS_FILESYSTEM_PATCH_TARGET(scope, opendir),
MOCKING_UTILS_PATCH_PROXY(opendir)),
#ifndef _GNU_SOURCE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also seems like a good candidate for combining with the ifndef block at line 88 below. Line 87 is the only line shared between the two cases.

stat_mock_(
MOCKING_UTILS_FILESYSTEM_PATCH_TARGET(scope, stat),
MOCKING_UTILS_PATCH_PROXY(stat))
{
stat_mock_.then_call(
std::bind(
&FileSystem::do_stat, this,
std::placeholders::_1, std::placeholders::_2));
#else
// Deal with binary API quirks in GNU Linux.
__xstat_mock_(
MOCKING_UTILS_FILESYSTEM_PATCH_TARGET(scope, __xstat),
MOCKING_UTILS_PATCH_PROXY(__xstat))
{
__xstat_mock_.then_call(
std::bind(
&FileSystem::do___xstat, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3));
#endif
opendir_mock_.then_call(std::bind(&FileSystem::do_opendir, this, std::placeholders::_1));
}

/// Force APIs that return file descriptors or handles to fail as if these had been exhausted.
void exhaust_file_descriptors()
{
forced_errno_ = EMFILE;
}

/// Get information from file in the mocked filesystem.
/**
* \param[in] path Path to the file whose information is to be retrieved.
* If file is not found, one will be added.
* \return mutable reference to file information.
*/
struct stat & file_info(const std::string & path)
{
return files_info_[path];
}

private:
DIR * do_opendir(const char *)
{
if (forced_errno_ != 0) {
errno = forced_errno_;
return NULL;
}
errno = ENOENT;
return NULL;
}
MOCKING_UTILS_PATCH_TYPE(ID, opendir) opendir_mock_;

#ifndef _GNU_SOURCE
int do_stat(const char * path, struct stat * info)
{
#else
int do___xstat(int, const char * path, struct stat * info)
{
#endif
if (files_info_.count(path) == 0) {
errno = ENOENT;
return -1;
}
*info = files_info_[path];
return 0;
}

#ifndef _GNU_SOURCE
MOCKING_UTILS_PATCH_TYPE(ID, stat) stat_mock_;
#else
MOCKING_UTILS_PATCH_TYPE(ID, __xstat) __xstat_mock_;
#endif

int forced_errno_{0};
std::map<std::string, struct stat> files_info_;
};

#else // !defined(_WIN32)

/// Helper class for patching the filesystem API.
/**
* \tparam ID Numerical identifier for this patches. Ought to be unique.
*/
template<size_t ID>
class FileSystem
{
public:
/// Construct mocked filesystem.
/**
* \param[in] scope Scope target string, using Mimick syntax.
* \see mocking_utils::Patch documentation for further reference.
*/
explicit FileSystem(const std::string & scope)
: find_first_file_mock_(MOCKING_UTILS_FILESYSTEM_PATCH_TARGET(scope, FindFirstFileA),
MOCKING_UTILS_PATCH_PROXY(FindFirstFileA)),
_stat_mock_(MOCKING_UTILS_FILESYSTEM_PATCH_TARGET(scope, _stat),
MOCKING_UTILS_PATCH_PROXY(_stat))
{
find_first_file_mock_.then_call(
std::bind(
&FileSystem::do_FindFirstFileA, this,
std::placeholders::_1, std::placeholders::_2));
_stat_mock_.then_call(
std::bind(
&FileSystem::do__stat, this,
std::placeholders::_1, std::placeholders::_2));
}

/// Force APIs that return file descriptors or handles to fail as if these had been exhausted.
void exhaust_file_descriptors()
{
forced_errno_ = ERROR_NO_MORE_SEARCH_HANDLES;
}

/// Get information from file in the mocked filesystem.
/**
* \param[in] path Path to the file whose information is to be retrieved.
* If file is not found, one will be added.
* \return mutable reference to file information.
*/
struct _stat & file_info(const std::string & path)
{
return files_info_[path];
}

private:
HANDLE do_FindFirstFileA(LPCSTR, LPWIN32_FIND_DATAA)
{
if (forced_errno_ != 0) {
SetLastError(forced_errno_);
return INVALID_HANDLE_VALUE;
}
SetLastError(ERROR_FILE_NOT_FOUND);
return INVALID_HANDLE_VALUE;
}

MOCKING_UTILS_PATCH_TYPE(ID, FindFirstFileA) find_first_file_mock_;

int do__stat(const char * path, struct _stat * info)
{
if (files_info_.count(path) == 0) {
errno = ENOENT;
return -1;
}
*info = files_info_[path];
return 0;
}

MOCKING_UTILS_PATCH_TYPE(ID, _stat) _stat_mock_;

int forced_errno_{0};
std::map<std::string, struct _stat> files_info_;
};

#endif // else !defined(_WIN32)

} // namespace filesystem

/// Patch filesystem API in a given `scope`.
#define patch_filesystem(scope) filesystem::FileSystem<__COUNTER__>(scope)

} // namespace mocking_utils

#endif // MOCKING_UTILS__FILESYSTEM_HPP_
Loading