From 52c438fd23128b4de96805ed5e09b91013914122 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Tue, 2 Apr 2019 13:41:13 -0500 Subject: [PATCH 1/3] Add commonly-used filesystem helper to utils (#5) * Add commonly-used filesystem helper to utils. Signed-off-by: Michael Carroll * Add split and tests. Signed-off-by: Michael Carroll * Update header path and fix tests. Signed-off-by: Michael Carroll * Add basic `is_absolute` functionality. Signed-off-by: Michael Carroll * Lint. Signed-off-by: Michael Carroll * Fixes for tests on Windows and Linux. Signed-off-by: Michael Carroll * Change documentation note about std::filesystem. Signed-off-by: Michael Carroll * Move preferred separator location. Signed-off-by: Michael Carroll --- CMakeLists.txt | 4 + include/rcpputils/filesystem_helper.hpp | 165 ++++++++++++++++++ include/rcpputils/split.hpp | 71 ++++++++ test/test_filesystem_helper.cpp | 105 +++++++++++ test/test_split.cpp | 223 ++++++++++++++++++++++++ 5 files changed, 568 insertions(+) create mode 100644 include/rcpputils/filesystem_helper.hpp create mode 100644 include/rcpputils/split.hpp create mode 100644 test/test_filesystem_helper.cpp create mode 100644 test/test_split.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ab7715..cf1343b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,10 @@ if(BUILD_TESTING) ament_lint_auto_find_test_dependencies() ament_add_gtest(test_basic test/test_basic.cpp) + + ament_add_gtest(test_split test/test_split.cpp) + + ament_add_gtest(test_filesystem_helper test/test_filesystem_helper.cpp) endif() ament_package() diff --git a/include/rcpputils/filesystem_helper.hpp b/include/rcpputils/filesystem_helper.hpp new file mode 100644 index 0000000..87d2c8d --- /dev/null +++ b/include/rcpputils/filesystem_helper.hpp @@ -0,0 +1,165 @@ +// Copyright (c) 2019, Open Source Robotics Foundation, Inc. +// All rights reserved. +// +// Software License Agreement (BSD License 2.0) +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// This file is originally from: +// https://github.com/ros/pluginlib/blob/1a4de29fa55173e9b897ca8ff57ebc88c047e0b3/pluginlib/include/pluginlib/impl/filesystem_helper.hpp + +/** + * If std::filesystem is not available the necessary functions are emulated. + * + * Note: Once std::filesystem is supported on all ROS2 platforms, this class + * can be deprecated in favor of the built-in functionality. + */ + +#ifndef RCPPUTILS__FILESYSTEM_HELPER_HPP_ +#define RCPPUTILS__FILESYSTEM_HELPER_HPP_ + +#include +#include +#include + +#ifdef _WIN32 +# define RCPPUTILS_IMPL_OS_DIRSEP '\\' +#else +# define RCPPUTILS_IMPL_OS_DIRSEP '/' +#endif + +#ifdef _WIN32 +# include +# define access _access_s +#else +# include +#endif + +#include "rcpputils/split.hpp" + +namespace rcpputils +{ +namespace fs +{ + +static constexpr const char kPreferredSeparator = RCPPUTILS_IMPL_OS_DIRSEP; + +class path +{ +public: + path() + : path("") + {} + + path(const std::string & p) // NOLINT(runtime/explicit): this is a conversion constructor + : path_(p), path_as_vector_(split(p, kPreferredSeparator)) + { + std::replace(path_.begin(), path_.end(), '\\', kPreferredSeparator); + std::replace(path_.begin(), path_.end(), '/', kPreferredSeparator); + } + + std::string string() const + { + return path_; + } + + bool exists() const + { + return access(path_.c_str(), 0) == 0; + } + + bool is_absolute() const + { + return path_.compare(0, 1, "/") == 0 || path_.compare(1, 2, ":\\") == 0; + } + + std::vector::const_iterator cbegin() const + { + return path_as_vector_.cbegin(); + } + + std::vector::const_iterator cend() const + { + return path_as_vector_.cend(); + } + + path parent_path() const + { + path parent(""); + for (auto it = this->cbegin(); it != --this->cend(); ++it) { + parent /= *it; + } + return parent; + } + + path filename() const + { + return path_.empty() ? path() : *--this->cend(); + } + + path operator/(const std::string & other) + { + return this->operator/(path(other)); + } + + path & operator/=(const std::string & other) + { + this->operator/=(path(other)); + return *this; + } + + path operator/(const path & other) + { + return path(*this).operator/=(other); + } + + path & operator/=(const path & other) + { + this->path_ += kPreferredSeparator + other.string(); + this->path_as_vector_.insert( + std::end(this->path_as_vector_), + std::begin(other.path_as_vector_), std::end(other.path_as_vector_)); + return *this; + } + +private: + std::string path_; + std::vector path_as_vector_; +}; + +inline bool exists(const path & path_to_check) +{ + return path_to_check.exists(); +} + +#undef RCPPUTILS_IMPL_OS_DIRSEP + +} // namespace fs +} // namespace rcpputils + +#endif // RCPPUTILS__FILESYSTEM_HELPER_HPP_ diff --git a/include/rcpputils/split.hpp b/include/rcpputils/split.hpp new file mode 100644 index 0000000..2ff8573 --- /dev/null +++ b/include/rcpputils/split.hpp @@ -0,0 +1,71 @@ +// Copyright (c) 2019, Open Source Robotics Foundation, Inc. +// All rights reserved. +// +// Software License Agreement (BSD License 2.0) +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// This file is originally from: +// https://github.com/ros/pluginlib/blob/1a4de29fa55173e9b897ca8ff57ebc88c047e0b3/pluginlib/include/pluginlib/impl/split.hpp + +#ifndef RCPPUTILS__SPLIT_HPP_ +#define RCPPUTILS__SPLIT_HPP_ + +#include +#include +#include + +namespace rcpputils +{ + +/// Split a specified input into tokens using a delimiter. +/** + * The returned vector will contain the tokens split from the input + * + * \param[in] input the input string to be split + * \param[in] delim the dlelimiter used to split the input string + * \return A vector of tokens. + */ +inline std::vector +split(const std::string & input, char delim, bool skip_empty = false) +{ + std::vector result; + std::stringstream ss; + ss.str(input); + std::string item; + while (std::getline(ss, item, delim)) { + if (skip_empty && item == "") { + continue; + } + result.push_back(item); + } + return result; +} +} // namespace rcpputils + +#endif // RCPPUTILS__SPLIT_HPP_ diff --git a/test/test_filesystem_helper.cpp b/test/test_filesystem_helper.cpp new file mode 100644 index 0000000..be4ca3c --- /dev/null +++ b/test/test_filesystem_helper.cpp @@ -0,0 +1,105 @@ +// Copyright 2019 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 + +#include + +#include "rcpputils/filesystem_helper.hpp" + +#ifdef WIN32 +static constexpr const bool is_win32 = true; +#else +static constexpr const bool is_win32 = false; +#endif + +using path = rcpputils::fs::path; + +TEST(TestFilesystemHelper, join_path) +{ + auto p = path("foo") / path("bar"); + + if (is_win32) { + EXPECT_EQ("foo\\bar", p.string()); + } else { + EXPECT_EQ("foo/bar", p.string()); + } +} + +TEST(TestFilesystemHelper, to_native_path) +{ + { + auto p = path("/foo/bar/baz"); + if (is_win32) { + EXPECT_EQ("\\foo\\bar\\baz", p.string()); + } else { + EXPECT_EQ("/foo/bar/baz", p.string()); + } + } + { + auto p = path("\\foo\\bar\\baz"); + if (is_win32) { + EXPECT_EQ("\\foo\\bar\\baz", p.string()); + } else { + EXPECT_EQ("/foo/bar/baz", p.string()); + } + } + { + auto p = path("/foo//bar/baz"); + if (is_win32) { + EXPECT_EQ("\\foo\\\\bar\\baz", p.string()); + } else { + EXPECT_EQ("/foo//bar/baz", p.string()); + } + } + { + auto p = path("\\foo\\\\bar\\baz"); + if (is_win32) { + EXPECT_EQ("\\foo\\\\bar\\baz", p.string()); + } else { + EXPECT_EQ("/foo//bar/baz", p.string()); + } + } +} + +TEST(TestFilesystemHelper, is_absolute) +{ + if (is_win32) { + { + auto p = path("C:\\foo\\bar\\baz"); + EXPECT_TRUE(p.is_absolute()); + } + { + auto p = path("D:\\foo\\bar\\baz"); + EXPECT_TRUE(p.is_absolute()); + } + { + auto p = path("C:/foo/bar/baz"); + EXPECT_TRUE(p.is_absolute()); + } + { + auto p = path("foo/bar/baz"); + EXPECT_FALSE(p.is_absolute()); + } + } else { + { + auto p = path("/foo/bar/baz"); + EXPECT_TRUE(p.is_absolute()); + } + { + auto p = path("foo/bar/baz"); + EXPECT_FALSE(p.is_absolute()); + } + } +} diff --git a/test/test_split.cpp b/test/test_split.cpp new file mode 100644 index 0000000..13b5948 --- /dev/null +++ b/test/test_split.cpp @@ -0,0 +1,223 @@ +// Copyright 2019 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 + +#include "rcpputils/split.hpp" + +TEST(test_split, split) { + { + auto ret = rcpputils::split("", '/', true); + EXPECT_EQ(0u, ret.size()); + } + { + auto ret = rcpputils::split("", '/', false); + EXPECT_EQ(0u, ret.size()); + } + { + auto ret = rcpputils::split("hello_world", '/', true); + EXPECT_EQ(1u, ret.size()); + EXPECT_EQ("hello_world", ret[0]); + } + { + auto ret = rcpputils::split("hello_world", '/', false); + EXPECT_EQ(1u, ret.size()); + EXPECT_EQ("hello_world", ret[0]); + } + { + auto ret = rcpputils::split("hello/world", '/', true); + EXPECT_EQ(2u, ret.size()); + EXPECT_EQ("hello", ret[0]); + EXPECT_EQ("world", ret[1]); + } + { + auto ret = rcpputils::split("hello/world", '/', false); + EXPECT_EQ(2u, ret.size()); + EXPECT_EQ("hello", ret[0]); + EXPECT_EQ("world", ret[1]); + } + { + auto ret = rcpputils::split("/hello/world", '/', true); + EXPECT_EQ(2u, ret.size()); + EXPECT_EQ("hello", ret[0]); + EXPECT_EQ("world", ret[1]); + } + { + auto ret = rcpputils::split("/hello/world", '/', false); + EXPECT_EQ(3u, ret.size()); + EXPECT_EQ("", ret[0]); + EXPECT_EQ("hello", ret[1]); + EXPECT_EQ("world", ret[2]); + } + { + auto ret = rcpputils::split("hello/world/", '/', true); + EXPECT_EQ(2u, ret.size()); + EXPECT_EQ("hello", ret[0]); + EXPECT_EQ("world", ret[1]); + } + { + auto ret = rcpputils::split("hello/world/", '/', false); + EXPECT_EQ(2u, ret.size()); + EXPECT_EQ("hello", ret[0]); + EXPECT_EQ("world", ret[1]); + } + { + auto ret = rcpputils::split("hello//world", '/', true); + EXPECT_EQ(2u, ret.size()); + EXPECT_EQ("hello", ret[0]); + EXPECT_EQ("world", ret[1]); + } + { + auto ret = rcpputils::split("hello//world", '/', false); + EXPECT_EQ(3u, ret.size()); + EXPECT_EQ("hello", ret[0]); + EXPECT_EQ("", ret[1]); + EXPECT_EQ("world", ret[2]); + } + { + auto ret = rcpputils::split("/my/hello/world", '/', true); + EXPECT_EQ(3u, ret.size()); + EXPECT_EQ("my", ret[0]); + EXPECT_EQ("hello", ret[1]); + EXPECT_EQ("world", ret[2]); + } + { + auto ret = rcpputils::split("/my/hello/world", '/', false); + EXPECT_EQ(4u, ret.size()); + EXPECT_EQ("", ret[0]); + EXPECT_EQ("my", ret[1]); + EXPECT_EQ("hello", ret[2]); + EXPECT_EQ("world", ret[3]); + } + { + auto ret = rcpputils::split("/my//hello//world/", '/', true); + EXPECT_EQ(3u, ret.size()); + EXPECT_EQ("my", ret[0]); + EXPECT_EQ("hello", ret[1]); + EXPECT_EQ("world", ret[2]); + } + { + auto ret = rcpputils::split("/my//hello//world/", '/', false); + EXPECT_EQ(6u, ret.size()); + EXPECT_EQ("", ret[0]); + EXPECT_EQ("my", ret[1]); + EXPECT_EQ("", ret[2]); + EXPECT_EQ("hello", ret[3]); + EXPECT_EQ("", ret[4]); + EXPECT_EQ("world", ret[5]); + } +} + +TEST(test_split, split_backslash) { + { + auto ret = rcpputils::split("", '\\', true); + EXPECT_EQ(0u, ret.size()); + } + { + auto ret = rcpputils::split("", '\\', false); + EXPECT_EQ(0u, ret.size()); + } + { + auto ret = rcpputils::split("hello_world", '\\', true); + EXPECT_EQ(1u, ret.size()); + EXPECT_EQ("hello_world", ret[0]); + } + { + auto ret = rcpputils::split("hello_world", '\\', false); + EXPECT_EQ(1u, ret.size()); + EXPECT_EQ("hello_world", ret[0]); + } + { + auto ret = rcpputils::split("hello\\world", '\\', true); + EXPECT_EQ(2u, ret.size()); + EXPECT_EQ("hello", ret[0]); + EXPECT_EQ("world", ret[1]); + } + { + auto ret = rcpputils::split("hello\\world", '\\', false); + EXPECT_EQ(2u, ret.size()); + EXPECT_EQ("hello", ret[0]); + EXPECT_EQ("world", ret[1]); + } + { + auto ret = rcpputils::split("\\hello\\world", '\\', true); + EXPECT_EQ(2u, ret.size()); + EXPECT_EQ("hello", ret[0]); + EXPECT_EQ("world", ret[1]); + } + { + auto ret = rcpputils::split("\\hello\\world", '\\', false); + EXPECT_EQ(3u, ret.size()); + EXPECT_EQ("", ret[0]); + EXPECT_EQ("hello", ret[1]); + EXPECT_EQ("world", ret[2]); + } + { + auto ret = rcpputils::split("hello\\world\\", '\\', true); + EXPECT_EQ(2u, ret.size()); + EXPECT_EQ("hello", ret[0]); + EXPECT_EQ("world", ret[1]); + } + { + auto ret = rcpputils::split("hello\\world\\", '\\', false); + EXPECT_EQ(2u, ret.size()); + EXPECT_EQ("hello", ret[0]); + EXPECT_EQ("world", ret[1]); + } + { + auto ret = rcpputils::split("hello\\\\world", '\\', true); + EXPECT_EQ(2u, ret.size()); + EXPECT_EQ("hello", ret[0]); + EXPECT_EQ("world", ret[1]); + } + { + auto ret = rcpputils::split("hello\\\\world", '\\', false); + EXPECT_EQ(3u, ret.size()); + EXPECT_EQ("hello", ret[0]); + EXPECT_EQ("", ret[1]); + EXPECT_EQ("world", ret[2]); + } + { + auto ret = rcpputils::split("\\my\\hello\\world", '\\', true); + EXPECT_EQ(3u, ret.size()); + EXPECT_EQ("my", ret[0]); + EXPECT_EQ("hello", ret[1]); + EXPECT_EQ("world", ret[2]); + } + { + auto ret = rcpputils::split("\\my\\hello\\world", '\\', false); + EXPECT_EQ(4u, ret.size()); + EXPECT_EQ("", ret[0]); + EXPECT_EQ("my", ret[1]); + EXPECT_EQ("hello", ret[2]); + EXPECT_EQ("world", ret[3]); + } + { + auto ret = rcpputils::split("\\my\\\\hello\\\\world\\", '\\', true); + EXPECT_EQ(3u, ret.size()); + EXPECT_EQ("my", ret[0]); + EXPECT_EQ("hello", ret[1]); + EXPECT_EQ("world", ret[2]); + } + { + auto ret = rcpputils::split("\\my\\\\hello\\\\world\\", '\\', false); + EXPECT_EQ(6u, ret.size()); + EXPECT_EQ("", ret[0]); + EXPECT_EQ("my", ret[1]); + EXPECT_EQ("", ret[2]); + EXPECT_EQ("hello", ret[3]); + EXPECT_EQ("", ret[4]); + EXPECT_EQ("world", ret[5]); + } +} From 80b7635d8642d9825369123b71410aeec561e691 Mon Sep 17 00:00:00 2001 From: Thomas Moulard Date: Mon, 8 Apr 2019 10:48:22 -0700 Subject: [PATCH 2/3] Adds CODEOWNERS file Ensures that any two members of the @ros2/dev or @ros2/aws-robotics teams can approve changes. Signed-off-by: Thomas Moulard --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 CODEOWNERS diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..89ddc5a --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @ros2/aws-robotics-code-owners From 418f95ca55d879554d1fbdabf1ad3af055c6f3e1 Mon Sep 17 00:00:00 2001 From: Thomas Moulard Date: Fri, 5 Apr 2019 18:51:09 -0700 Subject: [PATCH 3/3] Fix leak in test_basic.cpp Signed-off-by: Thomas Moulard --- test/test_basic.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/test_basic.cpp b/test/test_basic.cpp index 6d0d726..efea1f4 100644 --- a/test/test_basic.cpp +++ b/test/test_basic.cpp @@ -136,6 +136,11 @@ struct FakeGuarded * pData = 0; } + ~FakeGuarded() + { + delete pData; + } + int incr_HaveGuard() RCPPUTILS_TSA_REQUIRES(mu) { return data++; @@ -244,7 +249,6 @@ TEST(test_tsa, ptr_guard) { int * old = guarded.pData; guarded.pData = new int; // pData itself is not protected by the mutex delete old; - delete guarded.pData; } TEST(test_tsa, shared_capability) {