From 8299793f5e7879951467007782cbc188a30a3544 Mon Sep 17 00:00:00 2001 From: Levi Armstrong Date: Wed, 1 Jan 2025 09:50:43 -0600 Subject: [PATCH] Leverage fs::path::preferred_separator in locateResource --- tesseract_common/src/resource_locator.cpp | 35 +++++++-- tesseract_common/src/yaml_utils.cpp | 1 + .../test/resource_locator_unit.cpp | 16 ++++- .../test/tesseract_common_unit.cpp | 72 ++++++++++--------- 4 files changed, 83 insertions(+), 41 deletions(-) diff --git a/tesseract_common/src/resource_locator.cpp b/tesseract_common/src/resource_locator.cpp index 971ffe85476..2a6f02fca66 100644 --- a/tesseract_common/src/resource_locator.cpp +++ b/tesseract_common/src/resource_locator.cpp @@ -121,20 +121,33 @@ void GeneralResourceLocator::processToken(const std::string& token) } } +std::size_t findSeparator(const std::string& str) +{ + const size_t pos_slash = str.find('/'); + if (pos_slash != std::string::npos) + return pos_slash; + + const size_t pos_backslash = str.find('\\'); + if (pos_backslash != std::string::npos) + return pos_backslash; + + return std::string::npos; +} + std::shared_ptr 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; @@ -230,12 +243,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); } diff --git a/tesseract_common/src/yaml_utils.cpp b/tesseract_common/src/yaml_utils.cpp index 8ab6ea9f02f..6ead6da7092 100644 --- a/tesseract_common/src/yaml_utils.cpp +++ b/tesseract_common/src/yaml_utils.cpp @@ -27,6 +27,7 @@ #include TESSERACT_COMMON_IGNORE_WARNINGS_PUSH #include +#include TESSERACT_COMMON_IGNORE_WARNINGS_POP #include diff --git a/tesseract_common/test/resource_locator_unit.cpp b/tesseract_common/test/resource_locator_unit.cpp index 9f2d3c7c2e1..b888fc4eccc 100644 --- a/tesseract_common/test/resource_locator_unit.cpp +++ b/tesseract_common/test/resource_locator_unit.cpp @@ -9,6 +9,19 @@ TESSERACT_COMMON_IGNORE_WARNINGS_POP #include #include +std::size_t findSeparator(const std::string& str) +{ + const size_t pos_slash = str.find('/'); + if (pos_slash != std::string::npos) + return pos_slash; + + const size_t pos_backslash = str.find('\\'); + 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 { @@ -24,11 +37,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__); diff --git a/tesseract_common/test/tesseract_common_unit.cpp b/tesseract_common/test/tesseract_common_unit.cpp index fb3e7090616..55858756a4d 100644 --- a/tesseract_common/test/tesseract_common_unit.cpp +++ b/tesseract_common/test/tesseract_common_unit.cpp @@ -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); @@ -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()); @@ -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); @@ -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()); @@ -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); @@ -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()); @@ -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); @@ -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()); @@ -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); @@ -2797,12 +2802,12 @@ 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); @@ -2810,7 +2815,8 @@ key1: !include ./missing.yaml 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); @@ -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);