Skip to content

Commit

Permalink
Leverage fs::path::preferred_separator in locateResource
Browse files Browse the repository at this point in the history
  • Loading branch information
Levi-Armstrong committed Jan 2, 2025
1 parent 76582bf commit ad309d7
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 45 deletions.
39 changes: 33 additions & 6 deletions tesseract_common/src/resource_locator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,20 +121,37 @@ void GeneralResourceLocator::processToken(const std::string& token)
}
}

std::size_t findSeparator(const std::string& str)
{
const size_t pos_slash = str.find('/');
const size_t pos_backslash = str.find('\\');

if (pos_slash != std::string::npos && pos_backslash != std::string::npos)
return std::min(pos_slash, pos_backslash);

if (pos_slash != std::string::npos)
return pos_slash;

if (pos_backslash != std::string::npos)
return pos_backslash;

return std::string::npos;
}

std::shared_ptr<Resource> GeneralResourceLocator::locateResource(const std::string& url) const
{
std::string mod_url = url;
if (url.find("file:///") == 0)
{
mod_url.erase(0, strlen("file://"));
size_t pos = mod_url.find('/');
const size_t pos = findSeparator(mod_url);
if (pos == std::string::npos)
return nullptr;
}
else if (url.find("package://") == 0)
{
mod_url.erase(0, strlen("package://"));
size_t pos = mod_url.find('/');
const size_t pos = findSeparator(mod_url);
if (pos == std::string::npos)
return nullptr;

Expand Down Expand Up @@ -230,12 +247,22 @@ tesseract_common::Resource::Ptr SimpleLocatedResource::locateResource(const std:
tesseract_common::fs::path path(url);
if (path.is_relative())
{
auto last_slash = url_.find_last_of('/');
if (last_slash == std::string::npos)
// Find the last occurrences of both separators
std::size_t last_slash = url_.find_last_of('/');
std::size_t last_backslash = url_.find_last_of('\\');
std::size_t last_separator{ 0 };
if (last_slash != std::string::npos && last_backslash != std::string::npos)
last_separator = std::max(last_slash, last_backslash);
else if (last_slash != std::string::npos)
last_separator = last_slash;
else if (last_backslash != std::string::npos)
last_separator = last_backslash;
else
return nullptr;

std::string url_base_path = url_.substr(0, last_slash);
std::string new_url = url_base_path + "/" + path.filename().string();
std::string url_base_path = url_.substr(0, last_separator);
std::string new_url = url_base_path + std::string(1, fs::path::preferred_separator) + path.filename().string();
CONSOLE_BRIDGE_logError("new_url: %s", new_url.c_str());
return parent_->locateResource(new_url);
}

Expand Down
1 change: 1 addition & 0 deletions tesseract_common/src/yaml_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <tesseract_common/macros.h>
TESSERACT_COMMON_IGNORE_WARNINGS_PUSH
#include <yaml-cpp/yaml.h>
#include <fstream>
TESSERACT_COMMON_IGNORE_WARNINGS_POP

#include <tesseract_common/utils.h>
Expand Down
32 changes: 26 additions & 6 deletions tesseract_common/test/resource_locator_unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,23 @@ TESSERACT_COMMON_IGNORE_WARNINGS_POP
#include <tesseract_common/types.h>
#include <tesseract_common/unit_test_utils.h>

std::size_t findSeparator(const std::string& str)
{
const size_t pos_slash = str.find('/');
const size_t pos_backslash = str.find('\\');

if (pos_slash != std::string::npos && pos_backslash != std::string::npos)
return std::min(pos_slash, pos_backslash);

if (pos_slash != std::string::npos)
return pos_slash;

if (pos_backslash != std::string::npos)
return pos_backslash;

return std::string::npos;
}

/** @brief Resource locator implementation using a provided function to locate file resources */
class TestResourceLocator : public tesseract_common::ResourceLocator
{
Expand All @@ -24,11 +41,10 @@ class TestResourceLocator : public tesseract_common::ResourceLocator
if (url.find("package://tesseract_common") == 0)
{
mod_url.erase(0, strlen("package://tesseract_common"));
size_t pos = mod_url.find('/');
const size_t pos = findSeparator(mod_url);
if (pos == std::string::npos)
return nullptr;

std::string package = mod_url.substr(0, pos);
mod_url.erase(0, pos);

tesseract_common::fs::path file_path(__FILE__);
Expand Down Expand Up @@ -64,10 +80,11 @@ TEST(ResourceLocatorUnit, SimpleResourceLocatorUnit) // NOLINT
EXPECT_FALSE(resource->getResourceContents().empty());
EXPECT_TRUE(resource->getResourceContentStream() != nullptr);

const std::string separator(1, fs::path::preferred_separator);
Resource::Ptr sub_resource = resource->locateResource("colcon.pkg");
EXPECT_TRUE(sub_resource != nullptr);
EXPECT_TRUE(sub_resource->isFile());
EXPECT_EQ(sub_resource->getUrl(), "package://tesseract_common/colcon.pkg");
EXPECT_EQ(sub_resource->getUrl(), "package://tesseract_common" + separator + "colcon.pkg");
EXPECT_EQ(tesseract_common::fs::path(sub_resource->getFilePath()), (package_path / "colcon.pkg"));
EXPECT_FALSE(sub_resource->getResourceContents().empty());
EXPECT_TRUE(sub_resource->getResourceContentStream() != nullptr);
Expand Down Expand Up @@ -108,10 +125,11 @@ TEST(ResourceLocatorUnit, GeneralResourceLocatorUnit1) // NOLINT
EXPECT_FALSE(resource->getResourceContents().empty());
EXPECT_TRUE(resource->getResourceContentStream() != nullptr);

const std::string separator(1, fs::path::preferred_separator);
Resource::Ptr sub_resource = resource->locateResource("colcon.pkg");
EXPECT_TRUE(sub_resource != nullptr);
EXPECT_TRUE(sub_resource->isFile());
EXPECT_EQ(sub_resource->getUrl(), "package://tesseract_common/colcon.pkg");
EXPECT_EQ(sub_resource->getUrl(), "package://tesseract_common" + separator + "colcon.pkg");
EXPECT_EQ(tesseract_common::fs::path(sub_resource->getFilePath()), (package_path / "colcon.pkg"));
EXPECT_FALSE(sub_resource->getResourceContents().empty());
EXPECT_TRUE(sub_resource->getResourceContentStream() != nullptr);
Expand Down Expand Up @@ -152,10 +170,11 @@ TEST(ResourceLocatorUnit, GeneralResourceLocatorUnit2) // NOLINT
EXPECT_FALSE(resource->getResourceContents().empty());
EXPECT_TRUE(resource->getResourceContentStream() != nullptr);

const std::string separator(1, fs::path::preferred_separator);
Resource::Ptr sub_resource = resource->locateResource("colcon.pkg");
EXPECT_TRUE(sub_resource != nullptr);
EXPECT_TRUE(sub_resource->isFile());
EXPECT_EQ(sub_resource->getUrl(), "package://tesseract_common/colcon.pkg");
EXPECT_EQ(sub_resource->getUrl(), "package://tesseract_common" + separator + "colcon.pkg");
EXPECT_EQ(tesseract_common::fs::path(sub_resource->getFilePath()), (package_path / "colcon.pkg"));
EXPECT_FALSE(sub_resource->getResourceContents().empty());
EXPECT_TRUE(sub_resource->getResourceContentStream() != nullptr);
Expand Down Expand Up @@ -191,10 +210,11 @@ TEST(ResourceLocatorUnit, ByteResourceUnit) // NOLINT
EXPECT_FALSE(byte_resource->getResourceContents().empty());
EXPECT_TRUE(byte_resource->getResourceContentStream() != nullptr);

const std::string separator(1, fs::path::preferred_separator);
Resource::Ptr sub_resource = byte_resource->locateResource("colcon.pkg");
EXPECT_TRUE(sub_resource != nullptr);
EXPECT_TRUE(sub_resource->isFile());
EXPECT_EQ(sub_resource->getUrl(), "package://tesseract_common/colcon.pkg");
EXPECT_EQ(sub_resource->getUrl(), "package://tesseract_common" + separator + "colcon.pkg");
EXPECT_EQ(tesseract_common::fs::path(sub_resource->getFilePath()), (package_path / "colcon.pkg"));
EXPECT_FALSE(sub_resource->getResourceContents().empty());
EXPECT_TRUE(sub_resource->getResourceContentStream() != nullptr);
Expand Down
72 changes: 39 additions & 33 deletions tesseract_common/test/tesseract_common_unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2642,7 +2642,8 @@ void createTestYamlWithIncludeDirectivesFile(const std::string& filePath, const

TEST(TesseractCommonUnit, YamlBasicIncludeTest) // NOLINT
{
std::string test_dir = tesseract_common::getTempPath() + "test_yaml_1";
std::string separator(1, tesseract_common::fs::path::preferred_separator);
std::string test_dir = tesseract_common::getTempPath() + "test_yaml_1" + separator;

// Create a temporary test directory
tesseract_common::fs::create_directory(test_dir);
Expand All @@ -2651,18 +2652,18 @@ TEST(TesseractCommonUnit, YamlBasicIncludeTest) // NOLINT
tesseract_common::GeneralResourceLocator locator;

// Create test files
createTestYamlWithIncludeDirectivesFile(test_dir + "/main.yaml", R"(
createTestYamlWithIncludeDirectivesFile(test_dir + "main.yaml", R"(
key1: value1
key2: !include ./included.yaml
key2: !include included.yaml
)");
createTestYamlWithIncludeDirectivesFile(test_dir + "/included.yaml", R"(
createTestYamlWithIncludeDirectivesFile(test_dir + "included.yaml", R"(
included_key1: included_value1
included_key2: included_value2
)");

// Load the main file
YAML::Node root = loadYamlFile(test_dir + "/main.yaml", locator);
tesseract_common::writeYamlToFile(root, test_dir + "/processed.yaml");
YAML::Node root = loadYamlFile(test_dir + "main.yaml", locator);
tesseract_common::writeYamlToFile(root, test_dir + "processed.yaml");

// Validate the structure
ASSERT_TRUE(root["key1"].IsScalar());
Expand All @@ -2678,7 +2679,8 @@ included_key2: included_value2

TEST(TesseractCommonUnit, YamlIncludeNestedIncludesTest) // NOLINT
{
std::string test_dir = tesseract_common::getTempPath() + "test_yaml_2";
std::string separator(1, tesseract_common::fs::path::preferred_separator);
std::string test_dir = tesseract_common::getTempPath() + "test_yaml_2" + separator;

// Create a temporary test directory
tesseract_common::fs::create_directory(test_dir);
Expand All @@ -2687,20 +2689,20 @@ TEST(TesseractCommonUnit, YamlIncludeNestedIncludesTest) // NOLINT
tesseract_common::GeneralResourceLocator locator;

// Create test files
createTestYamlWithIncludeDirectivesFile(test_dir + "/main.yaml", R"(
createTestYamlWithIncludeDirectivesFile(test_dir + "main.yaml", R"(
key1: value1
key2: !include ./included.yaml
key2: !include included.yaml
)");
createTestYamlWithIncludeDirectivesFile(test_dir + "/included.yaml", R"(
nested_key1: !include ./nested.yaml
createTestYamlWithIncludeDirectivesFile(test_dir + "included.yaml", R"(
nested_key1: !include nested.yaml
)");
createTestYamlWithIncludeDirectivesFile(test_dir + "/nested.yaml", R"(
createTestYamlWithIncludeDirectivesFile(test_dir + "nested.yaml", R"(
deep_key1: deep_value1
)");

// Load the main file
YAML::Node root = loadYamlFile(test_dir + "/main.yaml", locator);
tesseract_common::writeYamlToFile(root, test_dir + "/processed.yaml");
YAML::Node root = loadYamlFile(test_dir + "main.yaml", locator);
tesseract_common::writeYamlToFile(root, test_dir + "processed.yaml");

// Validate the structure
ASSERT_TRUE(root["key2"].IsMap());
Expand All @@ -2713,7 +2715,8 @@ deep_key1: deep_value1

TEST(TesseractCommonUnit, YamlIncludeSequenceIncludesTest) // NOLINT
{
std::string test_dir = tesseract_common::getTempPath() + "test_yaml_3";
std::string separator(1, tesseract_common::fs::path::preferred_separator);
std::string test_dir = tesseract_common::getTempPath() + "test_yaml_3" + separator;

// Create a temporary test directory
tesseract_common::fs::create_directory(test_dir);
Expand All @@ -2722,19 +2725,19 @@ TEST(TesseractCommonUnit, YamlIncludeSequenceIncludesTest) // NOLINT
tesseract_common::GeneralResourceLocator locator;

// Create test files
createTestYamlWithIncludeDirectivesFile(test_dir + "/main.yaml", R"(
createTestYamlWithIncludeDirectivesFile(test_dir + "main.yaml", R"(
key1:
- item1
- !include ./included.yaml
- !include included.yaml
)");
createTestYamlWithIncludeDirectivesFile(test_dir + "/included.yaml", R"(
createTestYamlWithIncludeDirectivesFile(test_dir + "included.yaml", R"(
- included_item1
- included_item2
)");

// Load the main file
YAML::Node root = loadYamlFile(test_dir + "/main.yaml", locator);
tesseract_common::writeYamlToFile(root, test_dir + "/processed.yaml");
YAML::Node root = loadYamlFile(test_dir + "main.yaml", locator);
tesseract_common::writeYamlToFile(root, test_dir + "processed.yaml");

// Validate the structure
ASSERT_TRUE(root["key1"].IsSequence());
Expand All @@ -2749,7 +2752,8 @@ TEST(TesseractCommonUnit, YamlIncludeSequenceIncludesTest) // NOLINT

TEST(TesseractCommonUnit, YamlIncludeSequenceIncludesMapTest) // NOLINT
{
std::string test_dir = tesseract_common::getTempPath() + "test_yaml_4";
std::string separator(1, tesseract_common::fs::path::preferred_separator);
std::string test_dir = tesseract_common::getTempPath() + "test_yaml_4" + separator;

// Create a temporary test directory
tesseract_common::fs::create_directory(test_dir);
Expand All @@ -2758,19 +2762,19 @@ TEST(TesseractCommonUnit, YamlIncludeSequenceIncludesMapTest) // NOLINT
tesseract_common::GeneralResourceLocator locator;

// Create test files
createTestYamlWithIncludeDirectivesFile(test_dir + "/main.yaml", R"(
createTestYamlWithIncludeDirectivesFile(test_dir + "main.yaml", R"(
key1:
- item1
- !include ./included.yaml
- !include included.yaml
)");
createTestYamlWithIncludeDirectivesFile(test_dir + "/included.yaml", R"(
createTestYamlWithIncludeDirectivesFile(test_dir + "included.yaml", R"(
keyA: valueA
keyB: valueB
)");

// Load the main file
YAML::Node root = loadYamlFile(test_dir + "/main.yaml", locator);
tesseract_common::writeYamlToFile(root, test_dir + "/processed.yaml");
YAML::Node root = loadYamlFile(test_dir + "main.yaml", locator);
tesseract_common::writeYamlToFile(root, test_dir + "processed.yaml");

// Validate the structure
ASSERT_TRUE(root["key1"].IsSequence());
Expand All @@ -2788,7 +2792,8 @@ keyB: valueB

TEST(TesseractCommonUnit, YamlIncludeMissingIncludeFileTest) // NOLINT
{
std::string test_dir = tesseract_common::getTempPath() + "test_yaml_5";
std::string separator(1, tesseract_common::fs::path::preferred_separator);
std::string test_dir = tesseract_common::getTempPath() + "test_yaml_5" + separator;

// Create a temporary test directory
tesseract_common::fs::create_directory(test_dir);
Expand All @@ -2797,20 +2802,21 @@ TEST(TesseractCommonUnit, YamlIncludeMissingIncludeFileTest) // NOLINT
tesseract_common::GeneralResourceLocator locator;

// Create a test file
createTestYamlWithIncludeDirectivesFile(test_dir + "/main.yaml", R"(
key1: !include ./missing.yaml
createTestYamlWithIncludeDirectivesFile(test_dir + "main.yaml", R"(
key1: !include missing.yaml
)");

// Attempt to load the main file and expect an exception
EXPECT_THROW(loadYamlFile(test_dir + "/main.yaml", locator), std::runtime_error); // NOLINT
EXPECT_THROW(loadYamlFile(test_dir + "main.yaml", locator), std::runtime_error); // NOLINT

// Clean up the test directory
tesseract_common::fs::remove_all(test_dir);
}

TEST(TesseractCommonUnit, YamlIncludeInvalidIncludeTagTest) // NOLINT
{
std::string test_dir = tesseract_common::getTempPath() + "test_yaml_6";
std::string separator(1, tesseract_common::fs::path::preferred_separator);
std::string test_dir = tesseract_common::getTempPath() + "test_yaml_6" + separator;

// Create a temporary test directory
tesseract_common::fs::create_directory(test_dir);
Expand All @@ -2819,12 +2825,12 @@ TEST(TesseractCommonUnit, YamlIncludeInvalidIncludeTagTest) // NOLINT
tesseract_common::GeneralResourceLocator locator;

// Create a test file with an invalid !include tag
createTestYamlWithIncludeDirectivesFile(test_dir + "/main.yaml", R"(
createTestYamlWithIncludeDirectivesFile(test_dir + "main.yaml", R"(
key1: !include
)");

// Attempt to load the main file and expect an exception
EXPECT_THROW(loadYamlFile(test_dir + "/main.yaml", locator), std::runtime_error); // NOLINT
EXPECT_THROW(loadYamlFile(test_dir + "main.yaml", locator), std::runtime_error); // NOLINT

// Clean up the test directory
tesseract_common::fs::remove_all(test_dir);
Expand Down

0 comments on commit ad309d7

Please sign in to comment.