From fbb3830d02d79a18daad962f4aaf5456b44714f9 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 1 Dec 2025 14:42:58 +0000 Subject: [PATCH 001/124] Add example1.ini file (for tests) --- tests/testData/ExampleIniFiles/example1.ini | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/testData/ExampleIniFiles/example1.ini diff --git a/tests/testData/ExampleIniFiles/example1.ini b/tests/testData/ExampleIniFiles/example1.ini new file mode 100644 index 0000000..d6015be --- /dev/null +++ b/tests/testData/ExampleIniFiles/example1.ini @@ -0,0 +1,20 @@ +# First line of comment (Unix style) +; Second line of comment (regular ini file comment style) + +[DEFAULT] +version = 1.2 +prefixmap = res:../tests/testData/Indicies;../tests/testData/resourcesOnBranch res2:../tests/testData/ResourceGroups + +#============================================================================= +# example1.ini - test file +# - This file is intended to test generic ini file parsing. +# - Ini file parser of this file should handle both regular ini comments (;) and Unix style (#) comments. +; - It should handle multi-line values too (as in the respaths values below). +;============================================================================= + +[testYamlFilesOverMultiLineResPaths] +filter = [ .yaml ] +respaths = res:/... + res2:/... +resfile = res:/binaryFileIndex_v0_0_0.txt + From 31479c22e8f854343d947526ec34a7b79dc10096 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 1 Dec 2025 17:10:51 +0000 Subject: [PATCH 002/124] Add vcpkg inih to project --- CMakeLists.txt | 3 ++- vcpkg.json | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dbe3af7..8633899 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ include(cmake/CcpTargetConfigurations.cmake) include(cmake/CcpDocsGenerator.cmake) find_package(yaml-cpp CONFIG REQUIRED) +find_package(unofficial-inih CONFIG REQUIRED) # Add subdirectory for resource tools static library add_subdirectory(tools) @@ -65,7 +66,7 @@ endif () target_compile_definitions(resources PUBLIC CARBON_RESOURCES_STATIC) -target_link_libraries(resources PRIVATE $ yaml-cpp::yaml-cpp) +target_link_libraries(resources PRIVATE $ yaml-cpp::yaml-cpp unofficial::inih::inireader) target_include_directories(resources PUBLIC diff --git a/vcpkg.json b/vcpkg.json index fee8d3f..ac300ed 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -16,6 +16,10 @@ "name": "curl", "version>=": "8.11.1#1" }, + { + "name": "inih", + "version>=": "62" + }, { "name": "yaml-cpp", "version>=": "0.8.0#1" From 9ce31faf0c0865c7866db053890f48a54f9e287c Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 2 Dec 2025 13:18:21 +0000 Subject: [PATCH 003/124] Add first ResourceFilterTest --- tests/CMakeLists.txt | 2 ++ tests/src/ResourceFilterTest.cpp | 33 +++++++++++++++++++++ tests/src/ResourceFilterTest.h | 9 ++++++ tests/testData/ExampleIniFiles/example1.ini | 2 +- 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/src/ResourceFilterTest.cpp create mode 100644 tests/src/ResourceFilterTest.h diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 00e15d1..c0f8453 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,6 +14,8 @@ set(SRC_FILES src/ResourcesLibraryTest.cpp src/ResourcesCliTest.cpp src/ResourceToolsLibraryTest.cpp + src/ResourceFilterTest.cpp + src/ResourceFilterTest.h ) add_executable(resources-test ${SRC_FILES}) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp new file mode 100644 index 0000000..8acc297 --- /dev/null +++ b/tests/src/ResourceFilterTest.cpp @@ -0,0 +1,33 @@ +#include "ResourceFilterTest.h" +#include + +TEST_F(ResourceFilterTestFixture, Example1IniParsing_v1) +{ + // Path to the example ini file + const std::string iniPath = "../../../tests/testData/ExampleIniFiles/example1.ini"; + INIReader reader(iniPath); + ASSERT_EQ(reader.ParseError(), 0) << "Failed to parse example1.ini"; + + // Check [default] section + ASSERT_TRUE(reader.HasSection("default")); + EXPECT_EQ(reader.Get("default", "prefixmap", ""), "res:../../../tests/testData/Indicies;../../../tests/testData/resourcesOnBranch res2:../../../tests/testData/ResourceGroups"); + EXPECT_EQ(reader.Get("default", "version", ""), "1.2"); + + // Check [testyamlfilesovermultilinerespaths] section + ASSERT_TRUE(reader.HasSection("testyamlfilesovermultilinerespaths")); + EXPECT_EQ(reader.Get("testyamlfilesovermultilinerespaths", "filter", ""), "[ .yaml ]"); + EXPECT_EQ(reader.Get("testyamlfilesovermultilinerespaths", "respaths", ""), "res:/...\nres2:/..."); + EXPECT_EQ(reader.Get("testyamlfilesovermultilinerespaths", "resfile", ""), "res:/binaryFileIndex_v0_0_0.txt"); +} + +TEST_F(ResourceFilterTestFixture, Example1IniParsing_v2) +{ + // Next step. + // Do the same test as above, but now: + // - Change the class to inherit from ResourcesTestFixture and make use of the helper functions there for file paths + // - Get a list of all the sections and check if they match with the example1.ini file. Check the count. Do lowercase comparison on section names. + // - For each section, get the list of keys and check if they match. Check the count. Do lowercase comparison on key names. + // - For each key, check the value matches expected. + // - Where there are multiple lines or list of items, add them to a sorted vector and then compare. + +} diff --git a/tests/src/ResourceFilterTest.h b/tests/src/ResourceFilterTest.h new file mode 100644 index 0000000..805aaa8 --- /dev/null +++ b/tests/src/ResourceFilterTest.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +class ResourceFilterTestFixture : public ::testing::Test { +protected: + void SetUp() override {} + void TearDown() override {} +}; diff --git a/tests/testData/ExampleIniFiles/example1.ini b/tests/testData/ExampleIniFiles/example1.ini index d6015be..6da8ae9 100644 --- a/tests/testData/ExampleIniFiles/example1.ini +++ b/tests/testData/ExampleIniFiles/example1.ini @@ -3,7 +3,7 @@ [DEFAULT] version = 1.2 -prefixmap = res:../tests/testData/Indicies;../tests/testData/resourcesOnBranch res2:../tests/testData/ResourceGroups +prefixmap = res:../../../tests/testData/Indicies;../../../tests/testData/resourcesOnBranch res2:../../../tests/testData/ResourceGroups #============================================================================= # example1.ini - test file From 4a0a0d82843182ee45ee3569fa3e49ef55fa2b01 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 2 Dec 2025 16:15:52 +0000 Subject: [PATCH 004/124] Update ExampleIniParsing test Make use of existing filePath test functions --- tests/src/ResourceFilterTest.cpp | 41 +++++++++------------ tests/src/ResourceFilterTest.h | 9 ++--- tests/testData/ExampleIniFiles/example1.ini | 2 +- 3 files changed, 22 insertions(+), 30 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 8acc297..2fb23d5 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -1,33 +1,26 @@ #include "ResourceFilterTest.h" #include -TEST_F(ResourceFilterTestFixture, Example1IniParsing_v1) +TEST_F( ResourceFilterTestFixture, Example1IniParsing ) { - // Path to the example ini file - const std::string iniPath = "../../../tests/testData/ExampleIniFiles/example1.ini"; - INIReader reader(iniPath); - ASSERT_EQ(reader.ParseError(), 0) << "Failed to parse example1.ini"; + // Use the test fixture's helper to get the absolute path + const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/example1.ini" ); + INIReader reader( iniPath.string() ); + ASSERT_EQ( reader.ParseError(), 0 ) << "Failed to parse example1.ini"; + + // There should only be 2 sections + EXPECT_EQ( reader.Sections().size(), 2 ); // Check [default] section - ASSERT_TRUE(reader.HasSection("default")); - EXPECT_EQ(reader.Get("default", "prefixmap", ""), "res:../../../tests/testData/Indicies;../../../tests/testData/resourcesOnBranch res2:../../../tests/testData/ResourceGroups"); - EXPECT_EQ(reader.Get("default", "version", ""), "1.2"); + ASSERT_TRUE( reader.HasSection( "default" ) ); + EXPECT_EQ( reader.Get( "default", "prefixmap", "" ), "res:./Indicies;./resourcesOnBranch res2:./ResourceGroups" ); + EXPECT_EQ( reader.Get( "default", "version", "" ), "1.2" ); + EXPECT_EQ( reader.Keys( "default" ).size(), 2 ); // Check [testyamlfilesovermultilinerespaths] section - ASSERT_TRUE(reader.HasSection("testyamlfilesovermultilinerespaths")); - EXPECT_EQ(reader.Get("testyamlfilesovermultilinerespaths", "filter", ""), "[ .yaml ]"); - EXPECT_EQ(reader.Get("testyamlfilesovermultilinerespaths", "respaths", ""), "res:/...\nres2:/..."); - EXPECT_EQ(reader.Get("testyamlfilesovermultilinerespaths", "resfile", ""), "res:/binaryFileIndex_v0_0_0.txt"); -} - -TEST_F(ResourceFilterTestFixture, Example1IniParsing_v2) -{ - // Next step. - // Do the same test as above, but now: - // - Change the class to inherit from ResourcesTestFixture and make use of the helper functions there for file paths - // - Get a list of all the sections and check if they match with the example1.ini file. Check the count. Do lowercase comparison on section names. - // - For each section, get the list of keys and check if they match. Check the count. Do lowercase comparison on key names. - // - For each key, check the value matches expected. - // - Where there are multiple lines or list of items, add them to a sorted vector and then compare. - + ASSERT_TRUE( reader.HasSection( "testyamlfilesovermultilinerespaths" ) ); + EXPECT_EQ( reader.Get( "testyamlfilesovermultilinerespaths", "filter", "" ), "[ .yaml ]" ); + EXPECT_EQ( reader.Get( "testyamlfilesovermultilinerespaths", "resfile", "" ), "res:/binaryFileIndex_v0_0_0.txt" ); + EXPECT_EQ( reader.Get( "testyamlfilesovermultilinerespaths", "respaths", "" ), "res:/...\nres2:/..." ); + EXPECT_EQ( reader.Keys( "testyamlfilesovermultilinerespaths" ).size(), 3 ); } diff --git a/tests/src/ResourceFilterTest.h b/tests/src/ResourceFilterTest.h index 805aaa8..54f8f8e 100644 --- a/tests/src/ResourceFilterTest.h +++ b/tests/src/ResourceFilterTest.h @@ -1,9 +1,8 @@ #pragma once -#include +#include "ResourcesTestFixture.h" -class ResourceFilterTestFixture : public ::testing::Test { -protected: - void SetUp() override {} - void TearDown() override {} +// Inherit from ResourcesTestFixture to gain access to file and directory helper functions +class ResourceFilterTestFixture : public ResourcesTestFixture +{ }; diff --git a/tests/testData/ExampleIniFiles/example1.ini b/tests/testData/ExampleIniFiles/example1.ini index 6da8ae9..b58892d 100644 --- a/tests/testData/ExampleIniFiles/example1.ini +++ b/tests/testData/ExampleIniFiles/example1.ini @@ -3,7 +3,7 @@ [DEFAULT] version = 1.2 -prefixmap = res:../../../tests/testData/Indicies;../../../tests/testData/resourcesOnBranch res2:../../../tests/testData/ResourceGroups +prefixmap = res:./Indicies;./resourcesOnBranch res2:./ResourceGroups #============================================================================= # example1.ini - test file From dbbfb8355f231681e5241d63b707c91d5924b119 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 3 Dec 2025 09:34:20 +0000 Subject: [PATCH 005/124] Rename the ResourceFilterTest class --- tests/src/ResourceFilterTest.cpp | 4 +++- tests/src/ResourceFilterTest.h | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 2fb23d5..f4d612b 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -1,7 +1,9 @@ +// Copyright © 2025 CCP ehf. + #include "ResourceFilterTest.h" #include -TEST_F( ResourceFilterTestFixture, Example1IniParsing ) +TEST_F( ResourceFilterTest, Example1IniParsing ) { // Use the test fixture's helper to get the absolute path const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/example1.ini" ); diff --git a/tests/src/ResourceFilterTest.h b/tests/src/ResourceFilterTest.h index 54f8f8e..12a635c 100644 --- a/tests/src/ResourceFilterTest.h +++ b/tests/src/ResourceFilterTest.h @@ -1,8 +1,14 @@ +// Copyright © 2025 CCP ehf. + #pragma once +#ifndef ResourceFilterTest_H +#define ResourceFilterTest_H #include "ResourcesTestFixture.h" // Inherit from ResourcesTestFixture to gain access to file and directory helper functions -class ResourceFilterTestFixture : public ResourcesTestFixture +class ResourceFilterTest : public ResourcesTestFixture { }; + +#endif // ResourceFilterTest_H \ No newline at end of file From fd26db0c7d213bdea99427bd18252b1f9ca3a5b2 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 3 Dec 2025 17:43:07 +0000 Subject: [PATCH 006/124] Add scaffolding for the Filter classes --- CMakeLists.txt | 13 +++++++++++++ src/FilterDefaultSection.cpp | 20 ++++++++++++++++++++ src/FilterDefaultSection.h | 24 ++++++++++++++++++++++++ src/FilterNamedSection.cpp | 17 +++++++++++++++++ src/FilterNamedSection.h | 28 ++++++++++++++++++++++++++++ src/FilterPrefixmap.cpp | 24 ++++++++++++++++++++++++ src/FilterPrefixmap.h | 28 ++++++++++++++++++++++++++++ src/FilterResourceFile.cpp | 17 +++++++++++++++++ src/FilterResourceFile.h | 25 +++++++++++++++++++++++++ src/FilterResourceFilter.cpp | 34 ++++++++++++++++++++++++++++++++++ src/FilterResourceFilter.h | 33 +++++++++++++++++++++++++++++++++ src/FilterResourceLine.cpp | 22 ++++++++++++++++++++++ src/FilterResourceLine.h | 28 ++++++++++++++++++++++++++++ 13 files changed, 313 insertions(+) create mode 100644 src/FilterDefaultSection.cpp create mode 100644 src/FilterDefaultSection.h create mode 100644 src/FilterNamedSection.cpp create mode 100644 src/FilterNamedSection.h create mode 100644 src/FilterPrefixmap.cpp create mode 100644 src/FilterPrefixmap.h create mode 100644 src/FilterResourceFile.cpp create mode 100644 src/FilterResourceFile.h create mode 100644 src/FilterResourceFilter.cpp create mode 100644 src/FilterResourceFilter.h create mode 100644 src/FilterResourceLine.cpp create mode 100644 src/FilterResourceLine.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8633899..cbfe00f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,19 @@ set(SRC_FILES src/ResourceInfo/BundleResourceGroupInfo.cpp src/ParameterVersion.h src/ParameterVersion.cpp + + src/FilterResourceFile.h + src/FilterResourceFile.cpp + src/FilterDefaultSection.h + src/FilterDefaultSection.cpp + src/FilterNamedSection.h + src/FilterNamedSection.cpp + src/FilterPrefixmap.h + src/FilterPrefixmap.cpp + src/FilterResourceFilter.h + src/FilterResourceFilter.cpp + src/FilterResourceLine.h + src/FilterResourceLine.cpp ) add_library(resources STATIC ${SRC_FILES}) diff --git a/src/FilterDefaultSection.cpp b/src/FilterDefaultSection.cpp new file mode 100644 index 0000000..088f7a5 --- /dev/null +++ b/src/FilterDefaultSection.cpp @@ -0,0 +1,20 @@ +// Copyright © 2025 CCP ehf. + +#include +#include "FilterDefaultSection.h" + +namespace CarbonResources +{ + +FilterDefaultSection::FilterDefaultSection( const std::string& prefixmapStr ) : + m_prefixmap( prefixmapStr ) +{ + throw std::logic_error( "Not implemented yet exception" ); +} + +const FilterPrefixmap& FilterDefaultSection::GetPrefixmap() const +{ + throw std::logic_error( "Not implemented yet exception" ); +} + +} diff --git a/src/FilterDefaultSection.h b/src/FilterDefaultSection.h new file mode 100644 index 0000000..8c79da8 --- /dev/null +++ b/src/FilterDefaultSection.h @@ -0,0 +1,24 @@ +// Copyright © 2025 CCP ehf. + +#ifndef FILTERDEFAULTSECTION_H +#define FILTERDEFAULTSECTION_H + +#include "FilterPrefixmap.h" + +namespace CarbonResources +{ + +class FilterDefaultSection +{ +public: + explicit FilterDefaultSection( const std::string& prefixmapStr ); + + const FilterPrefixmap& GetPrefixmap() const; + +private: + FilterPrefixmap m_prefixmap; +}; + +} + +#endif // FILTERDEFAULTSECTION_H diff --git a/src/FilterNamedSection.cpp b/src/FilterNamedSection.cpp new file mode 100644 index 0000000..90d1a44 --- /dev/null +++ b/src/FilterNamedSection.cpp @@ -0,0 +1,17 @@ +// Copyright © 2025 CCP ehf. + +#include +#include "FilterNamedSection.h" + +namespace CarbonResources +{ + +FilterNamedSection::FilterNamedSection( const std::string& filter, const std::string& resfile, const std::vector& respaths ) : + m_filter( filter ), + m_resfile( resfile ), + m_respaths( respaths ) +{ + throw std::logic_error( "Not implemented yet exception" ); +} + +} diff --git a/src/FilterNamedSection.h b/src/FilterNamedSection.h new file mode 100644 index 0000000..e9cace5 --- /dev/null +++ b/src/FilterNamedSection.h @@ -0,0 +1,28 @@ +// Copyright © 2025 CCP ehf. + +#ifndef FILTERNAMEDSECTION_H +#define FILTERNAMEDSECTION_H + +#include +#include +#include "FilterResourceFilter.h" +#include "FilterResourceLine.h" + +namespace CarbonResources +{ + +class FilterNamedSection +{ +public: + explicit FilterNamedSection( const std::string& filter, const std::string& resfile, const std::vector& respaths ); + +private: + FilterResourceFilter m_filter; + std::string m_resfile; + std::vector m_respaths; + // Optionally, store parsed FilterResourceLine objects +}; + +} + +#endif // FILTERNAMEDSECTION_H diff --git a/src/FilterPrefixmap.cpp b/src/FilterPrefixmap.cpp new file mode 100644 index 0000000..98d5313 --- /dev/null +++ b/src/FilterPrefixmap.cpp @@ -0,0 +1,24 @@ +// Copyright © 2025 CCP ehf. + +#include +#include "FilterPrefixmap.h" + +namespace CarbonResources +{ + +FilterPrefixmap::FilterPrefixmap( const std::string& prefixmapStr ) +{ + throw std::logic_error( "Not implemented yet exception" ); +} + +const std::map>& FilterPrefixmap::GetMap() const +{ + throw std::logic_error( "Not implemented yet exception" ); +} + +void FilterPrefixmap::Parse( const std::string& prefixmapStr ) +{ + throw std::logic_error( "Not implemented yet exception" ); +} + +} diff --git a/src/FilterPrefixmap.h b/src/FilterPrefixmap.h new file mode 100644 index 0000000..92acf35 --- /dev/null +++ b/src/FilterPrefixmap.h @@ -0,0 +1,28 @@ +// Copyright © 2025 CCP ehf. + +#ifndef FILTERPREFIXMAP_H +#define FILTERPREFIXMAP_H + +#include +#include +#include + +namespace CarbonResources +{ + +class FilterPrefixmap +{ +public: + explicit FilterPrefixmap( const std::string& prefixmapStr ); + + const std::map>& GetMap() const; + +private: + std::map> m_prefixMap; + + void Parse( const std::string& prefixmapStr ); +}; + +} + +#endif // FILTERPREFIXMAP_H diff --git a/src/FilterResourceFile.cpp b/src/FilterResourceFile.cpp new file mode 100644 index 0000000..3a8424c --- /dev/null +++ b/src/FilterResourceFile.cpp @@ -0,0 +1,17 @@ +// Copyright © 2025 CCP ehf. + +#include + +#include "FilterResourceFile.h" + +namespace CarbonResources +{ + +FilterResourceFile::FilterResourceFile( const FilterDefaultSection& defaultSection, const std::vector& namedSections ) : + m_defaultSection( defaultSection ), + m_namedSections( namedSections ) +{ + throw std::logic_error( "Not implemented yet exception" ); +} + +} diff --git a/src/FilterResourceFile.h b/src/FilterResourceFile.h new file mode 100644 index 0000000..147ee12 --- /dev/null +++ b/src/FilterResourceFile.h @@ -0,0 +1,25 @@ +// Copyright © 2025 CCP ehf. + +#ifndef FILTERRESOURCEFILE_H +#define FILTERRESOURCEFILE_H + +#include +#include "FilterDefaultSection.h" +#include "FilterNamedSection.h" + +namespace CarbonResources +{ + +class FilterResourceFile +{ +public: + explicit FilterResourceFile( const FilterDefaultSection& defaultSection, const std::vector& namedSections ); + +private: + FilterDefaultSection m_defaultSection; + std::vector m_namedSections; +}; + +} + +#endif // FILTERRESOURCEFILE_H diff --git a/src/FilterResourceFilter.cpp b/src/FilterResourceFilter.cpp new file mode 100644 index 0000000..4170de0 --- /dev/null +++ b/src/FilterResourceFilter.cpp @@ -0,0 +1,34 @@ +// Copyright © 2025 CCP ehf. + +#include +#include "FilterResourceFilter.h" + +namespace CarbonResources +{ + +FilterResourceFilter::FilterResourceFilter( const std::string& rawFilter ) +{ + throw std::logic_error( "Not implemented yet exception" ); +} + +const std::string& FilterResourceFilter::GetRawFilter() const +{ + throw std::logic_error( "Not implemented yet exception" ); +} + +const std::vector& FilterResourceFilter::GetIncludeFilter() const +{ + throw std::logic_error( "Not implemented yet exception" ); +} + +const std::vector& FilterResourceFilter::GetExcludeFilter() const +{ + throw std::logic_error( "Not implemented yet exception" ); +} + +void FilterResourceFilter::ParseFilters() +{ + throw std::logic_error( "Not implemented yet exception" ); +} + +} diff --git a/src/FilterResourceFilter.h b/src/FilterResourceFilter.h new file mode 100644 index 0000000..0920588 --- /dev/null +++ b/src/FilterResourceFilter.h @@ -0,0 +1,33 @@ +// Copyright © 2025 CCP ehf. + +#ifndef FILTERRESOURCEFILTER_H +#define FILTERRESOURCEFILTER_H + +#include +#include + +namespace CarbonResources +{ + +class FilterResourceFilter +{ +public: + explicit FilterResourceFilter( const std::string& rawFilter ); + + const std::string& GetRawFilter() const; + + const std::vector& GetIncludeFilter() const; + + const std::vector& GetExcludeFilter() const; + +private: + std::string m_rawFilter; + std::vector m_includeFilter; + std::vector m_excludeFilter; + + void ParseFilters(); +}; + +} + +#endif // FILTERRESOURCEFILTER_H diff --git a/src/FilterResourceLine.cpp b/src/FilterResourceLine.cpp new file mode 100644 index 0000000..1c50aee --- /dev/null +++ b/src/FilterResourceLine.cpp @@ -0,0 +1,22 @@ +// Copyright © 2025 CCP ehf. + +#include +#include "FilterResourceLine.h" + +namespace CarbonResources +{ + +FilterResourceLine::FilterResourceLine( const std::string& line, const FilterPrefixmap& prefixMap, const FilterResourceFilter& sectionFilter ) : + m_line( line ), + m_prefixMap( prefixMap ), + m_resFilter( sectionFilter ) +{ + throw std::logic_error( "Not implemented yet exception" ); +} + +bool FilterResourceLine::IsValid() const +{ + throw std::logic_error( "Not implemented yet exception" ); +} + +} diff --git a/src/FilterResourceLine.h b/src/FilterResourceLine.h new file mode 100644 index 0000000..a7b33c1 --- /dev/null +++ b/src/FilterResourceLine.h @@ -0,0 +1,28 @@ +// Copyright © 2025 CCP ehf. + +#ifndef FILTERRESOURCELINE_H +#define FILTERRESOURCELINE_H + +#include +#include "FilterPrefixmap.h" +#include "FilterResourceFilter.h" + +namespace CarbonResources +{ + +class FilterResourceLine +{ +public: + explicit FilterResourceLine( const std::string& line, const FilterPrefixmap& prefixMap, const FilterResourceFilter& sectionFilter ); + + bool IsValid() const; + +private: + std::string m_line; + const FilterPrefixmap& m_prefixMap; + FilterResourceFilter m_resFilter; +}; + +} + +#endif // FILTERRESOURCELINE_H From 40ec6024f606ce49d9f7f828fb202d296605644e Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 5 Dec 2025 14:12:38 +0000 Subject: [PATCH 007/124] Include exclude filter implementation without regex --- src/FilterResourceFilter.cpp | 81 +++++++++++++++++++++++++++++--- tests/src/ResourceFilterTest.cpp | 67 ++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 6 deletions(-) diff --git a/src/FilterResourceFilter.cpp b/src/FilterResourceFilter.cpp index 4170de0..d888439 100644 --- a/src/FilterResourceFilter.cpp +++ b/src/FilterResourceFilter.cpp @@ -1,34 +1,103 @@ // Copyright © 2025 CCP ehf. -#include +//#include +//#include +#include +#include #include "FilterResourceFilter.h" namespace CarbonResources { FilterResourceFilter::FilterResourceFilter( const std::string& rawFilter ) + : m_rawFilter( rawFilter ) { - throw std::logic_error( "Not implemented yet exception" ); + ParseFilters(); } const std::string& FilterResourceFilter::GetRawFilter() const { - throw std::logic_error( "Not implemented yet exception" ); + return m_rawFilter; } const std::vector& FilterResourceFilter::GetIncludeFilter() const { - throw std::logic_error( "Not implemented yet exception" ); + return m_includeFilter; } const std::vector& FilterResourceFilter::GetExcludeFilter() const { - throw std::logic_error( "Not implemented yet exception" ); + return m_excludeFilter; } void FilterResourceFilter::ParseFilters() { - throw std::logic_error( "Not implemented yet exception" ); + m_includeFilter.clear(); + m_excludeFilter.clear(); + + std::string s = m_rawFilter; + size_t pos = 0; + while( pos < s.size() ) + { + // Skip whitespace + while( pos < s.size() && std::isspace(static_cast(s[pos])) ) + ++pos; + if( pos >= s.size() ) + break; + + bool isExclude = false; + if( s[pos] == '!' ) + { + isExclude = true; + ++pos; + } + + if( pos >= s.size() || s[pos] != '[' ) + throw std::invalid_argument( "Invalid filter format: missing '['" ); + + ++pos; // skip '[' + size_t endBracket = s.find( ']', pos ); + if( endBracket == std::string::npos ) + throw std::invalid_argument( "Invalid filter format: missing ']'" ); + + std::string entries = s.substr( pos, endBracket - pos ); + std::istringstream iss( entries ); + std::string token; + while( iss >> token ) + { + // Trim whitespace from token + size_t start = token.find_first_not_of( " \t\r\n" ); + size_t end = token.find_last_not_of( " \t\r\n" ); + if( start == std::string::npos || end == std::string::npos ) + continue; + token = token.substr( start, end - start + 1 ); + + if( token.empty() ) + continue; + + if( isExclude ) + { + // Remove from include if present + auto it = std::find( m_includeFilter.begin(), m_includeFilter.end(), token ); + if( it != m_includeFilter.end() ) + m_includeFilter.erase( it ); + // Add to exclude if not present + if( std::find( m_excludeFilter.begin(), m_excludeFilter.end(), token ) == m_excludeFilter.end() ) + m_excludeFilter.push_back( token ); + } + else + { + // Remove from exclude if present + auto it = std::find( m_excludeFilter.begin(), m_excludeFilter.end(), token ); + if( it != m_excludeFilter.end() ) + m_excludeFilter.erase( it ); + // Add to include if not present + if( std::find( m_includeFilter.begin(), m_includeFilter.end(), token ) == m_includeFilter.end() ) + m_includeFilter.push_back( token ); + } + } + pos = endBracket + 1; + } } } diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index f4d612b..7fd4e19 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -2,6 +2,7 @@ #include "ResourceFilterTest.h" #include +#include "../../src/FilterResourceFilter.h" TEST_F( ResourceFilterTest, Example1IniParsing ) { @@ -26,3 +27,69 @@ TEST_F( ResourceFilterTest, Example1IniParsing ) EXPECT_EQ( reader.Get( "testyamlfilesovermultilinerespaths", "respaths", "" ), "res:/...\nres2:/..." ); EXPECT_EQ( reader.Keys( "testyamlfilesovermultilinerespaths" ).size(), 3 ); } + +TEST_F( ResourceFilterTest, OnlyIncludeFilter ) +{ + CarbonResources::FilterResourceFilter filter("[ .this .is .included ]"); + const auto& includes = filter.GetIncludeFilter(); + const auto& excludes = filter.GetExcludeFilter(); + EXPECT_EQ(includes.size(), 3); + EXPECT_EQ(includes[0], ".this"); + EXPECT_EQ(includes[1], ".is"); + EXPECT_EQ(includes[2], ".included"); + EXPECT_TRUE(excludes.empty()); +} + +TEST_F( ResourceFilterTest, OnlyExcludeFilter ) +{ + CarbonResources::FilterResourceFilter filter("![ .excluded .extension ]"); + const auto& includes = filter.GetIncludeFilter(); + const auto& excludes = filter.GetExcludeFilter(); + EXPECT_TRUE(includes.empty()); + EXPECT_EQ(excludes.size(), 2); + EXPECT_EQ(excludes[0], ".excluded"); + EXPECT_EQ(excludes[1], ".extension"); +} + +TEST_F( ResourceFilterTest, ComplexIncludeExcludeFilter ) +{ + CarbonResources::FilterResourceFilter filter("[ .red .gr2 .dds .png .yaml ] [ .txt ] ![ .csv .xls ] [ .bat .sh ] ![ .blk .yel ]"); + const auto& includes = filter.GetIncludeFilter(); + const auto& excludes = filter.GetExcludeFilter(); + std::vector expectedIncludes = { ".red", ".gr2", ".dds", ".png", ".yaml", ".txt", ".bat", ".sh" }; + std::vector expectedExcludes = { ".csv", ".xls", ".blk", ".yel" }; + EXPECT_EQ(includes, expectedIncludes); + EXPECT_EQ(excludes, expectedExcludes); +} + +TEST_F( ResourceFilterTest, SimpleIncludeFilter ) +{ + CarbonResources::FilterResourceFilter filter("[ .red ]"); + const auto& includes = filter.GetIncludeFilter(); + EXPECT_EQ(includes.size(), 1); + EXPECT_EQ(includes[0], ".red"); +} + +TEST_F( ResourceFilterTest, SimpleExcludeFilter ) +{ + CarbonResources::FilterResourceFilter filter("![ .blk ]"); + const auto& excludes = filter.GetExcludeFilter(); + EXPECT_EQ(excludes.size(), 1); + EXPECT_EQ(excludes[0], ".blk"); +} + +TEST_F( ResourceFilterTest, IncludeExcludeInclude) +{ + // Include .in1 and .in2 + // Exclude .in2, .ex1, and .ex2 (removes .in2 from include) + // Include .ex1, .in3 and .in1 (removes .ex1 from exclude, adds .in3, keeps .in1) + // Resulting include: .in1, .ex1, .in3 + // Resulting exclude: .in2, .ex2 + CarbonResources::FilterResourceFilter filter("[ .in1 .in2 ] ![ .in2 .ex1 .ex2 ] [ .ex1 .in3 .in1 ]"); + const auto& includes = filter.GetIncludeFilter(); + const auto& excludes = filter.GetExcludeFilter(); + std::vector expectedIncludes = { ".in1", ".ex1", ".in3" }; + std::vector expectedExcludes = { ".in2", ".ex2" }; + EXPECT_EQ(includes, expectedIncludes); + EXPECT_EQ(excludes, expectedExcludes); +} \ No newline at end of file From ede963674511d386a85454fb5f0313c9b60af67f Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 5 Dec 2025 14:53:47 +0000 Subject: [PATCH 008/124] Simplify FilterResourceFilter::ParseFilters() --- src/FilterResourceFilter.cpp | 41 ++++++++++++++++-------------------- src/FilterResourceFilter.h | 8 +++++++ 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/FilterResourceFilter.cpp b/src/FilterResourceFilter.cpp index d888439..a38a6b1 100644 --- a/src/FilterResourceFilter.cpp +++ b/src/FilterResourceFilter.cpp @@ -9,8 +9,8 @@ namespace CarbonResources { -FilterResourceFilter::FilterResourceFilter( const std::string& rawFilter ) - : m_rawFilter( rawFilter ) +FilterResourceFilter::FilterResourceFilter( const std::string& rawFilter ) : + m_rawFilter( rawFilter ) { ParseFilters(); } @@ -30,6 +30,18 @@ const std::vector& FilterResourceFilter::GetExcludeFilter() const return m_excludeFilter; } +void FilterResourceFilter::PlaceTokenInCorrectVector( const std::string& token, std::vector& fromVector, std::vector& toVector ) +{ + // Remove token from the fromVector if present + auto it = std::find( fromVector.begin(), fromVector.end(), token ); + if( it != fromVector.end() ) + fromVector.erase( it ); + + // Add token to the toVector if not already present in it. + if( std::find( toVector.begin(), toVector.end(), token ) == toVector.end() ) + toVector.push_back( token ); +} + void FilterResourceFilter::ParseFilters() { m_includeFilter.clear(); @@ -40,7 +52,7 @@ void FilterResourceFilter::ParseFilters() while( pos < s.size() ) { // Skip whitespace - while( pos < s.size() && std::isspace(static_cast(s[pos])) ) + while( pos < s.size() && std::isspace( static_cast( s[pos] ) ) ) ++pos; if( pos >= s.size() ) break; @@ -75,26 +87,9 @@ void FilterResourceFilter::ParseFilters() if( token.empty() ) continue; - if( isExclude ) - { - // Remove from include if present - auto it = std::find( m_includeFilter.begin(), m_includeFilter.end(), token ); - if( it != m_includeFilter.end() ) - m_includeFilter.erase( it ); - // Add to exclude if not present - if( std::find( m_excludeFilter.begin(), m_excludeFilter.end(), token ) == m_excludeFilter.end() ) - m_excludeFilter.push_back( token ); - } - else - { - // Remove from exclude if present - auto it = std::find( m_excludeFilter.begin(), m_excludeFilter.end(), token ); - if( it != m_excludeFilter.end() ) - m_excludeFilter.erase( it ); - // Add to include if not present - if( std::find( m_includeFilter.begin(), m_includeFilter.end(), token ) == m_includeFilter.end() ) - m_includeFilter.push_back( token ); - } + PlaceTokenInCorrectVector( token, + isExclude ? m_includeFilter : m_excludeFilter, + isExclude ? m_excludeFilter : m_includeFilter ); } pos = endBracket + 1; } diff --git a/src/FilterResourceFilter.h b/src/FilterResourceFilter.h index 0920588..bade90c 100644 --- a/src/FilterResourceFilter.h +++ b/src/FilterResourceFilter.h @@ -26,6 +26,14 @@ class FilterResourceFilter std::vector m_excludeFilter; void ParseFilters(); + + /// @brief Static helper function that places a token in the correct vector, moving it from one vector to another if need be. + /// @param token the token to place in the correct vector. + /// @param fromVector the vector to remove the token from if it exists there. + /// @param toVector the vector to add the token to if it does not already exist there. + /// @see CarbonResources::FilterResourceFilter::ParseFilters for usage. + /// @return void + static void PlaceTokenInCorrectVector( const std::string& token, std::vector& fromVector, std::vector& toVector ); }; } From b79fe7e7f865cca4e73ddc6f44deeb4348b400c2 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 5 Dec 2025 16:49:34 +0000 Subject: [PATCH 009/124] Add error tests for FilterResourceFilter::ParseFilters() --- src/FilterResourceFilter.cpp | 12 +- tests/src/ResourceFilterTest.cpp | 204 +++++++++++++++++++++++++++---- 2 files changed, 190 insertions(+), 26 deletions(-) diff --git a/src/FilterResourceFilter.cpp b/src/FilterResourceFilter.cpp index a38a6b1..0683aea 100644 --- a/src/FilterResourceFilter.cpp +++ b/src/FilterResourceFilter.cpp @@ -57,18 +57,28 @@ void FilterResourceFilter::ParseFilters() if( pos >= s.size() ) break; + // Check for exclude filter marker '!' bool isExclude = false; if( s[pos] == '!' ) { + // We have an exclude filter, advance the position by one and skip whitespace(s) isExclude = true; ++pos; + while( pos < s.size() && std::isspace( static_cast( s[pos] ) ) ) + ++pos; + if( pos >= s.size() ) + throw std::invalid_argument( "Invalid filter format: exclude filter marker found without a [ token ] section" ); } if( pos >= s.size() || s[pos] != '[' ) throw std::invalid_argument( "Invalid filter format: missing '['" ); - ++pos; // skip '[' + size_t endBracket = s.find( ']', pos ); + size_t nextStartBracket = s.find( '[', pos ); + if( nextStartBracket != std::string::npos && nextStartBracket < endBracket ) + throw std::invalid_argument( "Invalid filter format: matching end bracket ']' not present before the next start bracket '['" ); + if( endBracket == std::string::npos ) throw std::invalid_argument( "Invalid filter format: missing ']'" ); diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 7fd4e19..f6684cd 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -30,66 +30,220 @@ TEST_F( ResourceFilterTest, Example1IniParsing ) TEST_F( ResourceFilterTest, OnlyIncludeFilter ) { - CarbonResources::FilterResourceFilter filter("[ .this .is .included ]"); + CarbonResources::FilterResourceFilter filter( "[ .this .is .included ]" ); const auto& includes = filter.GetIncludeFilter(); const auto& excludes = filter.GetExcludeFilter(); - EXPECT_EQ(includes.size(), 3); - EXPECT_EQ(includes[0], ".this"); - EXPECT_EQ(includes[1], ".is"); - EXPECT_EQ(includes[2], ".included"); - EXPECT_TRUE(excludes.empty()); + EXPECT_EQ( includes.size(), 3 ); + EXPECT_EQ( includes[0], ".this" ); + EXPECT_EQ( includes[1], ".is" ); + EXPECT_EQ( includes[2], ".included" ); + EXPECT_TRUE( excludes.empty() ); } TEST_F( ResourceFilterTest, OnlyExcludeFilter ) { - CarbonResources::FilterResourceFilter filter("![ .excluded .extension ]"); + CarbonResources::FilterResourceFilter filter( "![ .excluded .extension ]" ); const auto& includes = filter.GetIncludeFilter(); const auto& excludes = filter.GetExcludeFilter(); - EXPECT_TRUE(includes.empty()); - EXPECT_EQ(excludes.size(), 2); - EXPECT_EQ(excludes[0], ".excluded"); - EXPECT_EQ(excludes[1], ".extension"); + EXPECT_TRUE( includes.empty() ); + EXPECT_EQ( excludes.size(), 2 ); + EXPECT_EQ( excludes[0], ".excluded" ); + EXPECT_EQ( excludes[1], ".extension" ); } TEST_F( ResourceFilterTest, ComplexIncludeExcludeFilter ) { - CarbonResources::FilterResourceFilter filter("[ .red .gr2 .dds .png .yaml ] [ .txt ] ![ .csv .xls ] [ .bat .sh ] ![ .blk .yel ]"); + CarbonResources::FilterResourceFilter filter( "[ .red .gr2 .dds .png .yaml ] [ .txt ] ![ .csv .xls ] [ .bat .sh ] ![ .blk .yel ]" ); const auto& includes = filter.GetIncludeFilter(); const auto& excludes = filter.GetExcludeFilter(); std::vector expectedIncludes = { ".red", ".gr2", ".dds", ".png", ".yaml", ".txt", ".bat", ".sh" }; std::vector expectedExcludes = { ".csv", ".xls", ".blk", ".yel" }; - EXPECT_EQ(includes, expectedIncludes); - EXPECT_EQ(excludes, expectedExcludes); + EXPECT_EQ( includes, expectedIncludes ); + EXPECT_EQ( excludes, expectedExcludes ); } TEST_F( ResourceFilterTest, SimpleIncludeFilter ) { - CarbonResources::FilterResourceFilter filter("[ .red ]"); + CarbonResources::FilterResourceFilter filter( "[ .red ]" ); const auto& includes = filter.GetIncludeFilter(); - EXPECT_EQ(includes.size(), 1); - EXPECT_EQ(includes[0], ".red"); + EXPECT_EQ( includes.size(), 1 ); + EXPECT_EQ( includes[0], ".red" ); } TEST_F( ResourceFilterTest, SimpleExcludeFilter ) { - CarbonResources::FilterResourceFilter filter("![ .blk ]"); + CarbonResources::FilterResourceFilter filter( "![ .blk ]" ); const auto& excludes = filter.GetExcludeFilter(); - EXPECT_EQ(excludes.size(), 1); - EXPECT_EQ(excludes[0], ".blk"); + EXPECT_EQ( excludes.size(), 1 ); + EXPECT_EQ( excludes[0], ".blk" ); } -TEST_F( ResourceFilterTest, IncludeExcludeInclude) +TEST_F( ResourceFilterTest, IncludeExcludeInclude ) { // Include .in1 and .in2 // Exclude .in2, .ex1, and .ex2 (removes .in2 from include) // Include .ex1, .in3 and .in1 (removes .ex1 from exclude, adds .in3, keeps .in1) // Resulting include: .in1, .ex1, .in3 // Resulting exclude: .in2, .ex2 - CarbonResources::FilterResourceFilter filter("[ .in1 .in2 ] ![ .in2 .ex1 .ex2 ] [ .ex1 .in3 .in1 ]"); + CarbonResources::FilterResourceFilter filter( "[ .in1 .in2 ] ![ .in2 .ex1 .ex2 ] [ .ex1 .in3 .in1 ]" ); const auto& includes = filter.GetIncludeFilter(); const auto& excludes = filter.GetExcludeFilter(); std::vector expectedIncludes = { ".in1", ".ex1", ".in3" }; std::vector expectedExcludes = { ".in2", ".ex2" }; - EXPECT_EQ(includes, expectedIncludes); - EXPECT_EQ(excludes, expectedExcludes); -} \ No newline at end of file + EXPECT_EQ( includes, expectedIncludes ); + EXPECT_EQ( excludes, expectedExcludes ); +} + +TEST_F( ResourceFilterTest, MissingClosingIncludeBracketBeforeNextOpenExcludeBracket ) +{ + try + { + // This test filter has a missing closing bracket for the first include filter, before the next exclude filter starts + CarbonResources::FilterResourceFilter filter( "[ .in1 ! [ .ex1 ]" ); + FAIL() << "Expected std::invalid_argument (1)"; + } + catch( const std::invalid_argument& e ) + { + EXPECT_STREQ( e.what(), "Invalid filter format: matching end bracket ']' not present before the next start bracket '['" ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument (2)"; + } +} + +TEST_F( ResourceFilterTest, ExcludeMarkerWithoutBracket_v1 ) +{ + try + { + CarbonResources::FilterResourceFilter filter( "! .ex1 ]" ); + FAIL() << "Expected std::invalid_argument (1)"; + } + catch( const std::invalid_argument& e ) + { + EXPECT_STREQ( e.what(), "Invalid filter format: missing '['" ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument (2)"; + } +} + +TEST_F( ResourceFilterTest, ExcludeMarkerWithoutBracket_v2 ) +{ + try + { + CarbonResources::FilterResourceFilter filter( " [ .in1 ] ! .ex1 ]" ); + FAIL() << "Expected std::invalid_argument (1)"; + } + catch( const std::invalid_argument& e ) + { + EXPECT_STREQ( e.what(), "Invalid filter format: missing '['" ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument (2)"; + } +} + +TEST_F( ResourceFilterTest, ExcludeMarkerWithoutBracket_v3 ) +{ + try + { + CarbonResources::FilterResourceFilter filter( " [ .in1 ] ![ .ex1 ] ! " ); + FAIL() << "Expected std::invalid_argument (1)"; + } + catch( const std::invalid_argument& e ) + { + EXPECT_STREQ( e.what(), "Invalid filter format: exclude filter marker found without a [ token ] section" ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument (2)"; + } +} + +TEST_F( ResourceFilterTest, MissingOpeningBracket_v1 ) +{ + try + { + CarbonResources::FilterResourceFilter filter( ".in1 .in2 ]" ); + FAIL() << "Expected std::invalid_argument (1)"; + } + catch( const std::invalid_argument& e ) + { + EXPECT_STREQ( e.what(), "Invalid filter format: missing '['" ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument (2)"; + } +} + +TEST_F( ResourceFilterTest, MissingOpeningBracket_v2 ) +{ + try + { + CarbonResources::FilterResourceFilter filter( " [ .in1 .in2 ] .in3 ] " ); + FAIL() << "Expected std::invalid_argument (1)"; + } + catch( const std::invalid_argument& e ) + { + EXPECT_STREQ( e.what(), "Invalid filter format: missing '['" ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument (2)"; + } +} + +TEST_F( ResourceFilterTest, MissingClosingBracket_v1 ) +{ + try + { + CarbonResources::FilterResourceFilter filter( "[ .in1 .in2 " ); + FAIL() << "Expected std::invalid_argument (1)"; + } + catch( const std::invalid_argument& e ) + { + EXPECT_STREQ( e.what(), "Invalid filter format: missing ']'" ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument (2)"; + } +} + +TEST_F( ResourceFilterTest, MissingClosingBracket_v2 ) +{ + try + { + CarbonResources::FilterResourceFilter filter( "[ .in1 .in2 ] [ .in3 " ); + FAIL() << "Expected std::invalid_argument (1)"; + } + catch( const std::invalid_argument& e ) + { + EXPECT_STREQ( e.what(), "Invalid filter format: missing ']'" ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument (2)"; + } +} + +TEST_F( ResourceFilterTest, MissingClosingBracket_v3 ) +{ + try + { + CarbonResources::FilterResourceFilter filter( "[ .in1 .in2 ] [ .in3 [ .in4 ]" ); + FAIL() << "Expected std::invalid_argument (1)"; + } + catch( const std::invalid_argument& e ) + { + EXPECT_STREQ( e.what(), "Invalid filter format: matching end bracket ']' not present before the next start bracket '['" ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument (2)"; + } +} From a2bd883ca25b7a677dcf8896ba9a3c72e43dcc07 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 8 Dec 2025 14:00:00 +0000 Subject: [PATCH 010/124] Add tests for Condensed but valid filter strings --- tests/src/ResourceFilterTest.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index f6684cd..f1906a1 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -247,3 +247,25 @@ TEST_F( ResourceFilterTest, MissingClosingBracket_v3 ) FAIL() << "Expected std::invalid_argument (2)"; } } + +TEST_F( ResourceFilterTest, CondensedValidFilterStringv1 ) +{ + CarbonResources::FilterResourceFilter filter( "[inToken1 inToken2]![exToken1 exToken2][inToken3]" ); + const auto& includes = filter.GetIncludeFilter(); + const auto& excludes = filter.GetExcludeFilter(); + std::vector expectedIncludes = { "inToken1", "inToken2", "inToken3" }; + std::vector expectedExcludes = { "exToken1", "exToken2" }; + EXPECT_EQ( includes, expectedIncludes ); + EXPECT_EQ( excludes, expectedExcludes ); +} + +TEST_F( ResourceFilterTest, CondensedValidFilterStringv2 ) +{ + CarbonResources::FilterResourceFilter filter( "![exToken1][inToken1 inToken2]![exToken2][inToken3]" ); + const auto& includes = filter.GetIncludeFilter(); + const auto& excludes = filter.GetExcludeFilter(); + std::vector expectedIncludes = { "inToken1", "inToken2", "inToken3" }; + std::vector expectedExcludes = { "exToken1", "exToken2" }; + EXPECT_EQ( includes, expectedIncludes ); + EXPECT_EQ( excludes, expectedExcludes ); +} \ No newline at end of file From 1a406b3348593d7e1608bb1b6a78845d1a995095 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 8 Dec 2025 14:14:34 +0000 Subject: [PATCH 011/124] Add Doxygen markdown comments to FilterResourceFilter class. --- src/FilterResourceFilter.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/FilterResourceFilter.h b/src/FilterResourceFilter.h index bade90c..a9a6156 100644 --- a/src/FilterResourceFilter.h +++ b/src/FilterResourceFilter.h @@ -12,19 +12,42 @@ namespace CarbonResources class FilterResourceFilter { public: + /// @brief Constructor that takes a raw filter string and parses it into include and exclude filters. + /// @param rawFilter the raw filter string to parse. + /// @note Calls ParseFilters() which may throw std::invalid_argument if the filter string is malformed. + /// @see CarbonResources::FilterResourceFilter::ParseFilters explicit FilterResourceFilter( const std::string& rawFilter ); + /// @brief Gets the raw filter string. + /// @return the raw filter string. const std::string& GetRawFilter() const; + /// @brief Gets the include filter vector. + /// @return the valid include filter as a vector. const std::vector& GetIncludeFilter() const; + /// @brief Gets the exclude filter vector. + /// @return the valid exclude filter as a vector. const std::vector& GetExcludeFilter() const; private: + /// @brief The raw filter string. + /// @note Set in the constructor and used in ParseFilters(). std::string m_rawFilter; + + /// @brief The include filter vector. + /// @note Populated in ParseFilters(). + /// @see CarbonResources::FilterResourceFilter::ParseFilters std::vector m_includeFilter; + + /// @brief The exclude filter vector. + /// @note Populated in ParseFilters(). + /// @see CarbonResources::FilterResourceFilter::ParseFilters std::vector m_excludeFilter; + /// @brief Parses the raw filter string into include and exclude filters. + /// @return void + /// @note Throws std::invalid_argument if the filter string is malformed, bubbling the error up to the callar (class constructor). void ParseFilters(); /// @brief Static helper function that places a token in the correct vector, moving it from one vector to another if need be. From c65e3c4fc2b10f32a0ea5ff5932762faa3bd098c Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 9 Dec 2025 17:09:35 +0000 Subject: [PATCH 012/124] Change scaffolding of FilterResourceFilter --- src/FilterResourceFilter.h | 5 ++++- src/FilterResourceLine.cpp | 14 ++++++++++---- src/FilterResourceLine.h | 35 +++++++++++++++++++++++++++++++---- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/FilterResourceFilter.h b/src/FilterResourceFilter.h index a9a6156..042db2f 100644 --- a/src/FilterResourceFilter.h +++ b/src/FilterResourceFilter.h @@ -9,6 +9,9 @@ namespace CarbonResources { +/// @brief Class representing a resource filter with include and exclude filters. +/// @note This can be for the filter attribute of a NamedSection OR the optional filter for a respaths/resfile line (defined in FilterResourceLine). +/// see CarbonResources::FilterNamedSection and CarbonResources::FilterResourceLine for usage. class FilterResourceFilter { public: @@ -47,7 +50,7 @@ class FilterResourceFilter /// @brief Parses the raw filter string into include and exclude filters. /// @return void - /// @note Throws std::invalid_argument if the filter string is malformed, bubbling the error up to the callar (class constructor). + /// @note Throws std::invalid_argument if the filter string is malformed, bubbling the error up to the caller (class constructor). void ParseFilters(); /// @brief Static helper function that places a token in the correct vector, moving it from one vector to another if need be. diff --git a/src/FilterResourceLine.cpp b/src/FilterResourceLine.cpp index 1c50aee..6a3619c 100644 --- a/src/FilterResourceLine.cpp +++ b/src/FilterResourceLine.cpp @@ -6,15 +6,21 @@ namespace CarbonResources { -FilterResourceLine::FilterResourceLine( const std::string& line, const FilterPrefixmap& prefixMap, const FilterResourceFilter& sectionFilter ) : - m_line( line ), +FilterResourceLine::FilterResourceLine( const std::string& rawLine, const FilterPrefixmap& prefixMap, const FilterResourceFilter& sectionFilter ) : + m_rawLine( rawLine ), m_prefixMap( prefixMap ), - m_resFilter( sectionFilter ) + m_sectionFilter( sectionFilter ) { throw std::logic_error( "Not implemented yet exception" ); } -bool FilterResourceLine::IsValid() const +// TODO: Remove this, probably don't need it. +//bool FilterResourceLine::IsValid() const +//{ +// throw std::logic_error( "Not implemented yet exception" ); +//} + +void FilterResourceLine::ParseLine() { throw std::logic_error( "Not implemented yet exception" ); } diff --git a/src/FilterResourceLine.h b/src/FilterResourceLine.h index a7b33c1..d80fc4c 100644 --- a/src/FilterResourceLine.h +++ b/src/FilterResourceLine.h @@ -10,17 +10,44 @@ namespace CarbonResources { +/// @brief Class representing a single line of a resfile/respaths attribute. class FilterResourceLine { public: - explicit FilterResourceLine( const std::string& line, const FilterPrefixmap& prefixMap, const FilterResourceFilter& sectionFilter ); + /// @brief Constructor that takes a rawLine string, a reference to an already constructed prefixMap and sectionFilter. + /// @param rawLine the raw value of a resfile/respaths line string to parse. + /// @param prefixMap reference to an already constructed FilterPrefixmap object. + /// @param sectionFilter reference to an already constructed FilterResourceFilter object representing the "parent" filter for this section. + /// @note Calls ParseLine() which may throw std::invalid_argument if the rawLine string is malformed. + /// @see CarbonResources::FilterResourceLine::ParseLine + explicit FilterResourceLine( const std::string& rawLine, const FilterPrefixmap& prefixMap, const FilterResourceFilter& sectionFilter ); - bool IsValid() const; + // bool IsValid() const; // TODO: Remove this, probably don't need it. + + // TODO: Add a getter function that combines the section filter and optional line filter along with the concatenated prefixMap + respath/resfile value. private: - std::string m_line; + /// @brief The raw string, representing the value of the resfile/respaths attribute. + /// @note Set in the constructor and used in ParseLine(). + std::string m_rawLine; + + /// @brief Reference to an already constructed FilterPrefixmap object. const FilterPrefixmap& m_prefixMap; - FilterResourceFilter m_resFilter; + + /// @brief Reference to an already constructed FilterResourceFilter object representing the "parent" filter for this section. + const FilterResourceFilter& m_sectionFilter; + + // TODO: Decide how to handle optional FilterResourceFilter for the line-specific filter, if any + // Probably add: std::optional m_lineFilter + // Then add a getter function (probably private) that combines the section filter and line filter as needed. + + /// @brief The resolved full path after applying the prefix map to the file/path portion of the rawLine. + std::string m_linePath; + + /// @brief Parses the rawLine string into its components (m_linePath, optional m_lineFilter). + /// @note Throws std::invalid_argument if parsing fails, bubbling the error up to the caller (class constructor). + /// @return void + void ParseLine(); }; } From 5d151d0003fd98f93224bf6db5d3f83e287c6eec Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 10 Dec 2025 11:58:53 +0000 Subject: [PATCH 013/124] Move Filter logic into tools --- CMakeLists.txt | 13 ------------- tests/src/ResourceFilterTest.cpp | 2 +- tools/CMakeLists.txt | 12 ++++++++++++ {src => tools/include}/FilterDefaultSection.h | 0 {src => tools/include}/FilterNamedSection.h | 0 {src => tools/include}/FilterPrefixmap.h | 0 {src => tools/include}/FilterResourceFile.h | 0 {src => tools/include}/FilterResourceFilter.h | 0 {src => tools/include}/FilterResourceLine.h | 0 {src => tools/src}/FilterDefaultSection.cpp | 2 +- {src => tools/src}/FilterNamedSection.cpp | 2 +- {src => tools/src}/FilterPrefixmap.cpp | 2 +- {src => tools/src}/FilterResourceFile.cpp | 2 +- {src => tools/src}/FilterResourceFilter.cpp | 2 +- {src => tools/src}/FilterResourceLine.cpp | 2 +- 15 files changed, 19 insertions(+), 20 deletions(-) rename {src => tools/include}/FilterDefaultSection.h (100%) rename {src => tools/include}/FilterNamedSection.h (100%) rename {src => tools/include}/FilterPrefixmap.h (100%) rename {src => tools/include}/FilterResourceFile.h (100%) rename {src => tools/include}/FilterResourceFilter.h (100%) rename {src => tools/include}/FilterResourceLine.h (100%) rename {src => tools/src}/FilterDefaultSection.cpp (91%) rename {src => tools/src}/FilterNamedSection.cpp (91%) rename {src => tools/src}/FilterPrefixmap.cpp (94%) rename {src => tools/src}/FilterResourceFile.cpp (91%) rename {src => tools/src}/FilterResourceFilter.cpp (98%) rename {src => tools/src}/FilterResourceLine.cpp (95%) diff --git a/CMakeLists.txt b/CMakeLists.txt index cbfe00f..8633899 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,19 +50,6 @@ set(SRC_FILES src/ResourceInfo/BundleResourceGroupInfo.cpp src/ParameterVersion.h src/ParameterVersion.cpp - - src/FilterResourceFile.h - src/FilterResourceFile.cpp - src/FilterDefaultSection.h - src/FilterDefaultSection.cpp - src/FilterNamedSection.h - src/FilterNamedSection.cpp - src/FilterPrefixmap.h - src/FilterPrefixmap.cpp - src/FilterResourceFilter.h - src/FilterResourceFilter.cpp - src/FilterResourceLine.h - src/FilterResourceLine.cpp ) add_library(resources STATIC ${SRC_FILES}) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index f1906a1..269b98c 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -2,7 +2,7 @@ #include "ResourceFilterTest.h" #include -#include "../../src/FilterResourceFilter.h" +#include TEST_F( ResourceFilterTest, Example1IniParsing ) { diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 06b8025..97d4e92 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -20,6 +20,12 @@ set(SRC_FILES include/RollingChecksum.h include/ScopedFile.h include/StatusCallback.h + include/FilterResourceFile.h + include/FilterDefaultSection.h + include/FilterNamedSection.h + include/FilterPrefixmap.h + include/FilterResourceFilter.h + include/FilterResourceLine.h src/BundleStreamIn.cpp src/BundleStreamOut.cpp @@ -35,6 +41,12 @@ set(SRC_FILES src/ScopedFile.cpp src/Patching.cpp src/RollingChecksum.cpp + src/FilterResourceFile.cpp + src/FilterDefaultSection.cpp + src/FilterNamedSection.cpp + src/FilterPrefixmap.cpp + src/FilterResourceFilter.cpp + src/FilterResourceLine.cpp ) add_library(resources-tools STATIC ${SRC_FILES}) diff --git a/src/FilterDefaultSection.h b/tools/include/FilterDefaultSection.h similarity index 100% rename from src/FilterDefaultSection.h rename to tools/include/FilterDefaultSection.h diff --git a/src/FilterNamedSection.h b/tools/include/FilterNamedSection.h similarity index 100% rename from src/FilterNamedSection.h rename to tools/include/FilterNamedSection.h diff --git a/src/FilterPrefixmap.h b/tools/include/FilterPrefixmap.h similarity index 100% rename from src/FilterPrefixmap.h rename to tools/include/FilterPrefixmap.h diff --git a/src/FilterResourceFile.h b/tools/include/FilterResourceFile.h similarity index 100% rename from src/FilterResourceFile.h rename to tools/include/FilterResourceFile.h diff --git a/src/FilterResourceFilter.h b/tools/include/FilterResourceFilter.h similarity index 100% rename from src/FilterResourceFilter.h rename to tools/include/FilterResourceFilter.h diff --git a/src/FilterResourceLine.h b/tools/include/FilterResourceLine.h similarity index 100% rename from src/FilterResourceLine.h rename to tools/include/FilterResourceLine.h diff --git a/src/FilterDefaultSection.cpp b/tools/src/FilterDefaultSection.cpp similarity index 91% rename from src/FilterDefaultSection.cpp rename to tools/src/FilterDefaultSection.cpp index 088f7a5..fdd0b6c 100644 --- a/src/FilterDefaultSection.cpp +++ b/tools/src/FilterDefaultSection.cpp @@ -1,7 +1,7 @@ // Copyright © 2025 CCP ehf. #include -#include "FilterDefaultSection.h" +#include namespace CarbonResources { diff --git a/src/FilterNamedSection.cpp b/tools/src/FilterNamedSection.cpp similarity index 91% rename from src/FilterNamedSection.cpp rename to tools/src/FilterNamedSection.cpp index 90d1a44..3d09a1e 100644 --- a/src/FilterNamedSection.cpp +++ b/tools/src/FilterNamedSection.cpp @@ -1,7 +1,7 @@ // Copyright © 2025 CCP ehf. #include -#include "FilterNamedSection.h" +#include namespace CarbonResources { diff --git a/src/FilterPrefixmap.cpp b/tools/src/FilterPrefixmap.cpp similarity index 94% rename from src/FilterPrefixmap.cpp rename to tools/src/FilterPrefixmap.cpp index 98d5313..082b315 100644 --- a/src/FilterPrefixmap.cpp +++ b/tools/src/FilterPrefixmap.cpp @@ -1,7 +1,7 @@ // Copyright © 2025 CCP ehf. #include -#include "FilterPrefixmap.h" +#include namespace CarbonResources { diff --git a/src/FilterResourceFile.cpp b/tools/src/FilterResourceFile.cpp similarity index 91% rename from src/FilterResourceFile.cpp rename to tools/src/FilterResourceFile.cpp index 3a8424c..b7dcdcc 100644 --- a/src/FilterResourceFile.cpp +++ b/tools/src/FilterResourceFile.cpp @@ -2,7 +2,7 @@ #include -#include "FilterResourceFile.h" +#include namespace CarbonResources { diff --git a/src/FilterResourceFilter.cpp b/tools/src/FilterResourceFilter.cpp similarity index 98% rename from src/FilterResourceFilter.cpp rename to tools/src/FilterResourceFilter.cpp index 0683aea..4457220 100644 --- a/src/FilterResourceFilter.cpp +++ b/tools/src/FilterResourceFilter.cpp @@ -4,7 +4,7 @@ //#include #include #include -#include "FilterResourceFilter.h" +#include namespace CarbonResources { diff --git a/src/FilterResourceLine.cpp b/tools/src/FilterResourceLine.cpp similarity index 95% rename from src/FilterResourceLine.cpp rename to tools/src/FilterResourceLine.cpp index 6a3619c..869d2f1 100644 --- a/src/FilterResourceLine.cpp +++ b/tools/src/FilterResourceLine.cpp @@ -1,7 +1,7 @@ // Copyright © 2025 CCP ehf. #include -#include "FilterResourceLine.h" +#include namespace CarbonResources { From c8b8bcd6a6d82757c88d2dd2740549e807687aaa Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 10 Dec 2025 14:08:20 +0000 Subject: [PATCH 014/124] Make filter tool classes part of ResourceTools namespace --- tools/include/FilterDefaultSection.h | 2 +- tools/include/FilterNamedSection.h | 2 +- tools/include/FilterPrefixmap.h | 2 +- tools/include/FilterResourceFile.h | 2 +- tools/include/FilterResourceFilter.h | 2 +- tools/include/FilterResourceLine.h | 2 +- tools/src/FilterDefaultSection.cpp | 2 +- tools/src/FilterNamedSection.cpp | 2 +- tools/src/FilterPrefixmap.cpp | 2 +- tools/src/FilterResourceFile.cpp | 2 +- tools/src/FilterResourceFilter.cpp | 2 +- tools/src/FilterResourceLine.cpp | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tools/include/FilterDefaultSection.h b/tools/include/FilterDefaultSection.h index 8c79da8..459cc23 100644 --- a/tools/include/FilterDefaultSection.h +++ b/tools/include/FilterDefaultSection.h @@ -5,7 +5,7 @@ #include "FilterPrefixmap.h" -namespace CarbonResources +namespace ResourceTools { class FilterDefaultSection diff --git a/tools/include/FilterNamedSection.h b/tools/include/FilterNamedSection.h index e9cace5..34480d4 100644 --- a/tools/include/FilterNamedSection.h +++ b/tools/include/FilterNamedSection.h @@ -8,7 +8,7 @@ #include "FilterResourceFilter.h" #include "FilterResourceLine.h" -namespace CarbonResources +namespace ResourceTools { class FilterNamedSection diff --git a/tools/include/FilterPrefixmap.h b/tools/include/FilterPrefixmap.h index 92acf35..f8372a7 100644 --- a/tools/include/FilterPrefixmap.h +++ b/tools/include/FilterPrefixmap.h @@ -7,7 +7,7 @@ #include #include -namespace CarbonResources +namespace ResourceTools { class FilterPrefixmap diff --git a/tools/include/FilterResourceFile.h b/tools/include/FilterResourceFile.h index 147ee12..1756665 100644 --- a/tools/include/FilterResourceFile.h +++ b/tools/include/FilterResourceFile.h @@ -7,7 +7,7 @@ #include "FilterDefaultSection.h" #include "FilterNamedSection.h" -namespace CarbonResources +namespace ResourceTools { class FilterResourceFile diff --git a/tools/include/FilterResourceFilter.h b/tools/include/FilterResourceFilter.h index 042db2f..eb4a0a4 100644 --- a/tools/include/FilterResourceFilter.h +++ b/tools/include/FilterResourceFilter.h @@ -6,7 +6,7 @@ #include #include -namespace CarbonResources +namespace ResourceTools { /// @brief Class representing a resource filter with include and exclude filters. diff --git a/tools/include/FilterResourceLine.h b/tools/include/FilterResourceLine.h index d80fc4c..569dd1a 100644 --- a/tools/include/FilterResourceLine.h +++ b/tools/include/FilterResourceLine.h @@ -7,7 +7,7 @@ #include "FilterPrefixmap.h" #include "FilterResourceFilter.h" -namespace CarbonResources +namespace ResourceTools { /// @brief Class representing a single line of a resfile/respaths attribute. diff --git a/tools/src/FilterDefaultSection.cpp b/tools/src/FilterDefaultSection.cpp index fdd0b6c..0181402 100644 --- a/tools/src/FilterDefaultSection.cpp +++ b/tools/src/FilterDefaultSection.cpp @@ -3,7 +3,7 @@ #include #include -namespace CarbonResources +namespace ResourceTools { FilterDefaultSection::FilterDefaultSection( const std::string& prefixmapStr ) : diff --git a/tools/src/FilterNamedSection.cpp b/tools/src/FilterNamedSection.cpp index 3d09a1e..a74a900 100644 --- a/tools/src/FilterNamedSection.cpp +++ b/tools/src/FilterNamedSection.cpp @@ -3,7 +3,7 @@ #include #include -namespace CarbonResources +namespace ResourceTools { FilterNamedSection::FilterNamedSection( const std::string& filter, const std::string& resfile, const std::vector& respaths ) : diff --git a/tools/src/FilterPrefixmap.cpp b/tools/src/FilterPrefixmap.cpp index 082b315..ce26061 100644 --- a/tools/src/FilterPrefixmap.cpp +++ b/tools/src/FilterPrefixmap.cpp @@ -3,7 +3,7 @@ #include #include -namespace CarbonResources +namespace ResourceTools { FilterPrefixmap::FilterPrefixmap( const std::string& prefixmapStr ) diff --git a/tools/src/FilterResourceFile.cpp b/tools/src/FilterResourceFile.cpp index b7dcdcc..6e53450 100644 --- a/tools/src/FilterResourceFile.cpp +++ b/tools/src/FilterResourceFile.cpp @@ -4,7 +4,7 @@ #include -namespace CarbonResources +namespace ResourceTools { FilterResourceFile::FilterResourceFile( const FilterDefaultSection& defaultSection, const std::vector& namedSections ) : diff --git a/tools/src/FilterResourceFilter.cpp b/tools/src/FilterResourceFilter.cpp index 4457220..18d2c11 100644 --- a/tools/src/FilterResourceFilter.cpp +++ b/tools/src/FilterResourceFilter.cpp @@ -6,7 +6,7 @@ #include #include -namespace CarbonResources +namespace ResourceTools { FilterResourceFilter::FilterResourceFilter( const std::string& rawFilter ) : diff --git a/tools/src/FilterResourceLine.cpp b/tools/src/FilterResourceLine.cpp index 869d2f1..1b88a2a 100644 --- a/tools/src/FilterResourceLine.cpp +++ b/tools/src/FilterResourceLine.cpp @@ -3,7 +3,7 @@ #include #include -namespace CarbonResources +namespace ResourceTools { FilterResourceLine::FilterResourceLine( const std::string& rawLine, const FilterPrefixmap& prefixMap, const FilterResourceFilter& sectionFilter ) : From 5fed2b91df6b20f28573f321a9448534f078a424 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 10 Dec 2025 14:17:23 +0000 Subject: [PATCH 015/124] Fix Filter tests to now use new ResourceTools namespace --- tests/src/ResourceFilterTest.cpp | 34 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 269b98c..4dd785f 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -30,7 +30,7 @@ TEST_F( ResourceFilterTest, Example1IniParsing ) TEST_F( ResourceFilterTest, OnlyIncludeFilter ) { - CarbonResources::FilterResourceFilter filter( "[ .this .is .included ]" ); + ResourceTools::FilterResourceFilter filter( "[ .this .is .included ]" ); const auto& includes = filter.GetIncludeFilter(); const auto& excludes = filter.GetExcludeFilter(); EXPECT_EQ( includes.size(), 3 ); @@ -42,7 +42,7 @@ TEST_F( ResourceFilterTest, OnlyIncludeFilter ) TEST_F( ResourceFilterTest, OnlyExcludeFilter ) { - CarbonResources::FilterResourceFilter filter( "![ .excluded .extension ]" ); + ResourceTools::FilterResourceFilter filter( "![ .excluded .extension ]" ); const auto& includes = filter.GetIncludeFilter(); const auto& excludes = filter.GetExcludeFilter(); EXPECT_TRUE( includes.empty() ); @@ -53,7 +53,7 @@ TEST_F( ResourceFilterTest, OnlyExcludeFilter ) TEST_F( ResourceFilterTest, ComplexIncludeExcludeFilter ) { - CarbonResources::FilterResourceFilter filter( "[ .red .gr2 .dds .png .yaml ] [ .txt ] ![ .csv .xls ] [ .bat .sh ] ![ .blk .yel ]" ); + ResourceTools::FilterResourceFilter filter( "[ .red .gr2 .dds .png .yaml ] [ .txt ] ![ .csv .xls ] [ .bat .sh ] ![ .blk .yel ]" ); const auto& includes = filter.GetIncludeFilter(); const auto& excludes = filter.GetExcludeFilter(); std::vector expectedIncludes = { ".red", ".gr2", ".dds", ".png", ".yaml", ".txt", ".bat", ".sh" }; @@ -64,7 +64,7 @@ TEST_F( ResourceFilterTest, ComplexIncludeExcludeFilter ) TEST_F( ResourceFilterTest, SimpleIncludeFilter ) { - CarbonResources::FilterResourceFilter filter( "[ .red ]" ); + ResourceTools::FilterResourceFilter filter( "[ .red ]" ); const auto& includes = filter.GetIncludeFilter(); EXPECT_EQ( includes.size(), 1 ); EXPECT_EQ( includes[0], ".red" ); @@ -72,7 +72,7 @@ TEST_F( ResourceFilterTest, SimpleIncludeFilter ) TEST_F( ResourceFilterTest, SimpleExcludeFilter ) { - CarbonResources::FilterResourceFilter filter( "![ .blk ]" ); + ResourceTools::FilterResourceFilter filter( "![ .blk ]" ); const auto& excludes = filter.GetExcludeFilter(); EXPECT_EQ( excludes.size(), 1 ); EXPECT_EQ( excludes[0], ".blk" ); @@ -85,7 +85,7 @@ TEST_F( ResourceFilterTest, IncludeExcludeInclude ) // Include .ex1, .in3 and .in1 (removes .ex1 from exclude, adds .in3, keeps .in1) // Resulting include: .in1, .ex1, .in3 // Resulting exclude: .in2, .ex2 - CarbonResources::FilterResourceFilter filter( "[ .in1 .in2 ] ![ .in2 .ex1 .ex2 ] [ .ex1 .in3 .in1 ]" ); + ResourceTools::FilterResourceFilter filter( "[ .in1 .in2 ] ![ .in2 .ex1 .ex2 ] [ .ex1 .in3 .in1 ]" ); const auto& includes = filter.GetIncludeFilter(); const auto& excludes = filter.GetExcludeFilter(); std::vector expectedIncludes = { ".in1", ".ex1", ".in3" }; @@ -99,7 +99,7 @@ TEST_F( ResourceFilterTest, MissingClosingIncludeBracketBeforeNextOpenExcludeBra try { // This test filter has a missing closing bracket for the first include filter, before the next exclude filter starts - CarbonResources::FilterResourceFilter filter( "[ .in1 ! [ .ex1 ]" ); + ResourceTools::FilterResourceFilter filter( "[ .in1 ! [ .ex1 ]" ); FAIL() << "Expected std::invalid_argument (1)"; } catch( const std::invalid_argument& e ) @@ -116,7 +116,7 @@ TEST_F( ResourceFilterTest, ExcludeMarkerWithoutBracket_v1 ) { try { - CarbonResources::FilterResourceFilter filter( "! .ex1 ]" ); + ResourceTools::FilterResourceFilter filter( "! .ex1 ]" ); FAIL() << "Expected std::invalid_argument (1)"; } catch( const std::invalid_argument& e ) @@ -133,7 +133,7 @@ TEST_F( ResourceFilterTest, ExcludeMarkerWithoutBracket_v2 ) { try { - CarbonResources::FilterResourceFilter filter( " [ .in1 ] ! .ex1 ]" ); + ResourceTools::FilterResourceFilter filter( " [ .in1 ] ! .ex1 ]" ); FAIL() << "Expected std::invalid_argument (1)"; } catch( const std::invalid_argument& e ) @@ -150,7 +150,7 @@ TEST_F( ResourceFilterTest, ExcludeMarkerWithoutBracket_v3 ) { try { - CarbonResources::FilterResourceFilter filter( " [ .in1 ] ![ .ex1 ] ! " ); + ResourceTools::FilterResourceFilter filter( " [ .in1 ] ![ .ex1 ] ! " ); FAIL() << "Expected std::invalid_argument (1)"; } catch( const std::invalid_argument& e ) @@ -167,7 +167,7 @@ TEST_F( ResourceFilterTest, MissingOpeningBracket_v1 ) { try { - CarbonResources::FilterResourceFilter filter( ".in1 .in2 ]" ); + ResourceTools::FilterResourceFilter filter( ".in1 .in2 ]" ); FAIL() << "Expected std::invalid_argument (1)"; } catch( const std::invalid_argument& e ) @@ -184,7 +184,7 @@ TEST_F( ResourceFilterTest, MissingOpeningBracket_v2 ) { try { - CarbonResources::FilterResourceFilter filter( " [ .in1 .in2 ] .in3 ] " ); + ResourceTools::FilterResourceFilter filter( " [ .in1 .in2 ] .in3 ] " ); FAIL() << "Expected std::invalid_argument (1)"; } catch( const std::invalid_argument& e ) @@ -201,7 +201,7 @@ TEST_F( ResourceFilterTest, MissingClosingBracket_v1 ) { try { - CarbonResources::FilterResourceFilter filter( "[ .in1 .in2 " ); + ResourceTools::FilterResourceFilter filter( "[ .in1 .in2 " ); FAIL() << "Expected std::invalid_argument (1)"; } catch( const std::invalid_argument& e ) @@ -218,7 +218,7 @@ TEST_F( ResourceFilterTest, MissingClosingBracket_v2 ) { try { - CarbonResources::FilterResourceFilter filter( "[ .in1 .in2 ] [ .in3 " ); + ResourceTools::FilterResourceFilter filter( "[ .in1 .in2 ] [ .in3 " ); FAIL() << "Expected std::invalid_argument (1)"; } catch( const std::invalid_argument& e ) @@ -235,7 +235,7 @@ TEST_F( ResourceFilterTest, MissingClosingBracket_v3 ) { try { - CarbonResources::FilterResourceFilter filter( "[ .in1 .in2 ] [ .in3 [ .in4 ]" ); + ResourceTools::FilterResourceFilter filter( "[ .in1 .in2 ] [ .in3 [ .in4 ]" ); FAIL() << "Expected std::invalid_argument (1)"; } catch( const std::invalid_argument& e ) @@ -250,7 +250,7 @@ TEST_F( ResourceFilterTest, MissingClosingBracket_v3 ) TEST_F( ResourceFilterTest, CondensedValidFilterStringv1 ) { - CarbonResources::FilterResourceFilter filter( "[inToken1 inToken2]![exToken1 exToken2][inToken3]" ); + ResourceTools::FilterResourceFilter filter( "[inToken1 inToken2]![exToken1 exToken2][inToken3]" ); const auto& includes = filter.GetIncludeFilter(); const auto& excludes = filter.GetExcludeFilter(); std::vector expectedIncludes = { "inToken1", "inToken2", "inToken3" }; @@ -261,7 +261,7 @@ TEST_F( ResourceFilterTest, CondensedValidFilterStringv1 ) TEST_F( ResourceFilterTest, CondensedValidFilterStringv2 ) { - CarbonResources::FilterResourceFilter filter( "![exToken1][inToken1 inToken2]![exToken2][inToken3]" ); + ResourceTools::FilterResourceFilter filter( "![exToken1][inToken1 inToken2]![exToken2][inToken3]" ); const auto& includes = filter.GetIncludeFilter(); const auto& excludes = filter.GetExcludeFilter(); std::vector expectedIncludes = { "inToken1", "inToken2", "inToken3" }; From 369e3c809da6080d85ba8503eccc5043fdacc174 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 10 Dec 2025 15:38:08 +0000 Subject: [PATCH 016/124] Add test for multiline respaths with empty lines --- tests/src/ResourceFilterTest.cpp | 13 ++++++++----- tests/testData/ExampleIniFiles/example1.ini | 13 +++++++++---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 4dd785f..2c05904 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -21,11 +21,14 @@ TEST_F( ResourceFilterTest, Example1IniParsing ) EXPECT_EQ( reader.Keys( "default" ).size(), 2 ); // Check [testyamlfilesovermultilinerespaths] section - ASSERT_TRUE( reader.HasSection( "testyamlfilesovermultilinerespaths" ) ); - EXPECT_EQ( reader.Get( "testyamlfilesovermultilinerespaths", "filter", "" ), "[ .yaml ]" ); - EXPECT_EQ( reader.Get( "testyamlfilesovermultilinerespaths", "resfile", "" ), "res:/binaryFileIndex_v0_0_0.txt" ); - EXPECT_EQ( reader.Get( "testyamlfilesovermultilinerespaths", "respaths", "" ), "res:/...\nres2:/..." ); - EXPECT_EQ( reader.Keys( "testyamlfilesovermultilinerespaths" ).size(), 3 ); + ASSERT_TRUE( reader.HasSection( "testYamlFilesOverMultiLineResPathsWithEmptyLines" ) ); + EXPECT_EQ( reader.Get( "testYamlFilesOverMultiLineResPathsWithEmptyLines", "filter", "" ), "[ .yaml ]" ); + EXPECT_EQ( reader.Get( "testYamlFilesOverMultiLineResPathsWithEmptyLines", "resfile", "" ), "res:/binaryFileIndex_v0_0_0.txt" ); + auto respathValueGet = reader.Get( "testYamlFilesOverMultiLineResPathsWithEmptyLines", "respaths", "" ); + std::string respathValueGetString = reader.Get( "testYamlFilesOverMultiLineResPathsWithEmptyLines", "respaths", "" ); + EXPECT_EQ( respathValueGet, respathValueGetString ); + EXPECT_EQ( respathValueGet, "res:/firstLine/...\nres:/secondLine/...\nres2:/thirdLine/..." ); // Note: Under the hood, INIReader converts multi-empty-lines to a single \n line breaks + EXPECT_EQ( reader.Keys( "testYamlFilesOverMultiLineResPathsWithEmptyLines" ).size(), 3 ); } TEST_F( ResourceFilterTest, OnlyIncludeFilter ) diff --git a/tests/testData/ExampleIniFiles/example1.ini b/tests/testData/ExampleIniFiles/example1.ini index b58892d..a4dff71 100644 --- a/tests/testData/ExampleIniFiles/example1.ini +++ b/tests/testData/ExampleIniFiles/example1.ini @@ -9,12 +9,17 @@ prefixmap = res:./Indicies;./resourcesOnBranch res2:./ResourceGroups # example1.ini - test file # - This file is intended to test generic ini file parsing. # - Ini file parser of this file should handle both regular ini comments (;) and Unix style (#) comments. -; - It should handle multi-line values too (as in the respaths values below). +; - It should handle multi-line values too (as in the respaths values below, with empties inbetween). ;============================================================================= -[testYamlFilesOverMultiLineResPaths] +[testYamlFilesOverMultiLineResPathsWithEmptyLines] filter = [ .yaml ] -respaths = res:/... - res2:/... +respaths = res:/firstLine/... + res:/secondLine/... + + + + + res2:/thirdLine/... resfile = res:/binaryFileIndex_v0_0_0.txt From f9d4965bcca31aed19dd55d7ac91416252dc97bb Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 11 Dec 2025 13:07:59 +0000 Subject: [PATCH 017/124] Refactor of ResourceLing and PrefixMap --- tools/CMakeLists.txt | 4 +-- tools/include/FilterDefaultSection.h | 4 +-- tools/include/FilterPrefixmap.h | 17 ++++++++--- tools/include/FilterResourceFilter.h | 42 ++++++++------------------- tools/include/FilterResourceLine.h | 43 ++++++++++++---------------- tools/src/FilterDefaultSection.cpp | 4 +-- tools/src/FilterPrefixmap.cpp | 7 +++-- tools/src/FilterResourceLine.cpp | 2 +- 8 files changed, 55 insertions(+), 68 deletions(-) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 97d4e92..1817df5 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -23,7 +23,7 @@ set(SRC_FILES include/FilterResourceFile.h include/FilterDefaultSection.h include/FilterNamedSection.h - include/FilterPrefixmap.h + include/FilterPrefixMap.h include/FilterResourceFilter.h include/FilterResourceLine.h @@ -44,7 +44,7 @@ set(SRC_FILES src/FilterResourceFile.cpp src/FilterDefaultSection.cpp src/FilterNamedSection.cpp - src/FilterPrefixmap.cpp + src/FilterPrefixMap.cpp src/FilterResourceFilter.cpp src/FilterResourceLine.cpp ) diff --git a/tools/include/FilterDefaultSection.h b/tools/include/FilterDefaultSection.h index 459cc23..a3c10e5 100644 --- a/tools/include/FilterDefaultSection.h +++ b/tools/include/FilterDefaultSection.h @@ -13,10 +13,10 @@ class FilterDefaultSection public: explicit FilterDefaultSection( const std::string& prefixmapStr ); - const FilterPrefixmap& GetPrefixmap() const; + const FilterPrefixMap& GetPrefixmap() const; private: - FilterPrefixmap m_prefixmap; + FilterPrefixMap m_prefixMap; }; } diff --git a/tools/include/FilterPrefixmap.h b/tools/include/FilterPrefixmap.h index f8372a7..bd619f4 100644 --- a/tools/include/FilterPrefixmap.h +++ b/tools/include/FilterPrefixmap.h @@ -10,17 +10,26 @@ namespace ResourceTools { -class FilterPrefixmap +// Class representing the prefixmap attribute. +// - used to resolve actual resfile/respaths attributes of a NamedSection. +class FilterPrefixMap { public: - explicit FilterPrefixmap( const std::string& prefixmapStr ); + // Constructor that takes a raw filter string and parses it into a map of prefixes to list of paths. + explicit FilterPrefixMap( const std::string& rawPrefixMap ); - const std::map>& GetMap() const; + // Gets the prefix map. + const std::map>& GetPrefixMap() const; private: + // Raw filter string. + std::string m_rawPrefixMap; + + // Map of prefixes to list of paths. + // e.g. "res:" -> { "/dir1", "/dir2" } and "res2:" -> { "/otherDir1" } std::map> m_prefixMap; - void Parse( const std::string& prefixmapStr ); + void ParsePrefixMap(); }; } diff --git a/tools/include/FilterResourceFilter.h b/tools/include/FilterResourceFilter.h index eb4a0a4..a71f3aa 100644 --- a/tools/include/FilterResourceFilter.h +++ b/tools/include/FilterResourceFilter.h @@ -9,56 +9,38 @@ namespace ResourceTools { -/// @brief Class representing a resource filter with include and exclude filters. -/// @note This can be for the filter attribute of a NamedSection OR the optional filter for a respaths/resfile line (defined in FilterResourceLine). -/// see CarbonResources::FilterNamedSection and CarbonResources::FilterResourceLine for usage. +// Class representing a resource filter with include and exclude filters. +// - This can be for the filter attribute of a NamedSection OR the optional filter for a respaths/resfile line (defined in FilterResourceLine). +// - see ResourceTools::FilterNamedSection and ResourceTools::FilterResourceLine class FilterResourceFilter { public: - /// @brief Constructor that takes a raw filter string and parses it into include and exclude filters. - /// @param rawFilter the raw filter string to parse. - /// @note Calls ParseFilters() which may throw std::invalid_argument if the filter string is malformed. - /// @see CarbonResources::FilterResourceFilter::ParseFilters + // Constructor that takes a raw filter string and parses it into include and exclude filters. explicit FilterResourceFilter( const std::string& rawFilter ); - /// @brief Gets the raw filter string. - /// @return the raw filter string. + // Gets the raw filter string. const std::string& GetRawFilter() const; - /// @brief Gets the include filter vector. - /// @return the valid include filter as a vector. + // Gets the include filter vector. const std::vector& GetIncludeFilter() const; - /// @brief Gets the exclude filter vector. - /// @return the valid exclude filter as a vector. + // Gets the exclude filter vector. const std::vector& GetExcludeFilter() const; private: - /// @brief The raw filter string. - /// @note Set in the constructor and used in ParseFilters(). + // Raw filter string. std::string m_rawFilter; - /// @brief The include filter vector. - /// @note Populated in ParseFilters(). - /// @see CarbonResources::FilterResourceFilter::ParseFilters + // Include filter vector. std::vector m_includeFilter; - /// @brief The exclude filter vector. - /// @note Populated in ParseFilters(). - /// @see CarbonResources::FilterResourceFilter::ParseFilters + // Exclude filter vector. std::vector m_excludeFilter; - /// @brief Parses the raw filter string into include and exclude filters. - /// @return void - /// @note Throws std::invalid_argument if the filter string is malformed, bubbling the error up to the caller (class constructor). + // Parse raw filter string into vectors of include and exclude filters. void ParseFilters(); - /// @brief Static helper function that places a token in the correct vector, moving it from one vector to another if need be. - /// @param token the token to place in the correct vector. - /// @param fromVector the vector to remove the token from if it exists there. - /// @param toVector the vector to add the token to if it does not already exist there. - /// @see CarbonResources::FilterResourceFilter::ParseFilters for usage. - /// @return void + // Static helper to place a token in the correct vector, moving it from one vector to another, if need be. static void PlaceTokenInCorrectVector( const std::string& token, std::vector& fromVector, std::vector& toVector ); }; diff --git a/tools/include/FilterResourceLine.h b/tools/include/FilterResourceLine.h index 569dd1a..c42d186 100644 --- a/tools/include/FilterResourceLine.h +++ b/tools/include/FilterResourceLine.h @@ -7,46 +7,41 @@ #include "FilterPrefixmap.h" #include "FilterResourceFilter.h" +#include + namespace ResourceTools { -/// @brief Class representing a single line of a resfile/respaths attribute. +// Class representing a single raw line of a resfile/respaths attribute. class FilterResourceLine { public: - /// @brief Constructor that takes a rawLine string, a reference to an already constructed prefixMap and sectionFilter. - /// @param rawLine the raw value of a resfile/respaths line string to parse. - /// @param prefixMap reference to an already constructed FilterPrefixmap object. - /// @param sectionFilter reference to an already constructed FilterResourceFilter object representing the "parent" filter for this section. - /// @note Calls ParseLine() which may throw std::invalid_argument if the rawLine string is malformed. - /// @see CarbonResources::FilterResourceLine::ParseLine - explicit FilterResourceLine( const std::string& rawLine, const FilterPrefixmap& prefixMap, const FilterResourceFilter& sectionFilter ); - - // bool IsValid() const; // TODO: Remove this, probably don't need it. + // Constructor that takes a rawLine string to parse into a linePath vector, based on already constructed prefixMap and an optional sectionFilter. + // - sectionFilter is only optional, in case "resfile" attribute requires it + explicit FilterResourceLine( const std::string& rawLine, const FilterPrefixMap& prefixMap, std::optional sectionFilter ); // TODO: Add a getter function that combines the section filter and optional line filter along with the concatenated prefixMap + respath/resfile value. private: - /// @brief The raw string, representing the value of the resfile/respaths attribute. - /// @note Set in the constructor and used in ParseLine(). + // Raw string, representing the value of the resfile/respaths line attribute. std::string m_rawLine; - /// @brief Reference to an already constructed FilterPrefixmap object. - const FilterPrefixmap& m_prefixMap; + // Reference to an already constructed FilterPrefixMap object. + const FilterPrefixMap& m_prefixMap; - /// @brief Reference to an already constructed FilterResourceFilter object representing the "parent" filter for this section. - const FilterResourceFilter& m_sectionFilter; + // Optional FilterResourceFilter object representing the "parent" filter for this section. + // - optional, in case this is for a "resfile" attribute that MAY NOT have a section filter. + std::optional m_sectionFilter; - // TODO: Decide how to handle optional FilterResourceFilter for the line-specific filter, if any - // Probably add: std::optional m_lineFilter - // Then add a getter function (probably private) that combines the section filter and line filter as needed. + // The optional FilterResourceFilter object representing the line-specific filter, if any. + const std::optional m_lineFilter; - /// @brief The resolved full path after applying the prefix map to the file/path portion of the rawLine. - std::string m_linePath; + // A vector of FilterResourceLineEntries, with two elemants: resolved full paths after applying all relevant prefix maps to the path portion, along with the combined in-order m_sectionFilter + optional m_lineFilter. + // NOTE: FilterResourceLineEntry is a class/struct with a string path and combined FilterResourceFilter) + // Replace this std::vector m_linePath; + // with std::vector m_lineEntry; - /// @brief Parses the rawLine string into its components (m_linePath, optional m_lineFilter). - /// @note Throws std::invalid_argument if parsing fails, bubbling the error up to the caller (class constructor). - /// @return void + // Parses the rawLine string into its components (m_linePath, optional m_lineFilter). void ParseLine(); }; diff --git a/tools/src/FilterDefaultSection.cpp b/tools/src/FilterDefaultSection.cpp index 0181402..8cb7a65 100644 --- a/tools/src/FilterDefaultSection.cpp +++ b/tools/src/FilterDefaultSection.cpp @@ -7,12 +7,12 @@ namespace ResourceTools { FilterDefaultSection::FilterDefaultSection( const std::string& prefixmapStr ) : - m_prefixmap( prefixmapStr ) + m_prefixMap( prefixmapStr ) { throw std::logic_error( "Not implemented yet exception" ); } -const FilterPrefixmap& FilterDefaultSection::GetPrefixmap() const +const FilterPrefixMap& FilterDefaultSection::GetPrefixmap() const { throw std::logic_error( "Not implemented yet exception" ); } diff --git a/tools/src/FilterPrefixmap.cpp b/tools/src/FilterPrefixmap.cpp index ce26061..158e6b3 100644 --- a/tools/src/FilterPrefixmap.cpp +++ b/tools/src/FilterPrefixmap.cpp @@ -6,17 +6,18 @@ namespace ResourceTools { -FilterPrefixmap::FilterPrefixmap( const std::string& prefixmapStr ) +FilterPrefixMap::FilterPrefixMap( const std::string& rawPrefixMap ) : + m_rawPrefixMap( m_rawPrefixMap ) { throw std::logic_error( "Not implemented yet exception" ); } -const std::map>& FilterPrefixmap::GetMap() const +const std::map>& FilterPrefixMap::GetPrefixMap() const { throw std::logic_error( "Not implemented yet exception" ); } -void FilterPrefixmap::Parse( const std::string& prefixmapStr ) +void FilterPrefixMap::ParsePrefixMap() { throw std::logic_error( "Not implemented yet exception" ); } diff --git a/tools/src/FilterResourceLine.cpp b/tools/src/FilterResourceLine.cpp index 1b88a2a..e9f4ea1 100644 --- a/tools/src/FilterResourceLine.cpp +++ b/tools/src/FilterResourceLine.cpp @@ -6,7 +6,7 @@ namespace ResourceTools { -FilterResourceLine::FilterResourceLine( const std::string& rawLine, const FilterPrefixmap& prefixMap, const FilterResourceFilter& sectionFilter ) : +FilterResourceLine::FilterResourceLine( const std::string& rawLine, const FilterPrefixMap& prefixMap, std::optional sectionFilter ) : m_rawLine( rawLine ), m_prefixMap( prefixMap ), m_sectionFilter( sectionFilter ) From 8a68f53f4895077f6f18a2d2176550fa5cf76887 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 12 Dec 2025 16:46:52 +0000 Subject: [PATCH 018/124] Add FilterPrefixMap and Entry classes Next step, add missing tests. --- tools/CMakeLists.txt | 2 + tools/include/FilterPrefixMapEntry.h | 38 +++++++++++++++++ tools/include/FilterPrefixmap.h | 13 +++--- tools/src/FilterPrefixMapEntry.cpp | 48 +++++++++++++++++++++ tools/src/FilterPrefixmap.cpp | 63 ++++++++++++++++++++++++---- 5 files changed, 147 insertions(+), 17 deletions(-) create mode 100644 tools/include/FilterPrefixMapEntry.h create mode 100644 tools/src/FilterPrefixMapEntry.cpp diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 1817df5..66798dc 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -26,6 +26,7 @@ set(SRC_FILES include/FilterPrefixMap.h include/FilterResourceFilter.h include/FilterResourceLine.h + include/FilterPrefixMapEntry.h src/BundleStreamIn.cpp src/BundleStreamOut.cpp @@ -47,6 +48,7 @@ set(SRC_FILES src/FilterPrefixMap.cpp src/FilterResourceFilter.cpp src/FilterResourceLine.cpp + src/FilterPrefixMapEntry.cpp ) add_library(resources-tools STATIC ${SRC_FILES}) diff --git a/tools/include/FilterPrefixMapEntry.h b/tools/include/FilterPrefixMapEntry.h new file mode 100644 index 0000000..016c8e0 --- /dev/null +++ b/tools/include/FilterPrefixMapEntry.h @@ -0,0 +1,38 @@ +// Copyright © 2025 CCP ehf. + +#ifndef FILTERPREFIXMAPENTRY_H +#define FILTERPREFIXMAPENTRY_H + +#include +#include + +namespace ResourceTools +{ + +// Class representing all (one or more) path entries for a given prefix identifier. +class FilterPrefixMapEntry +{ +public: + // Constructor that takes a prefix identifier and a raw paths string (semicolon-separated). + explicit FilterPrefixMapEntry( const std::string& prefix, const std::string& rawPaths ); + + // Parse rawPaths and appends to existing paths if needed. + void AppendPaths( const std::string& prefix, const std::string& rawPaths ); + + // Gets the prefix identifier. + const std::string& GetPrefix() const; + + // Gets the set of paths. + const std::set& GetPaths() const; + +private: + // The prefix identifier. + std::string m_prefix; + + // The set of parsed paths, sorted. + std::set m_paths; +}; + +} + +#endif // FILTERPREFIXMAPENTRY_H diff --git a/tools/include/FilterPrefixmap.h b/tools/include/FilterPrefixmap.h index bd619f4..10a3089 100644 --- a/tools/include/FilterPrefixmap.h +++ b/tools/include/FilterPrefixmap.h @@ -6,6 +6,7 @@ #include #include #include +#include "FilterPrefixMapEntry.h" namespace ResourceTools { @@ -19,17 +20,13 @@ class FilterPrefixMap explicit FilterPrefixMap( const std::string& rawPrefixMap ); // Gets the prefix map. - const std::map>& GetPrefixMap() const; + const std::map GetPrefixMap() const; private: - // Raw filter string. - std::string m_rawPrefixMap; + // Map of prefixes to FilterPrefixMapEntry objects. + std::map m_prefixMap; - // Map of prefixes to list of paths. - // e.g. "res:" -> { "/dir1", "/dir2" } and "res2:" -> { "/otherDir1" } - std::map> m_prefixMap; - - void ParsePrefixMap(); + void ParsePrefixMap( const std::string& rawPrefixMap ); }; } diff --git a/tools/src/FilterPrefixMapEntry.cpp b/tools/src/FilterPrefixMapEntry.cpp new file mode 100644 index 0000000..5a110ad --- /dev/null +++ b/tools/src/FilterPrefixMapEntry.cpp @@ -0,0 +1,48 @@ +// Copyright © 2025 CCP ehf. + +#include "FilterPrefixMapEntry.h" + +namespace ResourceTools +{ + +FilterPrefixMapEntry::FilterPrefixMapEntry( const std::string& prefix, const std::string& rawPaths ) : + m_prefix( prefix ) +{ + m_paths.clear(); + + AppendPaths( prefix, rawPaths ); +} + +void FilterPrefixMapEntry::AppendPaths( const std::string& prefix, const std::string& rawPaths ) +{ + if( prefix != m_prefix ) + throw std::invalid_argument( "Prefix mismatch while appending: " + prefix + "(incoming) != " + m_prefix + "(existing)" ); + + std::size_t pos = 0; + while( pos < rawPaths.size() ) + { + // Split the string up by semicolons (in case of multiple paths) + std::size_t semicolon = rawPaths.find( ';', pos ); + std::string path = ( semicolon == std::string::npos ) ? rawPaths.substr( pos ) : rawPaths.substr( pos, semicolon - pos ); + if( !path.empty() ) + m_paths.insert( path ); + + if( semicolon == std::string::npos ) + break; + pos = semicolon + 1; + } + if( m_paths.empty() ) + throw std::invalid_argument( "Invalid prefixmap format: No paths associated with prefix: " + m_prefix ); +} + +const std::string& FilterPrefixMapEntry::GetPrefix() const +{ + return m_prefix; +} + +const std::set& FilterPrefixMapEntry::GetPaths() const +{ + return m_paths; +} + +} diff --git a/tools/src/FilterPrefixmap.cpp b/tools/src/FilterPrefixmap.cpp index 158e6b3..bcf10a5 100644 --- a/tools/src/FilterPrefixmap.cpp +++ b/tools/src/FilterPrefixmap.cpp @@ -1,25 +1,70 @@ // Copyright © 2025 CCP ehf. +#include "FilterPrefixMap.h" +#include "FilterPrefixMapEntry.h" #include -#include +#include namespace ResourceTools { -FilterPrefixMap::FilterPrefixMap( const std::string& rawPrefixMap ) : - m_rawPrefixMap( m_rawPrefixMap ) +FilterPrefixMap::FilterPrefixMap( const std::string& rawPrefixMap ) { - throw std::logic_error( "Not implemented yet exception" ); + m_prefixMap.clear(); + + ParsePrefixMap( rawPrefixMap ); } -const std::map>& FilterPrefixMap::GetPrefixMap() const +const std::map FilterPrefixMap::GetPrefixMap() const { - throw std::logic_error( "Not implemented yet exception" ); + return m_prefixMap; } -void FilterPrefixMap::ParsePrefixMap() +void FilterPrefixMap::ParsePrefixMap( const std::string& rawPrefixMap ) { - throw std::logic_error( "Not implemented yet exception" ); -} + std::size_t pos = 0; + while( pos < rawPrefixMap.size() ) + { + // Find the prefix (or error out if missing a colon) + std::size_t colon = rawPrefixMap.find( ':', pos ); + if( colon == std::string::npos ) + throw std::invalid_argument( "Invalid prefixmap format: missing ':'" ); + + std::string prefix = rawPrefixMap.substr( pos, colon - pos ); + if( prefix.empty() ) + throw std::invalid_argument( "Invalid prefixmap format: empty prefix" ); + + // Move position past the colon + pos = colon + 1; + + // Find end of paths (next whitespace or end of string) + std::size_t nextSpace = rawPrefixMap.find_first_not_of( " \t\r\n", pos ); + std::string rawPaths = ( nextSpace == std::string::npos ) ? rawPrefixMap.substr( pos ) : rawPrefixMap.substr( pos, nextSpace - pos ); + if( rawPaths.empty() ) + throw std::invalid_argument( "Invalid prefixmap format: No paths defined for prefix: " + prefix ); + + auto it = m_prefixMap.find( prefix ); + if( it == m_prefixMap.end() ) + { + // Prefix doesn't exist, create a map entry for it. + m_prefixMap.emplace( prefix, FilterPrefixMapEntry( prefix, rawPaths ) ); + } + else + { + // The same prefix has been found again, appending paths to the existing entry + it->second.AppendPaths( prefix, rawPaths ); + } + + // Go to the next token in the rawPrefixMap (or break if at end) + if( nextSpace == std::string::npos ) + break; + pos = nextSpace + 1; + + // There was a whitespace, skip any additional spaces as well + while( pos < rawPrefixMap.size() && std::isspace( static_cast( rawPrefixMap[pos] ) ) ) + ++pos; + } } + +} \ No newline at end of file From e90027bdfb42612eae70e6aca880c171dd758173 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 15 Dec 2025 13:07:35 +0000 Subject: [PATCH 019/124] Add tests for FilterPrefixMap and Entry --- tests/src/ResourceFilterTest.cpp | 199 ++++++++++++++++++++++++++- tools/include/FilterPrefixMapEntry.h | 6 +- tools/include/FilterPrefixmap.h | 4 +- tools/src/FilterPrefixMapEntry.cpp | 4 +- tools/src/FilterPrefixmap.cpp | 3 +- 5 files changed, 204 insertions(+), 12 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 2c05904..b591bfe 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -3,6 +3,8 @@ #include "ResourceFilterTest.h" #include #include +#include +#include TEST_F( ResourceFilterTest, Example1IniParsing ) { @@ -24,13 +26,15 @@ TEST_F( ResourceFilterTest, Example1IniParsing ) ASSERT_TRUE( reader.HasSection( "testYamlFilesOverMultiLineResPathsWithEmptyLines" ) ); EXPECT_EQ( reader.Get( "testYamlFilesOverMultiLineResPathsWithEmptyLines", "filter", "" ), "[ .yaml ]" ); EXPECT_EQ( reader.Get( "testYamlFilesOverMultiLineResPathsWithEmptyLines", "resfile", "" ), "res:/binaryFileIndex_v0_0_0.txt" ); - auto respathValueGet = reader.Get( "testYamlFilesOverMultiLineResPathsWithEmptyLines", "respaths", "" ); + auto respathValueGet = reader.Get( "testYamlFilesOverMultiLineResPathsWithEmptyLines", "respaths", "" ); std::string respathValueGetString = reader.Get( "testYamlFilesOverMultiLineResPathsWithEmptyLines", "respaths", "" ); EXPECT_EQ( respathValueGet, respathValueGetString ); EXPECT_EQ( respathValueGet, "res:/firstLine/...\nres:/secondLine/...\nres2:/thirdLine/..." ); // Note: Under the hood, INIReader converts multi-empty-lines to a single \n line breaks EXPECT_EQ( reader.Keys( "testYamlFilesOverMultiLineResPathsWithEmptyLines" ).size(), 3 ); } +// ----------------------------------------- + TEST_F( ResourceFilterTest, OnlyIncludeFilter ) { ResourceTools::FilterResourceFilter filter( "[ .this .is .included ]" ); @@ -271,4 +275,197 @@ TEST_F( ResourceFilterTest, CondensedValidFilterStringv2 ) std::vector expectedExcludes = { "exToken1", "exToken2" }; EXPECT_EQ( includes, expectedIncludes ); EXPECT_EQ( excludes, expectedExcludes ); +} + +// ----------------------------------------- + +TEST_F( ResourceFilterTest, SinglePrefixMultiplePaths ) +{ + ResourceTools::FilterPrefixMap map( "prefix1:/somePath;../otherPath" ); + const auto& prefixMap = map.GetPrefixMap(); + ASSERT_EQ( prefixMap.size(), 1 ) << "There should only be 1 prefix in the map"; + + // If iterator is at end, the prefix was not found + auto it = prefixMap.find( "prefix1" ); + ASSERT_NE( it, prefixMap.end() ) << "Prefix 'prefix1' not found in the map"; + + std::set expected = { "/somePath", "../otherPath" }; + EXPECT_EQ( it->second.GetPrefix(), "prefix1" ) << "Prefix should be 'prefix1'"; + EXPECT_EQ( it->first, it->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; + EXPECT_EQ( it->second.GetPaths(), expected ) << "Paths do not match expected values"; +} + +TEST_F( ResourceFilterTest, MultiplePrefixes ) +{ + ResourceTools::FilterPrefixMap map( "prefix1:/path1;/path2 prefix2:/newPath1" ); + const auto& prefixMap = map.GetPrefixMap(); + ASSERT_EQ( prefixMap.size(), 2 ) << "There should be 2 prefixes in the map"; + + // Make sure both prefixes exist + auto it1 = prefixMap.find( "prefix1" ); + auto it2 = prefixMap.find( "prefix2" ); + ASSERT_NE( it1, prefixMap.end() ) << "Prefix 'prefix1' not found in the map"; + ASSERT_NE( it2, prefixMap.end() ) << "Prefix 'prefix2' not found in the map"; + + std::set expected1 = { "/path1", "/path2" }; + std::set expected2 = { "/newPath1" }; + EXPECT_EQ( it1->second.GetPrefix(), "prefix1" ) << "Prefix should be 'prefix1'"; + EXPECT_EQ( it2->second.GetPrefix(), "prefix2" ) << "Prefix should be 'prefix2'"; + EXPECT_EQ( it1->first, it1->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; + EXPECT_EQ( it2->first, it2->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; + EXPECT_EQ( it1->second.GetPaths(), expected1 ) << "Paths do not match expected values"; + EXPECT_EQ( it2->second.GetPaths(), expected2 ) << "Paths do not match expected values"; +} + +TEST_F( ResourceFilterTest, DuplicateSamePrefixPathsInDifferentOrder ) +{ + ResourceTools::FilterPrefixMap map( "prefix1:/path1;/path2 prefix1:/path2;/path1" ); + + // There should only be one prefix (prefix1) + const auto& prefixMap = map.GetPrefixMap(); + ASSERT_EQ( prefixMap.size(), 1 ) << "There should only be 1 prefix in the map"; + auto it = prefixMap.find( "prefix1" ); + ASSERT_NE( it, prefixMap.end() ) << "Prefix 'prefix1' not found in the map"; + + // There should be only 2 paths, sorted in set + std::set expected_a = { "/path1", "/path2" }; + std::set expected_b = { "/path2", "/path1" }; + EXPECT_EQ( it->second.GetPrefix(), "prefix1" ) << "Prefix should be 'prefix1'"; + EXPECT_EQ( it->second.GetPaths(), expected_a ) << "Paths do not match expected values - inserted in alphabetical order"; + EXPECT_EQ( it->second.GetPaths(), expected_b ) << "Paths do not match expected values - inserted in reverse alphabetical order"; + EXPECT_EQ( it->first, it->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; +} + +TEST_F( ResourceFilterTest, MultiplePrefixesAppendToPaths ) +{ + ResourceTools::FilterPrefixMap map( "prefix1:/path2;/path1 prefix2:/otherPath1;/otherPath2 prefix1:/path3;/path1" ); + const auto& prefixMap = map.GetPrefixMap(); + ASSERT_EQ( prefixMap.size(), 2 ) << "There should be 2 prefixes in the map"; + auto it1 = prefixMap.find( "prefix1" ); + auto it2 = prefixMap.find( "prefix2" ); + ASSERT_NE( it1, prefixMap.end() ) << "Prefix 'prefix1' not found in the map"; + ASSERT_NE( it2, prefixMap.end() ) << "Prefix 'prefix2' not found in the map"; + + // Prefix1 should have 3 paths and prefix2 should have 2 + std::set prefix1Paths = { "/path1", "/path2", "/path3" }; + std::set prefix2Paths = { "/otherPath1", "/otherPath2" }; + EXPECT_EQ( it1->second.GetPrefix(), "prefix1" ) << "Prefix should be 'prefix1'"; + EXPECT_EQ( it2->second.GetPrefix(), "prefix2" ) << "Prefix should be 'prefix2'"; + EXPECT_EQ( it1->second.GetPaths(), prefix1Paths ) << "Paths do not match expected values"; + EXPECT_EQ( it2->second.GetPaths(), prefix2Paths ) << "Paths do not match expected values"; + EXPECT_EQ( it1->first, it1->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; + EXPECT_EQ( it2->first, it2->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; +} + +TEST_F( ResourceFilterTest, DifferentWhitespacesBetweenPrefixes ) +{ + std::string input = "prefix1:/path1\tprefixTab:/path2\nprefixNewLine:/path3"; + ResourceTools::FilterPrefixMap map( input ); + const auto& prefixMap = map.GetPrefixMap(); + ASSERT_EQ( prefixMap.size(), 3 ) << "There should only be 3 prefix in the map"; + auto it1 = prefixMap.find( "prefix1" ); + auto it2 = prefixMap.find( "prefixTab" ); + auto it3 = prefixMap.find( "prefixNewLine" ); + EXPECT_NE( it1, prefixMap.end() ) << "Prefix 'prefix1' not found in the map"; + EXPECT_NE( it2, prefixMap.end() ) << "Prefix 'prefixTab' not found in the map"; + EXPECT_NE( it3, prefixMap.end() ) << "Prefix 'prefixNewLine' not found in the map"; + + std::set prefix1Paths = { "/path1" }; + std::set prefixTabPaths = { "/path2" }; + std::set prefixNewLinePaths = { "/path3" }; + EXPECT_EQ( it1->second.GetPrefix(), "prefix1" ) << "Prefix should be 'prefix1'"; + EXPECT_EQ( it2->second.GetPrefix(), "prefixTab" ) << "Prefix should be 'prefixTab'"; + EXPECT_EQ( it3->second.GetPrefix(), "prefixNewLine" ) << "Prefix should be 'prefixNewLine'"; + EXPECT_EQ( it1->second.GetPaths(), prefix1Paths ) << "Paths do not match expected values"; + EXPECT_EQ( it2->second.GetPaths(), prefixTabPaths ) << "Paths do not match expected values"; + EXPECT_EQ( it3->second.GetPaths(), prefixNewLinePaths ) << "Paths do not match expected values"; + EXPECT_EQ( it1->first, it1->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; + EXPECT_EQ( it2->first, it2->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; + EXPECT_EQ( it3->first, it3->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; +} + +TEST_F( ResourceFilterTest, ParsePrefixMap_InvalidMissingColon ) +{ + try + { + ResourceTools::FilterPrefixMap prefixmap( "prefix1/path1" ); + FAIL() << "Expected std::invalid_argument (1)"; + } + catch( const std::invalid_argument& e ) + { + EXPECT_STREQ( e.what(), "Invalid prefixmap format: missing ':'" ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument (2)"; + } +} + +TEST_F( ResourceFilterTest, ParsePrefixMap_InvalidEmptyPrefix ) +{ + try + { + ResourceTools::FilterPrefixMap prefixmap( ":/path1" ); + FAIL() << "Expected std::invalid_argument (1)"; + } + catch( const std::invalid_argument& e ) + { + EXPECT_STREQ( e.what(), "Invalid prefixmap format: empty prefix" ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument (2)"; + } +} + +TEST_F( ResourceFilterTest, ParsePrefixMap_InvalidNoPaths ) +{ + try + { + ResourceTools::FilterPrefixMap prefixmap( "prefix1:" ); + FAIL() << "Expected std::invalid_argument (1)"; + } + catch( const std::invalid_argument& e ) + { + EXPECT_STREQ( e.what(), "Invalid prefixmap format: No paths defined for prefix: prefix1" ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument (2)"; + } +} + +TEST_F ( ResourceFilterTest, FilterPrefixMapEntry_PrefixMismatchOnAppend ) +{ + try + { + ResourceTools::FilterPrefixMapEntry entry( "prefix1", "/path1" ); + entry.AppendPaths( "prefix2", "/path2" ); + FAIL() << "Expected std::invalid_argument (1)"; + } + catch( const std::invalid_argument& e ) + { + EXPECT_STREQ( e.what(), "Prefix mismatch while appending path(s): prefix2 (incoming) != prefix1 (existing)" ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument (2)"; + } +} + +TEST_F ( ResourceFilterTest, FilterPrefixMapEntry_InvalidNoPathsOnAppend ) +{ + try + { + ResourceTools::FilterPrefixMapEntry entry( "prefix1", "" ); // Empty string for paths + FAIL() << "Expected std::invalid_argument (1)"; + } + catch( const std::invalid_argument& e ) + { + EXPECT_STREQ( e.what(), "Invalid prefixmap format: No paths appended for prefix: prefix1" ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument (2)"; + } } \ No newline at end of file diff --git a/tools/include/FilterPrefixMapEntry.h b/tools/include/FilterPrefixMapEntry.h index 016c8e0..ee04cba 100644 --- a/tools/include/FilterPrefixMapEntry.h +++ b/tools/include/FilterPrefixMapEntry.h @@ -13,23 +13,19 @@ namespace ResourceTools class FilterPrefixMapEntry { public: - // Constructor that takes a prefix identifier and a raw paths string (semicolon-separated). explicit FilterPrefixMapEntry( const std::string& prefix, const std::string& rawPaths ); // Parse rawPaths and appends to existing paths if needed. void AppendPaths( const std::string& prefix, const std::string& rawPaths ); - // Gets the prefix identifier. const std::string& GetPrefix() const; - // Gets the set of paths. const std::set& GetPaths() const; private: - // The prefix identifier. std::string m_prefix; - // The set of parsed paths, sorted. + // The set of parsed paths (sorted). std::set m_paths; }; diff --git a/tools/include/FilterPrefixmap.h b/tools/include/FilterPrefixmap.h index 10a3089..a26474a 100644 --- a/tools/include/FilterPrefixmap.h +++ b/tools/include/FilterPrefixmap.h @@ -6,20 +6,18 @@ #include #include #include + #include "FilterPrefixMapEntry.h" namespace ResourceTools { // Class representing the prefixmap attribute. -// - used to resolve actual resfile/respaths attributes of a NamedSection. class FilterPrefixMap { public: - // Constructor that takes a raw filter string and parses it into a map of prefixes to list of paths. explicit FilterPrefixMap( const std::string& rawPrefixMap ); - // Gets the prefix map. const std::map GetPrefixMap() const; private: diff --git a/tools/src/FilterPrefixMapEntry.cpp b/tools/src/FilterPrefixMapEntry.cpp index 5a110ad..b636dcc 100644 --- a/tools/src/FilterPrefixMapEntry.cpp +++ b/tools/src/FilterPrefixMapEntry.cpp @@ -16,7 +16,7 @@ FilterPrefixMapEntry::FilterPrefixMapEntry( const std::string& prefix, const std void FilterPrefixMapEntry::AppendPaths( const std::string& prefix, const std::string& rawPaths ) { if( prefix != m_prefix ) - throw std::invalid_argument( "Prefix mismatch while appending: " + prefix + "(incoming) != " + m_prefix + "(existing)" ); + throw std::invalid_argument( "Prefix mismatch while appending path(s): " + prefix + " (incoming) != " + m_prefix + " (existing)" ); std::size_t pos = 0; while( pos < rawPaths.size() ) @@ -32,7 +32,7 @@ void FilterPrefixMapEntry::AppendPaths( const std::string& prefix, const std::st pos = semicolon + 1; } if( m_paths.empty() ) - throw std::invalid_argument( "Invalid prefixmap format: No paths associated with prefix: " + m_prefix ); + throw std::invalid_argument( "Invalid prefixmap format: No paths appended for prefix: " + m_prefix ); } const std::string& FilterPrefixMapEntry::GetPrefix() const diff --git a/tools/src/FilterPrefixmap.cpp b/tools/src/FilterPrefixmap.cpp index bcf10a5..1ac4f3b 100644 --- a/tools/src/FilterPrefixmap.cpp +++ b/tools/src/FilterPrefixmap.cpp @@ -2,6 +2,7 @@ #include "FilterPrefixMap.h" #include "FilterPrefixMapEntry.h" + #include #include @@ -38,7 +39,7 @@ void FilterPrefixMap::ParsePrefixMap( const std::string& rawPrefixMap ) pos = colon + 1; // Find end of paths (next whitespace or end of string) - std::size_t nextSpace = rawPrefixMap.find_first_not_of( " \t\r\n", pos ); + std::size_t nextSpace = rawPrefixMap.find_first_of( " \t\r\n", pos ); std::string rawPaths = ( nextSpace == std::string::npos ) ? rawPrefixMap.substr( pos ) : rawPrefixMap.substr( pos, nextSpace - pos ); if( rawPaths.empty() ) From cd967c2e5c907f3f8bcaba6aece8a65d3f972395 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 15 Dec 2025 14:04:25 +0000 Subject: [PATCH 020/124] Include and function signature cleanup --- tools/include/FilterDefaultSection.h | 6 ++++-- tools/include/FilterNamedSection.h | 4 ++-- tools/include/FilterPrefixmap.h | 5 ++--- tools/include/FilterResourceFile.h | 4 ++-- tools/include/FilterResourceLine.h | 5 ++--- tools/src/FilterDefaultSection.cpp | 6 +++--- tools/src/FilterPrefixMapEntry.cpp | 2 +- tools/src/FilterPrefixmap.cpp | 7 +++---- tools/src/FilterResourceFilter.cpp | 2 -- 9 files changed, 19 insertions(+), 22 deletions(-) diff --git a/tools/include/FilterDefaultSection.h b/tools/include/FilterDefaultSection.h index a3c10e5..c2c099f 100644 --- a/tools/include/FilterDefaultSection.h +++ b/tools/include/FilterDefaultSection.h @@ -3,7 +3,8 @@ #ifndef FILTERDEFAULTSECTION_H #define FILTERDEFAULTSECTION_H -#include "FilterPrefixmap.h" +#include +#include namespace ResourceTools { @@ -13,7 +14,8 @@ class FilterDefaultSection public: explicit FilterDefaultSection( const std::string& prefixmapStr ); - const FilterPrefixMap& GetPrefixmap() const; + //const FilterPrefixMap& GetPrefixmap() const; + const std::map& GetPrefixMap() const; private: FilterPrefixMap m_prefixMap; diff --git a/tools/include/FilterNamedSection.h b/tools/include/FilterNamedSection.h index 34480d4..95631ba 100644 --- a/tools/include/FilterNamedSection.h +++ b/tools/include/FilterNamedSection.h @@ -5,8 +5,8 @@ #include #include -#include "FilterResourceFilter.h" -#include "FilterResourceLine.h" +#include +#include namespace ResourceTools { diff --git a/tools/include/FilterPrefixmap.h b/tools/include/FilterPrefixmap.h index a26474a..6836b16 100644 --- a/tools/include/FilterPrefixmap.h +++ b/tools/include/FilterPrefixmap.h @@ -6,8 +6,7 @@ #include #include #include - -#include "FilterPrefixMapEntry.h" +#include namespace ResourceTools { @@ -18,7 +17,7 @@ class FilterPrefixMap public: explicit FilterPrefixMap( const std::string& rawPrefixMap ); - const std::map GetPrefixMap() const; + const std::map& GetPrefixMap() const; private: // Map of prefixes to FilterPrefixMapEntry objects. diff --git a/tools/include/FilterResourceFile.h b/tools/include/FilterResourceFile.h index 1756665..b598527 100644 --- a/tools/include/FilterResourceFile.h +++ b/tools/include/FilterResourceFile.h @@ -4,8 +4,8 @@ #define FILTERRESOURCEFILE_H #include -#include "FilterDefaultSection.h" -#include "FilterNamedSection.h" +#include +#include namespace ResourceTools { diff --git a/tools/include/FilterResourceLine.h b/tools/include/FilterResourceLine.h index c42d186..bd565e8 100644 --- a/tools/include/FilterResourceLine.h +++ b/tools/include/FilterResourceLine.h @@ -4,10 +4,9 @@ #define FILTERRESOURCELINE_H #include -#include "FilterPrefixmap.h" -#include "FilterResourceFilter.h" - #include +#include +#include namespace ResourceTools { diff --git a/tools/src/FilterDefaultSection.cpp b/tools/src/FilterDefaultSection.cpp index 8cb7a65..7e8f7a9 100644 --- a/tools/src/FilterDefaultSection.cpp +++ b/tools/src/FilterDefaultSection.cpp @@ -9,12 +9,12 @@ namespace ResourceTools FilterDefaultSection::FilterDefaultSection( const std::string& prefixmapStr ) : m_prefixMap( prefixmapStr ) { - throw std::logic_error( "Not implemented yet exception" ); } -const FilterPrefixMap& FilterDefaultSection::GetPrefixmap() const +const std::map& FilterDefaultSection::GetPrefixMap() const { - throw std::logic_error( "Not implemented yet exception" ); + return m_prefixMap.GetPrefixMap(); } + } diff --git a/tools/src/FilterPrefixMapEntry.cpp b/tools/src/FilterPrefixMapEntry.cpp index b636dcc..4688b32 100644 --- a/tools/src/FilterPrefixMapEntry.cpp +++ b/tools/src/FilterPrefixMapEntry.cpp @@ -1,6 +1,6 @@ // Copyright © 2025 CCP ehf. -#include "FilterPrefixMapEntry.h" +#include namespace ResourceTools { diff --git a/tools/src/FilterPrefixmap.cpp b/tools/src/FilterPrefixmap.cpp index 1ac4f3b..cc76fa5 100644 --- a/tools/src/FilterPrefixmap.cpp +++ b/tools/src/FilterPrefixmap.cpp @@ -1,8 +1,7 @@ // Copyright © 2025 CCP ehf. -#include "FilterPrefixMap.h" -#include "FilterPrefixMapEntry.h" - +#include +#include #include #include @@ -16,7 +15,7 @@ FilterPrefixMap::FilterPrefixMap( const std::string& rawPrefixMap ) ParsePrefixMap( rawPrefixMap ); } -const std::map FilterPrefixMap::GetPrefixMap() const +const std::map& FilterPrefixMap::GetPrefixMap() const { return m_prefixMap; } diff --git a/tools/src/FilterResourceFilter.cpp b/tools/src/FilterResourceFilter.cpp index 18d2c11..c2809eb 100644 --- a/tools/src/FilterResourceFilter.cpp +++ b/tools/src/FilterResourceFilter.cpp @@ -1,7 +1,5 @@ // Copyright © 2025 CCP ehf. -//#include -//#include #include #include #include From a84ebd92fdb61803c996236f4894a77da8827c95 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 15 Dec 2025 14:16:04 +0000 Subject: [PATCH 021/124] Add tests for class FilterDefaultSection --- tests/src/ResourceFilterTest.cpp | 59 ++++++++++++++++++++++++++++ tools/include/FilterDefaultSection.h | 1 - 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index b591bfe..fefd7cf 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -1,10 +1,12 @@ // Copyright © 2025 CCP ehf. #include "ResourceFilterTest.h" + #include #include #include #include +#include TEST_F( ResourceFilterTest, Example1IniParsing ) { @@ -468,4 +470,61 @@ TEST_F ( ResourceFilterTest, FilterPrefixMapEntry_InvalidNoPathsOnAppend ) { FAIL() << "Expected std::invalid_argument (2)"; } +} + +// ----------------------------------------- + +TEST_F ( ResourceFilterTest, FilterDefaultSection_InitializeValid ) +{ + std::string input = "prefix1:/path1;../path2 prefix2:/path3"; + ResourceTools::FilterDefaultSection defaultSection( input ); + const auto& prefixMap = defaultSection.GetPrefixMap(); + ASSERT_EQ( prefixMap.size(), 2 ) << "There should be 2 prefixes in the map"; + auto it1 = prefixMap.find( "prefix1" ); + auto it2 = prefixMap.find( "prefix2" ); + ASSERT_NE( it1, prefixMap.end() ) << "Prefix 'prefix1' not found in the map"; + ASSERT_NE( it2, prefixMap.end() ) << "Prefix 'prefix2' not found in the map"; + + std::set prefix1Paths = { "/path1", "../path2" }; + std::set prefix2Paths = { "/path3" }; + EXPECT_EQ( it1->second.GetPrefix(), "prefix1" ) << "Prefix should be 'prefix1'"; + EXPECT_EQ( it2->second.GetPrefix(), "prefix2" ) << "Prefix should be 'prefix2'"; + EXPECT_EQ( it1->second.GetPaths(), prefix1Paths ) << "Paths do not match expected values"; + EXPECT_EQ( it2->second.GetPaths(), prefix2Paths ) << "Paths do not match expected values"; + EXPECT_EQ( it1->first, it1->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; + EXPECT_EQ( it2->first, it2->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; +} + +TEST_F( ResourceFilterTest, FilterDefaultSection_InitializeInvalidMissingColon ) +{ + try + { + ResourceTools::FilterDefaultSection defaultSection( "prefix1/path1" ); + FAIL() << "Expected std::invalid_argument (1)"; + } + catch( const std::invalid_argument& e ) + { + EXPECT_STREQ( e.what(), "Invalid prefixmap format: missing ':'" ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument (2)"; + } +} + +TEST_F( ResourceFilterTest, FilterDefaultSection_InitializeInvalidEmptyPrefix ) +{ + try + { + ResourceTools::FilterDefaultSection defaultSection( ":/path1" ); + FAIL() << "Expected std::invalid_argument (1)"; + } + catch( const std::invalid_argument& e ) + { + EXPECT_STREQ( e.what(), "Invalid prefixmap format: empty prefix" ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument (2)"; + } } \ No newline at end of file diff --git a/tools/include/FilterDefaultSection.h b/tools/include/FilterDefaultSection.h index c2c099f..e8a313d 100644 --- a/tools/include/FilterDefaultSection.h +++ b/tools/include/FilterDefaultSection.h @@ -14,7 +14,6 @@ class FilterDefaultSection public: explicit FilterDefaultSection( const std::string& prefixmapStr ); - //const FilterPrefixMap& GetPrefixmap() const; const std::map& GetPrefixMap() const; private: From 12e4d7468512895d4746a59d3b2bf9f4c1f282d0 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 8 Jan 2026 17:17:30 +0000 Subject: [PATCH 022/124] Fixup NamedSection and related classes. --- tests/src/ResourceFilterTest.cpp | 184 +++++++++++++++++++- tools/CMakeLists.txt | 6 +- tools/include/FilterNamedSection.h | 37 +++- tools/include/FilterPrefixmap.h | 2 + tools/include/FilterResourceFile.h | 1 + tools/include/FilterResourceFilter.h | 16 +- tools/include/FilterResourceLine.h | 49 ------ tools/include/FilterResourcePathFile.h | 41 +++++ tools/include/FilterResourcePathFileEntry.h | 41 +++++ tools/src/FilterNamedSection.cpp | 85 ++++++++- tools/src/FilterResourceLine.cpp | 28 --- tools/src/FilterResourcePathFile.cpp | 56 ++++++ tools/src/FilterResourcePathFileEntry.cpp | 96 ++++++++++ 13 files changed, 537 insertions(+), 105 deletions(-) delete mode 100644 tools/include/FilterResourceLine.h create mode 100644 tools/include/FilterResourcePathFile.h create mode 100644 tools/include/FilterResourcePathFileEntry.h delete mode 100644 tools/src/FilterResourceLine.cpp create mode 100644 tools/src/FilterResourcePathFile.cpp create mode 100644 tools/src/FilterResourcePathFileEntry.cpp diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index fefd7cf..d46e707 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -7,6 +7,7 @@ #include #include #include +#include TEST_F( ResourceFilterTest, Example1IniParsing ) { @@ -437,7 +438,7 @@ TEST_F( ResourceFilterTest, ParsePrefixMap_InvalidNoPaths ) } } -TEST_F ( ResourceFilterTest, FilterPrefixMapEntry_PrefixMismatchOnAppend ) +TEST_F( ResourceFilterTest, FilterPrefixMapEntry_PrefixMismatchOnAppend ) { try { @@ -455,11 +456,11 @@ TEST_F ( ResourceFilterTest, FilterPrefixMapEntry_PrefixMismatchOnAppend ) } } -TEST_F ( ResourceFilterTest, FilterPrefixMapEntry_InvalidNoPathsOnAppend ) +TEST_F( ResourceFilterTest, FilterPrefixMapEntry_InvalidNoPathsOnAppend ) { try { - ResourceTools::FilterPrefixMapEntry entry( "prefix1", "" ); // Empty string for paths + ResourceTools::FilterPrefixMapEntry entry( "prefix1", "" ); // Empty string for paths FAIL() << "Expected std::invalid_argument (1)"; } catch( const std::invalid_argument& e ) @@ -474,7 +475,7 @@ TEST_F ( ResourceFilterTest, FilterPrefixMapEntry_InvalidNoPathsOnAppend ) // ----------------------------------------- -TEST_F ( ResourceFilterTest, FilterDefaultSection_InitializeValid ) +TEST_F( ResourceFilterTest, FilterDefaultSection_InitializeValid ) { std::string input = "prefix1:/path1;../path2 prefix2:/path3"; ResourceTools::FilterDefaultSection defaultSection( input ); @@ -527,4 +528,177 @@ TEST_F( ResourceFilterTest, FilterDefaultSection_InitializeInvalidEmptyPrefix ) { FAIL() << "Expected std::invalid_argument (2)"; } -} \ No newline at end of file +} + +// ----------------------------------------- + +TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_NoFilter ) +{ + std::string prefixMapStr = "prefix1:/path1;../path2"; + std::string parentFilterStr = "[ .in1 .in2 ] ![ .ex1 ]"; + ResourceTools::FilterPrefixMap prefixMap( prefixMapStr ); + ResourceTools::FilterResourceFilter parentFilter( parentFilterStr ); + + std::string rawResPathAttrib = "prefix1:/foo/bar"; + ResourceTools::FilterResourcePathFile pathFile( rawResPathAttrib, prefixMap, parentFilter ); + const auto& resolvedPathMap = pathFile.GetResolvedPathMap(); + + // Check the resolved path and filters against expected + std::set expectedPaths = { "/path1/foo/bar", "../path2/foo/bar" }; + std::vector expectedIncludes = { ".in1", ".in2" }; + std::vector expectedExcludes = { ".ex1" }; + + for( const auto& p : expectedPaths ) + { + EXPECT_TRUE( resolvedPathMap.count( p ) ); + } + + for( const auto& kv : resolvedPathMap ) + { + EXPECT_EQ( kv.second.GetIncludeFilter(), expectedIncludes ); + EXPECT_EQ( kv.second.GetExcludeFilter(), expectedExcludes ); + } +} + +TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_InlineIncludeExclude ) +{ + std::string prefixMapStr = "prefix1:/path1"; + std::string parentFilterStr = "[ .in1 .in2 ] ![ .ex1 ]"; + ResourceTools::FilterPrefixMap prefixMap( prefixMapStr ); + ResourceTools::FilterResourceFilter parentFilter( parentFilterStr ); + + std::string rawPathFileAttrib = "prefix1:/foo/bar [ .inLine1 ] ![ .exLine1 ]"; + ResourceTools::FilterResourcePathFile pathFile( rawPathFileAttrib, prefixMap, parentFilter ); + const auto& resolvedPathMap = pathFile.GetResolvedPathMap(); + + // Check the resolved path and filters against expected + std::set expectedPaths = { "/path1/foo/bar" }; + std::vector expectedIncludes = { ".in1", ".in2", ".inLine1" }; + std::vector expectedExcludes = { ".ex1", ".exLine1" }; + + for( const auto& p : expectedPaths ) + { + EXPECT_TRUE( resolvedPathMap.count( p ) ); + } + + for( const auto& kv : resolvedPathMap ) + { + EXPECT_EQ( kv.second.GetIncludeFilter(), expectedIncludes ); + EXPECT_EQ( kv.second.GetExcludeFilter(), expectedExcludes ); + } +} + +TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_InlineOverridesParentFilter ) +{ + std::string prefixMapStr = "prefix1:/path1;../subPath2;/path3"; + std::string parentFilterStr = "[ .parIn1 .parIn2 ] ![ .parEx1 ]"; + ResourceTools::FilterPrefixMap prefixMap( prefixMapStr ); + ResourceTools::FilterResourceFilter parentFilter( parentFilterStr ); + + // Override the "location" of the parent include filter (.parIn2) and exclude filter (.parEx1), moving them to opposite filter side + std::string rawPathFileAttrib = "prefix1:/foo [ .lineIn1 .parEx1 ] ![ .parIn2 .lineEx1 ]"; + ResourceTools::FilterResourcePathFile pathFile( rawPathFileAttrib, prefixMap, parentFilter ); + const auto& resolvedPathMap = pathFile.GetResolvedPathMap(); + + // Check the resolved path and filters against expected (some filters switched around) + std::set expectedPaths = { "/path1/foo", "../subPath2/foo", "/path3/foo" }; + std::vector expectedIncludes = { ".parIn1", ".lineIn1", ".parEx1" }; + std::vector expectedExcludes = { ".parIn2", ".lineEx1" }; + + for( const auto& p : expectedPaths ) + { + EXPECT_TRUE( resolvedPathMap.count( p ) ); + } + + for( const auto& kv : resolvedPathMap ) + { + EXPECT_EQ( kv.second.GetIncludeFilter(), expectedIncludes ); + EXPECT_EQ( kv.second.GetExcludeFilter(), expectedExcludes ); + } +} + +TEST_F( ResourceFilterTest, FilterResourcePathFile_MultiLine_MixedFiltersWithOverrides ) +{ + std::string prefixMapStr = "prefix1:/path1;../path2 prefix2:/path3"; + std::string parentFilterStr = "[ .parIn1 .parIn2 ] ![ .parEx1 ]"; + ResourceTools::FilterPrefixMap prefixMap( prefixMapStr ); + ResourceTools::FilterResourceFilter parentFilter( parentFilterStr ); + + std::string rawPathFileAttrib = + "prefix1:/firstLine [ .inLine1 ] ![ .parIn1 ]\n" // Add .inLine1 to include and move .parIn1 from include to exclude filter + "prefix2:/secondLine\n" // Keep parent filters unchanged + "prefix1:/thirdLine ![ .exLine3 ] [ .parEx1 ]\n" // Add .exLine3 to exclude filter and move .parEx1 from exclude to include + "prefix2:/fourthLine [ .inLine4 ] ![ .exLine4 ]"; // Add .inLine4 to include and .exLine4 to exclude filter + ResourceTools::FilterResourcePathFile pathFile( rawPathFileAttrib, prefixMap, parentFilter ); + const auto& resolved = pathFile.GetResolvedPathMap(); + + // Check the resolved path and filters against expected (multiple switching of filters and overrides) + std::set expectedPaths = { "/path1/firstLine", "../path2/firstLine", "/path3/secondLine", "/path1/thirdLine", "../path2/thirdLine", "/path3/fourthLine" }; + for( const auto& p : expectedPaths ) + { + EXPECT_TRUE( resolved.count( p ) ); + } + + for( const auto& kv : resolved ) + { + if( kv.first == "/path1/firstLine" || kv.first == "../path2/firstLine" ) + { + // Add .inLine1 to include and move .parIn1 from include to exclude filter + EXPECT_EQ( kv.second.GetIncludeFilter(), std::vector( { ".parIn2", ".inLine1" } ) ); + EXPECT_EQ( kv.second.GetExcludeFilter(), std::vector( { ".parEx1", ".parIn1" } ) ); + } + else if( kv.first == "/path3/secondLine" ) + { + // Keep parent filters unchanged + EXPECT_EQ( kv.second.GetIncludeFilter(), std::vector( { ".parIn1", ".parIn2" } ) ); + EXPECT_EQ( kv.second.GetExcludeFilter(), std::vector( { ".parEx1" } ) ); + } + else if( kv.first == "/path1/thirdLine" || kv.first == "../path2/thirdLine" ) + { + // Add .exLine3 to exclude filter and move .parEx1 from exclude to include + EXPECT_EQ( kv.second.GetIncludeFilter(), std::vector( { ".parIn1", ".parIn2", ".parEx1" } ) ); + EXPECT_EQ( kv.second.GetExcludeFilter(), std::vector( { ".exLine3" } ) ); + } + else if( kv.first == "/path3/fourthLine" ) + { + // Add .inLine4 to include and .exLine4 to exclude filter + EXPECT_EQ( kv.second.GetIncludeFilter(), std::vector( { ".parIn1", ".parIn2", ".inLine4" } ) ); + EXPECT_EQ( kv.second.GetExcludeFilter(), std::vector( { ".parEx1", ".exLine4" } ) ); + } + } +} + +TEST_F( ResourceFilterTest, FilterResourcePathFile_Invalid_MissingPrefix ) +{ + std::string prefixMapStr = "prefix1:/path1;../path2 prefix2:/path3"; + std::string parentFilterStr = "[ .in1 .in2 ] ![ .ex1 ]"; + ResourceTools::FilterPrefixMap prefixMap( prefixMapStr ); + ResourceTools::FilterResourceFilter parentFilter( parentFilterStr ); + + std::string rawPathFileAttrib = "/foo/bar"; // respath is missing the prefix: + EXPECT_THROW( ResourceTools::FilterResourcePathFile pathFile( rawPathFileAttrib, prefixMap, parentFilter ), std::invalid_argument ); +} + +TEST_F( ResourceFilterTest, FilterResourcePathFile_Invalid_UnknownPrefix ) +{ + std::string prefixMapStr = "prefix1:/path1;../path2 prefix2:/path3"; + std::string parentFilterStr = "[ .in1 .in2 ] ![ .ex1 ]"; + ResourceTools::FilterPrefixMap prefixMap( prefixMapStr ); + ResourceTools::FilterResourceFilter parentFilter( parentFilterStr ); + + std::string rawPathFileAttrib = "prefixNotInPrefixMap:/foo/bar"; // unknown prefix + EXPECT_THROW( ResourceTools::FilterResourcePathFile pathFile( rawPathFileAttrib, prefixMap, parentFilter ), std::invalid_argument ); +} + +TEST_F( ResourceFilterTest, FilterResourcePathFile_Invalid_MalformedInlineFilter ) +{ + std::string prefixMapStr = "prefix1:/path1;../path2 prefix2:/path3"; + std::string parentFilterStr = "[ .in1 .in2 ] ![ .ex1 ]"; + ResourceTools::FilterPrefixMap prefixMap( prefixMapStr ); + ResourceTools::FilterResourceFilter parentFilter( parentFilterStr ); + + std::string rawPathFileAttrib = "prefix1:/foo/bar [ .yaml "; // missing closing bracket of inline include filter + EXPECT_THROW( ResourceTools::FilterResourcePathFile pathFile( rawPathFileAttrib, prefixMap, parentFilter ), std::invalid_argument ); +} + +// ----------------------------------------- diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 66798dc..03e11ab 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -25,8 +25,9 @@ set(SRC_FILES include/FilterNamedSection.h include/FilterPrefixMap.h include/FilterResourceFilter.h - include/FilterResourceLine.h + include/FilterResourcePathFile.h include/FilterPrefixMapEntry.h + include/FilterResourcePathFileEntry.h src/BundleStreamIn.cpp src/BundleStreamOut.cpp @@ -47,8 +48,9 @@ set(SRC_FILES src/FilterNamedSection.cpp src/FilterPrefixMap.cpp src/FilterResourceFilter.cpp - src/FilterResourceLine.cpp + src/FilterResourcePathFile.cpp src/FilterPrefixMapEntry.cpp + src/FilterResourcePathFileEntry.cpp ) add_library(resources-tools STATIC ${SRC_FILES}) diff --git a/tools/include/FilterNamedSection.h b/tools/include/FilterNamedSection.h index 95631ba..b03087f 100644 --- a/tools/include/FilterNamedSection.h +++ b/tools/include/FilterNamedSection.h @@ -5,8 +5,11 @@ #include #include +#include +#include +#include #include -#include +#include namespace ResourceTools { @@ -14,13 +17,37 @@ namespace ResourceTools class FilterNamedSection { public: - explicit FilterNamedSection( const std::string& filter, const std::string& resfile, const std::vector& respaths ); + explicit FilterNamedSection( const std::string& sectionName, const std::string& filter, const std::string& respaths, const std::string& resfile, const FilterPrefixMap& parentPrefixMap ); + + // Return combined resolved path map from both respaths and optional resfile + const std::map& GetCombinedResolvedPathMap() const; + + const std::map& GetResolvedRespathsMap() const; + + const std::map& GetResolvedResfileMap() const; private: + std::string m_sectionName; + + // The raw (unparsed) string representation of the filter, resfile and respaths attributes + std::string m_rawFilter; + + std::string m_rawRespaths; // Potentially a multi-line string + + std::string m_rawResfile; // If not empty, then only a single line string + + FilterPrefixMap m_parentPrefixMap; // The "parent" prefix map from the [DEFAULT] section + FilterResourceFilter m_filter; - std::string m_resfile; - std::vector m_respaths; - // Optionally, store parsed FilterResourceLine objects + + FilterResourcePathFile m_respaths; + + std::optional m_resfile; + + // Combined map of fully resolved respaths and resfile FilterResourceFilter objects + std::map m_resolvedCombinedPathMap; + + void ParseNamedSection(); }; } diff --git a/tools/include/FilterPrefixmap.h b/tools/include/FilterPrefixmap.h index 6836b16..55ed003 100644 --- a/tools/include/FilterPrefixmap.h +++ b/tools/include/FilterPrefixmap.h @@ -15,6 +15,8 @@ namespace ResourceTools class FilterPrefixMap { public: + FilterPrefixMap() = default; + explicit FilterPrefixMap( const std::string& rawPrefixMap ); const std::map& GetPrefixMap() const; diff --git a/tools/include/FilterResourceFile.h b/tools/include/FilterResourceFile.h index b598527..7d78391 100644 --- a/tools/include/FilterResourceFile.h +++ b/tools/include/FilterResourceFile.h @@ -17,6 +17,7 @@ class FilterResourceFile private: FilterDefaultSection m_defaultSection; + std::vector m_namedSections; }; diff --git a/tools/include/FilterResourceFilter.h b/tools/include/FilterResourceFilter.h index a71f3aa..73fbc1c 100644 --- a/tools/include/FilterResourceFilter.h +++ b/tools/include/FilterResourceFilter.h @@ -10,37 +10,31 @@ namespace ResourceTools { // Class representing a resource filter with include and exclude filters. -// - This can be for the filter attribute of a NamedSection OR the optional filter for a respaths/resfile line (defined in FilterResourceLine). -// - see ResourceTools::FilterNamedSection and ResourceTools::FilterResourceLine +// - This is for filter attribute of a NamedSection AND the "combined resolved" filter for each respaths/resfile line class FilterResourceFilter { public: - // Constructor that takes a raw filter string and parses it into include and exclude filters. + FilterResourceFilter() = default; + explicit FilterResourceFilter( const std::string& rawFilter ); - // Gets the raw filter string. + // Used as input when constructing a combined resolved filter for a respaths/resfile line. const std::string& GetRawFilter() const; - // Gets the include filter vector. const std::vector& GetIncludeFilter() const; - // Gets the exclude filter vector. const std::vector& GetExcludeFilter() const; private: - // Raw filter string. std::string m_rawFilter; - // Include filter vector. std::vector m_includeFilter; - // Exclude filter vector. std::vector m_excludeFilter; - // Parse raw filter string into vectors of include and exclude filters. void ParseFilters(); - // Static helper to place a token in the correct vector, moving it from one vector to another, if need be. + // Static helper placing tokens in the correct vector, moving it if need be. static void PlaceTokenInCorrectVector( const std::string& token, std::vector& fromVector, std::vector& toVector ); }; diff --git a/tools/include/FilterResourceLine.h b/tools/include/FilterResourceLine.h deleted file mode 100644 index bd565e8..0000000 --- a/tools/include/FilterResourceLine.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright © 2025 CCP ehf. - -#ifndef FILTERRESOURCELINE_H -#define FILTERRESOURCELINE_H - -#include -#include -#include -#include - -namespace ResourceTools -{ - -// Class representing a single raw line of a resfile/respaths attribute. -class FilterResourceLine -{ -public: - // Constructor that takes a rawLine string to parse into a linePath vector, based on already constructed prefixMap and an optional sectionFilter. - // - sectionFilter is only optional, in case "resfile" attribute requires it - explicit FilterResourceLine( const std::string& rawLine, const FilterPrefixMap& prefixMap, std::optional sectionFilter ); - - // TODO: Add a getter function that combines the section filter and optional line filter along with the concatenated prefixMap + respath/resfile value. - -private: - // Raw string, representing the value of the resfile/respaths line attribute. - std::string m_rawLine; - - // Reference to an already constructed FilterPrefixMap object. - const FilterPrefixMap& m_prefixMap; - - // Optional FilterResourceFilter object representing the "parent" filter for this section. - // - optional, in case this is for a "resfile" attribute that MAY NOT have a section filter. - std::optional m_sectionFilter; - - // The optional FilterResourceFilter object representing the line-specific filter, if any. - const std::optional m_lineFilter; - - // A vector of FilterResourceLineEntries, with two elemants: resolved full paths after applying all relevant prefix maps to the path portion, along with the combined in-order m_sectionFilter + optional m_lineFilter. - // NOTE: FilterResourceLineEntry is a class/struct with a string path and combined FilterResourceFilter) - // Replace this std::vector m_linePath; - // with std::vector m_lineEntry; - - // Parses the rawLine string into its components (m_linePath, optional m_lineFilter). - void ParseLine(); -}; - -} - -#endif // FILTERRESOURCELINE_H diff --git a/tools/include/FilterResourcePathFile.h b/tools/include/FilterResourcePathFile.h new file mode 100644 index 0000000..de71959 --- /dev/null +++ b/tools/include/FilterResourcePathFile.h @@ -0,0 +1,41 @@ +// Copyright © 2025 CCP ehf. + +#ifndef FILTERRESOURCEPATHFILE_H +#define FILTERRESOURCEPATHFILE_H + +#include +#include +#include +#include + +namespace ResourceTools +{ + +// Class representing a resfile/respaths attribute. +class FilterResourcePathFile +{ +public: + FilterResourcePathFile() = default; + + explicit FilterResourcePathFile( const std::string& rawPathFileAttrib, const FilterPrefixMap& parentPrefixMap, const FilterResourceFilter& parentSectionFilter ); + + // Get the map of fully resolved paths to their combined FilterResourceFilter objects. + const std::map& GetResolvedPathMap() const; + +private: + // The raw (multiline) respath attribute (same for resfile). + std::string m_rawPathFileAttrib; + + FilterPrefixMap m_parentPrefixMap; + + FilterResourceFilter m_parentSectionFilter; + + // Map of fully resolved paths to their combined FilterResourceFilter objects. + std::map m_resolvedPathMap; + + void ParseRawPathFileAttribute(); +}; + +} + +#endif // FILTERRESOURCEPATHFILE_H diff --git a/tools/include/FilterResourcePathFileEntry.h b/tools/include/FilterResourcePathFileEntry.h new file mode 100644 index 0000000..3ffbcc9 --- /dev/null +++ b/tools/include/FilterResourcePathFileEntry.h @@ -0,0 +1,41 @@ +// Copyright © 2025 CCP ehf. + +#ifndef FILTERRESOURCEPATHFILEENTRY_H +#define FILTERRESOURCEPATHFILEENTRY_H + +#include +#include +#include + +namespace ResourceTools +{ + +class FilterResourcePathFileEntry +{ +public: + explicit FilterResourcePathFileEntry( const std::string& rawPathLine, const FilterPrefixMap& parentPrefixMap, const FilterResourceFilter& parentSectionFilter ); + + const FilterResourceFilter& GetEntryFilter() const; + + const std::set& GetResolvedPaths() const; + +private: + std::string m_rawPathLine; + + FilterPrefixMap m_parentPrefixMap; + + FilterResourceFilter m_parentSectionFilter; + + // The combined filter for this resfile/respaths attribute line, built from the parentSectionFilter and any inline filter. + FilterResourceFilter m_entryFilter; + + // The set of resolved paths (sorted). + std::set m_resolvedPaths; + + // Parse the m_rawPathLine by constructing the combined m_entryFilter and append paths to m_resolvedPaths based on the m_parentPrefixMap + void ParseRawPathLine(); +}; + +} + +#endif //FILTERRESOURCEPATHFILEENTRY_H diff --git a/tools/src/FilterNamedSection.cpp b/tools/src/FilterNamedSection.cpp index a74a900..9521211 100644 --- a/tools/src/FilterNamedSection.cpp +++ b/tools/src/FilterNamedSection.cpp @@ -6,12 +6,87 @@ namespace ResourceTools { -FilterNamedSection::FilterNamedSection( const std::string& filter, const std::string& resfile, const std::vector& respaths ) : - m_filter( filter ), - m_resfile( resfile ), - m_respaths( respaths ) +FilterNamedSection::FilterNamedSection( const std::string& sectionName, const std::string& filter, const std::string& respaths, const std::string& resfile, const FilterPrefixMap& parentPrefixMap ) : + m_sectionName( sectionName ), + m_rawFilter( filter ), + m_rawRespaths( respaths ), + m_rawResfile( resfile ), + m_parentPrefixMap( parentPrefixMap ) { - throw std::logic_error( "Not implemented yet exception" ); + m_filter = FilterResourceFilter( m_rawFilter ); + + ParseNamedSection(); +} + +const std::map& FilterNamedSection::GetCombinedResolvedPathMap() const +{ + return m_resolvedCombinedPathMap; +} + +const std::map& FilterNamedSection::GetResolvedRespathsMap() const +{ + return m_respaths.GetResolvedPathMap(); +} + +const std::map& FilterNamedSection::GetResolvedResfileMap() const +{ + if( m_resfile ) + { + return m_resfile->GetResolvedPathMap(); + } + else + { + static const std::map emptyResfileMap; + return emptyResfileMap; + } +} + +void FilterNamedSection::ParseNamedSection() +{ + // Respath + if( m_rawRespaths.empty() ) + { + throw std::invalid_argument( "Respaths attribute is empty for section: " + m_sectionName ); + } + m_respaths = FilterResourcePathFile( m_rawRespaths, m_parentPrefixMap, m_filter ); + + // Resfile + if( !m_rawResfile.empty() ) + { + m_resfile = FilterResourcePathFile( m_rawResfile, m_parentPrefixMap, m_filter ); + } + else + { + m_resfile.reset(); + } + + // Populate the combined map. + for( const auto& kv : m_respaths.GetResolvedPathMap() ) + { + m_resolvedCombinedPathMap[kv.first] = kv.second; + } + + // Add resfile to the combined map + if( m_resfile ) + { + // Allow "resfile" to contain multiple entries (future proofing) + for( const auto& kv : m_resfile->GetResolvedPathMap() ) + { + // Combine filters of both if same key already exists + auto it = m_resolvedCombinedPathMap.find( kv.first ); + if( it != m_resolvedCombinedPathMap.end() ) + { + // Combine the filters (using raw filter strings) + std::string combinedRawFilter = it->second.GetRawFilter() + " " + kv.second.GetRawFilter(); + FilterResourceFilter combinedFilter( combinedRawFilter ); + m_resolvedCombinedPathMap[kv.first] = combinedFilter; + } + else + { + m_resolvedCombinedPathMap[kv.first] = kv.second; + } + } + } } } diff --git a/tools/src/FilterResourceLine.cpp b/tools/src/FilterResourceLine.cpp deleted file mode 100644 index e9f4ea1..0000000 --- a/tools/src/FilterResourceLine.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright © 2025 CCP ehf. - -#include -#include - -namespace ResourceTools -{ - -FilterResourceLine::FilterResourceLine( const std::string& rawLine, const FilterPrefixMap& prefixMap, std::optional sectionFilter ) : - m_rawLine( rawLine ), - m_prefixMap( prefixMap ), - m_sectionFilter( sectionFilter ) -{ - throw std::logic_error( "Not implemented yet exception" ); -} - -// TODO: Remove this, probably don't need it. -//bool FilterResourceLine::IsValid() const -//{ -// throw std::logic_error( "Not implemented yet exception" ); -//} - -void FilterResourceLine::ParseLine() -{ - throw std::logic_error( "Not implemented yet exception" ); -} - -} diff --git a/tools/src/FilterResourcePathFile.cpp b/tools/src/FilterResourcePathFile.cpp new file mode 100644 index 0000000..e3b9b5e --- /dev/null +++ b/tools/src/FilterResourcePathFile.cpp @@ -0,0 +1,56 @@ +// Copyright © 2025 CCP ehf. + +#include +#include +#include + + +namespace ResourceTools +{ + +FilterResourcePathFile::FilterResourcePathFile( const std::string& rawPathFileAttrib, const FilterPrefixMap& parentPrefixMap, const FilterResourceFilter& parentSectionFilter ) : + m_rawPathFileAttrib( rawPathFileAttrib ), + m_parentPrefixMap( parentPrefixMap ), + m_parentSectionFilter( parentSectionFilter ) +{ + m_resolvedPathMap.clear(); + + ParseRawPathFileAttribute(); +} + +const std::map& FilterResourcePathFile::GetResolvedPathMap() const +{ + return m_resolvedPathMap; +} + +void FilterResourcePathFile::ParseRawPathFileAttribute() +{ + // Split m_rawPathFileAttrib into lines (in case of multiline attribute) + std::istringstream stream( m_rawPathFileAttrib ); + std::string line; + while( std::getline( stream, line ) ) + { + // Trim whitespace from both ends + size_t first = line.find_first_not_of( " \t\r" ); + if( first == std::string::npos ) + continue; // skip if empty line + + size_t last = line.find_last_not_of( " \t\r" ); + std::string rawPathLine = line.substr( first, last - first + 1 ); + + // Skip commented out lines (in case there is "inline" comment within the .ini file attribute value) + if( rawPathLine.empty() || rawPathLine[0] == '#' || rawPathLine[0] == ';' ) + continue; + + // Add entries to the resolved path map + auto lineEntry = FilterResourcePathFileEntry( rawPathLine, m_parentPrefixMap, m_parentSectionFilter ); + const auto resolvedPaths = lineEntry.GetResolvedPaths(); + const auto entryFilter = lineEntry.GetEntryFilter(); + for( const auto& path : resolvedPaths ) + { + m_resolvedPathMap.emplace( path, entryFilter ); + } + } +} + +} diff --git a/tools/src/FilterResourcePathFileEntry.cpp b/tools/src/FilterResourcePathFileEntry.cpp new file mode 100644 index 0000000..a32f8d1 --- /dev/null +++ b/tools/src/FilterResourcePathFileEntry.cpp @@ -0,0 +1,96 @@ +// Copyright © 2025 CCP ehf. + +#include +#include + +namespace ResourceTools +{ + +FilterResourcePathFileEntry::FilterResourcePathFileEntry( const std::string& rawPathLine, const FilterPrefixMap& parentPrefixMap, const FilterResourceFilter& parentSectionFilter ) : + m_rawPathLine( rawPathLine ), + m_parentPrefixMap( parentPrefixMap ), + m_parentSectionFilter( parentSectionFilter ) +{ + ParseRawPathLine(); +} + + +const FilterResourceFilter& FilterResourcePathFileEntry::GetEntryFilter() const +{ + return m_entryFilter; +} + +const std::set& FilterResourcePathFileEntry::GetResolvedPaths() const +{ + return m_resolvedPaths; +} + +void FilterResourcePathFileEntry::ParseRawPathLine() +{ + // Split on whitespace: first token is pathPart, rest is (optional) filterPart + std::string rawPathToken; + std::string rawOptionalFilterPart; + std::string combinedRawFilter; + + std::istringstream iss( m_rawPathLine ); + iss >> rawPathToken; + if( !iss.eof() ) + { + // There is an optional filter part. Construct it, will error out if wrong format + std::getline( iss, rawOptionalFilterPart ); + FilterResourceFilter inlineFilter = FilterResourceFilter( rawOptionalFilterPart ); + + // Combine parent filter and inline filter + combinedRawFilter = m_parentSectionFilter.GetRawFilter() + " " + inlineFilter.GetRawFilter(); + } + else + { + // No inline filter, use parent section filter as is + combinedRawFilter = m_parentSectionFilter.GetRawFilter(); + } + m_entryFilter = FilterResourceFilter( combinedRawFilter ); + + // Validate the rawPathToken + size_t colon = rawPathToken.find( ':' ); + if( colon == std::string::npos ) + { + // TODO: Change this to a defined error code/type + throw std::invalid_argument( std::string( "Missing prefix in path for: " ) + m_rawPathLine ); + } + std::string prefix = rawPathToken.substr( 0, colon ); + std::string rest = rawPathToken.substr( colon + 1 ); + + const auto& prefixMap = m_parentPrefixMap.GetPrefixMap(); + auto it = prefixMap.find( prefix ); + if( it == prefixMap.end() ) + { + // TODO: Change this to a defined error code/type + throw std::invalid_argument( std::string( "Prefix '" ) + prefix + "' not present in prefixMap for line: " + m_rawPathLine ); + } + + // Each FilterPrefixMapEntry may have multiple paths, combine/resolve all of them + const auto& prefixEntry = it->second; + const auto& prefixPaths = prefixEntry.GetPaths(); + for( const auto& basePrefixPath : prefixPaths ) + { + // Ensure only one '/' at the join point + bool baseEndsWithSlash = !basePrefixPath.empty() && basePrefixPath.back() == '/'; + bool restStartsWithSlash = !rest.empty() && rest.front() == '/'; + std::string resolvedPath = basePrefixPath; + if( baseEndsWithSlash && restStartsWithSlash ) + { + resolvedPath += rest.substr( 1 ); + } + else if( !baseEndsWithSlash && !restStartsWithSlash ) + { + resolvedPath += '/' + rest; + } + else + { + resolvedPath = basePrefixPath + rest; + } + m_resolvedPaths.insert( resolvedPath ); + } +} + +} \ No newline at end of file From 3ce25ae56ded8c1bcc742855c22c052f1c901189 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 8 Jan 2026 17:37:45 +0000 Subject: [PATCH 023/124] Add test for Duplicate Filter in Overrides --- tests/src/ResourceFilterTest.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index d46e707..6be5405 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -668,6 +668,35 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_MultiLine_MixedFiltersWithOve } } +TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_DuplicateOverrides ) +{ + std::string prefixMapStr = "prefix1:/path1"; + std::string parentFilterStr = "[ .parIn1 .parIn2 ] ![ .parEx1 ]"; + ResourceTools::FilterPrefixMap prefixMap( prefixMapStr ); + ResourceTools::FilterResourceFilter parentFilter( parentFilterStr ); + + // Make sure we DUPLICATE the inline with the same as the parents (should NOT result in combined duplicates) + std::string rawPathFileAttrib = "prefix1:/foo/bar [ .parIn2 .parIn1 .lineIn1 .parIn1 .parIn2 ] ![ .lineEx1 .parEx1 ]"; + ResourceTools::FilterResourcePathFile pathFile( rawPathFileAttrib, prefixMap, parentFilter ); + const auto& resolvedPathMap = pathFile.GetResolvedPathMap(); + + // Check the resolved path and filters against expected (NO duplicates in final filters list) + std::set expectedPaths = { "/path1/foo/bar" }; + std::vector expectedIncludes = { ".parIn1", ".parIn2", ".lineIn1" }; + std::vector expectedExcludes = { ".parEx1", ".lineEx1" }; + + for( const auto& p : expectedPaths ) + { + EXPECT_TRUE( resolvedPathMap.count( p ) ); + } + + for( const auto& kv : resolvedPathMap ) + { + EXPECT_EQ( kv.second.GetIncludeFilter(), expectedIncludes ); + EXPECT_EQ( kv.second.GetExcludeFilter(), expectedExcludes ); + } +} + TEST_F( ResourceFilterTest, FilterResourcePathFile_Invalid_MissingPrefix ) { std::string prefixMapStr = "prefix1:/path1;../path2 prefix2:/path3"; From f96197d429f09c42db4ca4354454e35bd4030ce8 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 9 Jan 2026 12:17:00 +0000 Subject: [PATCH 024/124] Change m_parentPrefixMap to a const reference Also fixed clang-tidy suggestions --- tools/include/FilterNamedSection.h | 15 ++++---- tools/include/FilterResourcePathFile.h | 6 ++-- tools/include/FilterResourcePathFileEntry.h | 6 ++-- tools/src/FilterNamedSection.cpp | 38 ++++++++------------- tools/src/FilterResourcePathFile.cpp | 6 ++-- tools/src/FilterResourcePathFileEntry.cpp | 6 ++-- 6 files changed, 36 insertions(+), 41 deletions(-) diff --git a/tools/include/FilterNamedSection.h b/tools/include/FilterNamedSection.h index b03087f..a47a896 100644 --- a/tools/include/FilterNamedSection.h +++ b/tools/include/FilterNamedSection.h @@ -17,7 +17,11 @@ namespace ResourceTools class FilterNamedSection { public: - explicit FilterNamedSection( const std::string& sectionName, const std::string& filter, const std::string& respaths, const std::string& resfile, const FilterPrefixMap& parentPrefixMap ); + explicit FilterNamedSection( std::string sectionName, + const std::string& filter, + const std::string& respaths, + const std::string& resfile, + const FilterPrefixMap& parentPrefixMap ); // Return combined resolved path map from both respaths and optional resfile const std::map& GetCombinedResolvedPathMap() const; @@ -29,14 +33,7 @@ class FilterNamedSection private: std::string m_sectionName; - // The raw (unparsed) string representation of the filter, resfile and respaths attributes - std::string m_rawFilter; - - std::string m_rawRespaths; // Potentially a multi-line string - - std::string m_rawResfile; // If not empty, then only a single line string - - FilterPrefixMap m_parentPrefixMap; // The "parent" prefix map from the [DEFAULT] section + const FilterPrefixMap& m_parentPrefixMap; // The "parent" prefix map from the [DEFAULT] section FilterResourceFilter m_filter; diff --git a/tools/include/FilterResourcePathFile.h b/tools/include/FilterResourcePathFile.h index de71959..713e99d 100644 --- a/tools/include/FilterResourcePathFile.h +++ b/tools/include/FilterResourcePathFile.h @@ -17,7 +17,9 @@ class FilterResourcePathFile public: FilterResourcePathFile() = default; - explicit FilterResourcePathFile( const std::string& rawPathFileAttrib, const FilterPrefixMap& parentPrefixMap, const FilterResourceFilter& parentSectionFilter ); + explicit FilterResourcePathFile( std::string rawPathFileAttrib, + const FilterPrefixMap& parentPrefixMap, + const FilterResourceFilter& parentSectionFilter ); // Get the map of fully resolved paths to their combined FilterResourceFilter objects. const std::map& GetResolvedPathMap() const; @@ -26,7 +28,7 @@ class FilterResourcePathFile // The raw (multiline) respath attribute (same for resfile). std::string m_rawPathFileAttrib; - FilterPrefixMap m_parentPrefixMap; + const FilterPrefixMap& m_parentPrefixMap; // The "parent" prefix map from the [DEFAULT] section FilterResourceFilter m_parentSectionFilter; diff --git a/tools/include/FilterResourcePathFileEntry.h b/tools/include/FilterResourcePathFileEntry.h index 3ffbcc9..e6e5044 100644 --- a/tools/include/FilterResourcePathFileEntry.h +++ b/tools/include/FilterResourcePathFileEntry.h @@ -13,7 +13,9 @@ namespace ResourceTools class FilterResourcePathFileEntry { public: - explicit FilterResourcePathFileEntry( const std::string& rawPathLine, const FilterPrefixMap& parentPrefixMap, const FilterResourceFilter& parentSectionFilter ); + explicit FilterResourcePathFileEntry( std::string rawPathLine, + const FilterPrefixMap& parentPrefixMap, + const FilterResourceFilter& parentSectionFilter ); const FilterResourceFilter& GetEntryFilter() const; @@ -22,7 +24,7 @@ class FilterResourcePathFileEntry private: std::string m_rawPathLine; - FilterPrefixMap m_parentPrefixMap; + const FilterPrefixMap& m_parentPrefixMap; // The "parent" prefix map from the [DEFAULT] section FilterResourceFilter m_parentSectionFilter; diff --git a/tools/src/FilterNamedSection.cpp b/tools/src/FilterNamedSection.cpp index 9521211..dffd630 100644 --- a/tools/src/FilterNamedSection.cpp +++ b/tools/src/FilterNamedSection.cpp @@ -6,14 +6,21 @@ namespace ResourceTools { -FilterNamedSection::FilterNamedSection( const std::string& sectionName, const std::string& filter, const std::string& respaths, const std::string& resfile, const FilterPrefixMap& parentPrefixMap ) : - m_sectionName( sectionName ), - m_rawFilter( filter ), - m_rawRespaths( respaths ), - m_rawResfile( resfile ), - m_parentPrefixMap( parentPrefixMap ) +FilterNamedSection::FilterNamedSection( std::string sectionName, + const std::string& filter, + const std::string& respaths, + const std::string& resfile, + const FilterPrefixMap& parentPrefixMap ) : + m_sectionName( std::move( sectionName ) ), + m_parentPrefixMap( parentPrefixMap ), + m_filter( filter ), + m_respaths( respaths, parentPrefixMap, m_filter ), + m_resfile( resfile.empty() ? std::nullopt : std::make_optional( resfile, parentPrefixMap, m_filter ) ) { - m_filter = FilterResourceFilter( m_rawFilter ); + if( respaths.empty() ) + { + throw std::invalid_argument( "Respaths attribute is empty for section: " + m_sectionName ); + } ParseNamedSection(); } @@ -43,23 +50,6 @@ const std::map& FilterNamedSection::GetResolv void FilterNamedSection::ParseNamedSection() { - // Respath - if( m_rawRespaths.empty() ) - { - throw std::invalid_argument( "Respaths attribute is empty for section: " + m_sectionName ); - } - m_respaths = FilterResourcePathFile( m_rawRespaths, m_parentPrefixMap, m_filter ); - - // Resfile - if( !m_rawResfile.empty() ) - { - m_resfile = FilterResourcePathFile( m_rawResfile, m_parentPrefixMap, m_filter ); - } - else - { - m_resfile.reset(); - } - // Populate the combined map. for( const auto& kv : m_respaths.GetResolvedPathMap() ) { diff --git a/tools/src/FilterResourcePathFile.cpp b/tools/src/FilterResourcePathFile.cpp index e3b9b5e..c76a94f 100644 --- a/tools/src/FilterResourcePathFile.cpp +++ b/tools/src/FilterResourcePathFile.cpp @@ -8,8 +8,10 @@ namespace ResourceTools { -FilterResourcePathFile::FilterResourcePathFile( const std::string& rawPathFileAttrib, const FilterPrefixMap& parentPrefixMap, const FilterResourceFilter& parentSectionFilter ) : - m_rawPathFileAttrib( rawPathFileAttrib ), +FilterResourcePathFile::FilterResourcePathFile( std::string rawPathFileAttrib, + const FilterPrefixMap& parentPrefixMap, + const FilterResourceFilter& parentSectionFilter ) : + m_rawPathFileAttrib( std::move( rawPathFileAttrib ) ), m_parentPrefixMap( parentPrefixMap ), m_parentSectionFilter( parentSectionFilter ) { diff --git a/tools/src/FilterResourcePathFileEntry.cpp b/tools/src/FilterResourcePathFileEntry.cpp index a32f8d1..5881c89 100644 --- a/tools/src/FilterResourcePathFileEntry.cpp +++ b/tools/src/FilterResourcePathFileEntry.cpp @@ -6,8 +6,10 @@ namespace ResourceTools { -FilterResourcePathFileEntry::FilterResourcePathFileEntry( const std::string& rawPathLine, const FilterPrefixMap& parentPrefixMap, const FilterResourceFilter& parentSectionFilter ) : - m_rawPathLine( rawPathLine ), +FilterResourcePathFileEntry::FilterResourcePathFileEntry( std::string rawPathLine, + const FilterPrefixMap& parentPrefixMap, + const FilterResourceFilter& parentSectionFilter ) : + m_rawPathLine( std::move( rawPathLine ) ), m_parentPrefixMap( parentPrefixMap ), m_parentSectionFilter( parentSectionFilter ) { From 698874ba9951540b77c894365a0c1f5e5887f755 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:19:25 +0000 Subject: [PATCH 025/124] Change m_parentSectionFilter to a const reference --- tools/include/FilterResourcePathFile.h | 6 ++++-- tools/include/FilterResourcePathFileEntry.h | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tools/include/FilterResourcePathFile.h b/tools/include/FilterResourcePathFile.h index 713e99d..0bcce86 100644 --- a/tools/include/FilterResourcePathFile.h +++ b/tools/include/FilterResourcePathFile.h @@ -28,9 +28,11 @@ class FilterResourcePathFile // The raw (multiline) respath attribute (same for resfile). std::string m_rawPathFileAttrib; - const FilterPrefixMap& m_parentPrefixMap; // The "parent" prefix map from the [DEFAULT] section + // The "parent" prefix map from the [DEFAULT] section + const FilterPrefixMap& m_parentPrefixMap; - FilterResourceFilter m_parentSectionFilter; + // The "parent" filter from the [namedSection] containing this resfile/respaths attribute + const FilterResourceFilter& m_parentSectionFilter; // Map of fully resolved paths to their combined FilterResourceFilter objects. std::map m_resolvedPathMap; diff --git a/tools/include/FilterResourcePathFileEntry.h b/tools/include/FilterResourcePathFileEntry.h index e6e5044..993fa0a 100644 --- a/tools/include/FilterResourcePathFileEntry.h +++ b/tools/include/FilterResourcePathFileEntry.h @@ -24,9 +24,11 @@ class FilterResourcePathFileEntry private: std::string m_rawPathLine; - const FilterPrefixMap& m_parentPrefixMap; // The "parent" prefix map from the [DEFAULT] section + // The "parent" prefix map from the [DEFAULT] section + const FilterPrefixMap& m_parentPrefixMap; - FilterResourceFilter m_parentSectionFilter; + // The "parent" filter from the [namedSection] + const FilterResourceFilter& m_parentSectionFilter; // The combined filter for this resfile/respaths attribute line, built from the parentSectionFilter and any inline filter. FilterResourceFilter m_entryFilter; From 74de1228e251eecaaaace1a7f5f8c1e969cbe42a Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 9 Jan 2026 14:52:13 +0000 Subject: [PATCH 026/124] Remove un-necessary default constructors --- tools/include/FilterPrefixmap.h | 2 -- tools/include/FilterResourcePathFile.h | 2 -- tools/src/FilterNamedSection.cpp | 9 ++++----- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/tools/include/FilterPrefixmap.h b/tools/include/FilterPrefixmap.h index 55ed003..6836b16 100644 --- a/tools/include/FilterPrefixmap.h +++ b/tools/include/FilterPrefixmap.h @@ -15,8 +15,6 @@ namespace ResourceTools class FilterPrefixMap { public: - FilterPrefixMap() = default; - explicit FilterPrefixMap( const std::string& rawPrefixMap ); const std::map& GetPrefixMap() const; diff --git a/tools/include/FilterResourcePathFile.h b/tools/include/FilterResourcePathFile.h index 0bcce86..5b2c6be 100644 --- a/tools/include/FilterResourcePathFile.h +++ b/tools/include/FilterResourcePathFile.h @@ -15,8 +15,6 @@ namespace ResourceTools class FilterResourcePathFile { public: - FilterResourcePathFile() = default; - explicit FilterResourcePathFile( std::string rawPathFileAttrib, const FilterPrefixMap& parentPrefixMap, const FilterResourceFilter& parentSectionFilter ); diff --git a/tools/src/FilterNamedSection.cpp b/tools/src/FilterNamedSection.cpp index dffd630..173409b 100644 --- a/tools/src/FilterNamedSection.cpp +++ b/tools/src/FilterNamedSection.cpp @@ -41,11 +41,10 @@ const std::map& FilterNamedSection::GetResolv { return m_resfile->GetResolvedPathMap(); } - else - { - static const std::map emptyResfileMap; - return emptyResfileMap; - } + + // Return empty map if no resfile present + static const std::map emptyResfileMap; + return emptyResfileMap; } void FilterNamedSection::ParseNamedSection() From 022b0cf4cfce40b0aaab3c46f2439043299c56cd Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 9 Jan 2026 15:51:49 +0000 Subject: [PATCH 027/124] Rename and gorup Resource Filter tests by class tested --- tests/src/ResourceFilterTest.cpp | 54 ++++++++++++++++---------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 6be5405..d9c979d 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -38,7 +38,7 @@ TEST_F( ResourceFilterTest, Example1IniParsing ) // ----------------------------------------- -TEST_F( ResourceFilterTest, OnlyIncludeFilter ) +TEST_F( ResourceFilterTest, FilterResourceFilter_OnlyIncludeFilter ) { ResourceTools::FilterResourceFilter filter( "[ .this .is .included ]" ); const auto& includes = filter.GetIncludeFilter(); @@ -50,7 +50,7 @@ TEST_F( ResourceFilterTest, OnlyIncludeFilter ) EXPECT_TRUE( excludes.empty() ); } -TEST_F( ResourceFilterTest, OnlyExcludeFilter ) +TEST_F( ResourceFilterTest, FilterResourceFilter_OnlyExcludeFilter ) { ResourceTools::FilterResourceFilter filter( "![ .excluded .extension ]" ); const auto& includes = filter.GetIncludeFilter(); @@ -61,7 +61,7 @@ TEST_F( ResourceFilterTest, OnlyExcludeFilter ) EXPECT_EQ( excludes[1], ".extension" ); } -TEST_F( ResourceFilterTest, ComplexIncludeExcludeFilter ) +TEST_F( ResourceFilterTest, FilterResourceFilter_ComplexIncludeExcludeFilter ) { ResourceTools::FilterResourceFilter filter( "[ .red .gr2 .dds .png .yaml ] [ .txt ] ![ .csv .xls ] [ .bat .sh ] ![ .blk .yel ]" ); const auto& includes = filter.GetIncludeFilter(); @@ -72,7 +72,7 @@ TEST_F( ResourceFilterTest, ComplexIncludeExcludeFilter ) EXPECT_EQ( excludes, expectedExcludes ); } -TEST_F( ResourceFilterTest, SimpleIncludeFilter ) +TEST_F( ResourceFilterTest, FilterResourceFilter_SimpleIncludeFilter ) { ResourceTools::FilterResourceFilter filter( "[ .red ]" ); const auto& includes = filter.GetIncludeFilter(); @@ -80,7 +80,7 @@ TEST_F( ResourceFilterTest, SimpleIncludeFilter ) EXPECT_EQ( includes[0], ".red" ); } -TEST_F( ResourceFilterTest, SimpleExcludeFilter ) +TEST_F( ResourceFilterTest, FilterResourceFilter_SimpleExcludeFilter ) { ResourceTools::FilterResourceFilter filter( "![ .blk ]" ); const auto& excludes = filter.GetExcludeFilter(); @@ -88,7 +88,7 @@ TEST_F( ResourceFilterTest, SimpleExcludeFilter ) EXPECT_EQ( excludes[0], ".blk" ); } -TEST_F( ResourceFilterTest, IncludeExcludeInclude ) +TEST_F( ResourceFilterTest, FilterResourceFilter_IncludeExcludeInclude ) { // Include .in1 and .in2 // Exclude .in2, .ex1, and .ex2 (removes .in2 from include) @@ -104,7 +104,7 @@ TEST_F( ResourceFilterTest, IncludeExcludeInclude ) EXPECT_EQ( excludes, expectedExcludes ); } -TEST_F( ResourceFilterTest, MissingClosingIncludeBracketBeforeNextOpenExcludeBracket ) +TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingIncludeBracketBeforeNextOpenExcludeBracket ) { try { @@ -122,7 +122,7 @@ TEST_F( ResourceFilterTest, MissingClosingIncludeBracketBeforeNextOpenExcludeBra } } -TEST_F( ResourceFilterTest, ExcludeMarkerWithoutBracket_v1 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_ExcludeMarkerWithoutBracket_v1 ) { try { @@ -139,7 +139,7 @@ TEST_F( ResourceFilterTest, ExcludeMarkerWithoutBracket_v1 ) } } -TEST_F( ResourceFilterTest, ExcludeMarkerWithoutBracket_v2 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_ExcludeMarkerWithoutBracket_v2 ) { try { @@ -156,7 +156,7 @@ TEST_F( ResourceFilterTest, ExcludeMarkerWithoutBracket_v2 ) } } -TEST_F( ResourceFilterTest, ExcludeMarkerWithoutBracket_v3 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_ExcludeMarkerWithoutBracket_v3 ) { try { @@ -173,7 +173,7 @@ TEST_F( ResourceFilterTest, ExcludeMarkerWithoutBracket_v3 ) } } -TEST_F( ResourceFilterTest, MissingOpeningBracket_v1 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_MissingOpeningBracket_v1 ) { try { @@ -190,7 +190,7 @@ TEST_F( ResourceFilterTest, MissingOpeningBracket_v1 ) } } -TEST_F( ResourceFilterTest, MissingOpeningBracket_v2 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_MissingOpeningBracket_v2 ) { try { @@ -207,7 +207,7 @@ TEST_F( ResourceFilterTest, MissingOpeningBracket_v2 ) } } -TEST_F( ResourceFilterTest, MissingClosingBracket_v1 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingBracket_v1 ) { try { @@ -224,7 +224,7 @@ TEST_F( ResourceFilterTest, MissingClosingBracket_v1 ) } } -TEST_F( ResourceFilterTest, MissingClosingBracket_v2 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingBracket_v2 ) { try { @@ -241,7 +241,7 @@ TEST_F( ResourceFilterTest, MissingClosingBracket_v2 ) } } -TEST_F( ResourceFilterTest, MissingClosingBracket_v3 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingBracket_v3 ) { try { @@ -258,7 +258,7 @@ TEST_F( ResourceFilterTest, MissingClosingBracket_v3 ) } } -TEST_F( ResourceFilterTest, CondensedValidFilterStringv1 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_CondensedValidFilterStringv1 ) { ResourceTools::FilterResourceFilter filter( "[inToken1 inToken2]![exToken1 exToken2][inToken3]" ); const auto& includes = filter.GetIncludeFilter(); @@ -269,7 +269,7 @@ TEST_F( ResourceFilterTest, CondensedValidFilterStringv1 ) EXPECT_EQ( excludes, expectedExcludes ); } -TEST_F( ResourceFilterTest, CondensedValidFilterStringv2 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_CondensedValidFilterStringv2 ) { ResourceTools::FilterResourceFilter filter( "![exToken1][inToken1 inToken2]![exToken2][inToken3]" ); const auto& includes = filter.GetIncludeFilter(); @@ -282,7 +282,7 @@ TEST_F( ResourceFilterTest, CondensedValidFilterStringv2 ) // ----------------------------------------- -TEST_F( ResourceFilterTest, SinglePrefixMultiplePaths ) +TEST_F( ResourceFilterTest, FilterPrefixMap_SinglePrefixMultiplePaths ) { ResourceTools::FilterPrefixMap map( "prefix1:/somePath;../otherPath" ); const auto& prefixMap = map.GetPrefixMap(); @@ -298,7 +298,7 @@ TEST_F( ResourceFilterTest, SinglePrefixMultiplePaths ) EXPECT_EQ( it->second.GetPaths(), expected ) << "Paths do not match expected values"; } -TEST_F( ResourceFilterTest, MultiplePrefixes ) +TEST_F( ResourceFilterTest, FilterPrefixMap_MultiplePrefixes ) { ResourceTools::FilterPrefixMap map( "prefix1:/path1;/path2 prefix2:/newPath1" ); const auto& prefixMap = map.GetPrefixMap(); @@ -320,7 +320,7 @@ TEST_F( ResourceFilterTest, MultiplePrefixes ) EXPECT_EQ( it2->second.GetPaths(), expected2 ) << "Paths do not match expected values"; } -TEST_F( ResourceFilterTest, DuplicateSamePrefixPathsInDifferentOrder ) +TEST_F( ResourceFilterTest, FilterPrefixMap_DuplicateSamePrefixPathsInDifferentOrder ) { ResourceTools::FilterPrefixMap map( "prefix1:/path1;/path2 prefix1:/path2;/path1" ); @@ -339,7 +339,7 @@ TEST_F( ResourceFilterTest, DuplicateSamePrefixPathsInDifferentOrder ) EXPECT_EQ( it->first, it->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; } -TEST_F( ResourceFilterTest, MultiplePrefixesAppendToPaths ) +TEST_F( ResourceFilterTest, FilterPrefixMap_MultiplePrefixesAppendToPaths ) { ResourceTools::FilterPrefixMap map( "prefix1:/path2;/path1 prefix2:/otherPath1;/otherPath2 prefix1:/path3;/path1" ); const auto& prefixMap = map.GetPrefixMap(); @@ -360,7 +360,7 @@ TEST_F( ResourceFilterTest, MultiplePrefixesAppendToPaths ) EXPECT_EQ( it2->first, it2->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; } -TEST_F( ResourceFilterTest, DifferentWhitespacesBetweenPrefixes ) +TEST_F( ResourceFilterTest, FilterPrefixMap_DifferentWhitespacesBetweenPrefixes ) { std::string input = "prefix1:/path1\tprefixTab:/path2\nprefixNewLine:/path3"; ResourceTools::FilterPrefixMap map( input ); @@ -387,7 +387,7 @@ TEST_F( ResourceFilterTest, DifferentWhitespacesBetweenPrefixes ) EXPECT_EQ( it3->first, it3->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; } -TEST_F( ResourceFilterTest, ParsePrefixMap_InvalidMissingColon ) +TEST_F( ResourceFilterTest, FilterPrefixMap_Invalid_MissingColon ) { try { @@ -404,7 +404,7 @@ TEST_F( ResourceFilterTest, ParsePrefixMap_InvalidMissingColon ) } } -TEST_F( ResourceFilterTest, ParsePrefixMap_InvalidEmptyPrefix ) +TEST_F( ResourceFilterTest, FilterPrefixMap_Invalid_EmptyPrefix ) { try { @@ -421,7 +421,7 @@ TEST_F( ResourceFilterTest, ParsePrefixMap_InvalidEmptyPrefix ) } } -TEST_F( ResourceFilterTest, ParsePrefixMap_InvalidNoPaths ) +TEST_F( ResourceFilterTest, FilterPrefixMap_Invalid_NoPaths ) { try { @@ -496,7 +496,7 @@ TEST_F( ResourceFilterTest, FilterDefaultSection_InitializeValid ) EXPECT_EQ( it2->first, it2->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; } -TEST_F( ResourceFilterTest, FilterDefaultSection_InitializeInvalidMissingColon ) +TEST_F( ResourceFilterTest, FilterDefaultSection_Initialize_InvalidMissingColon ) { try { @@ -513,7 +513,7 @@ TEST_F( ResourceFilterTest, FilterDefaultSection_InitializeInvalidMissingColon ) } } -TEST_F( ResourceFilterTest, FilterDefaultSection_InitializeInvalidEmptyPrefix ) +TEST_F( ResourceFilterTest, FilterDefaultSection_Initialize_InvalidEmptyPrefix ) { try { From 7b31e28baf6fab005e7868541354f22c2c3e498a Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 12 Jan 2026 17:38:19 +0000 Subject: [PATCH 028/124] Add FilterNamedSection tests --- tests/src/ResourceFilterTest.cpp | 374 +++++++++++++++++++++------ tools/include/FilterNamedSection.h | 6 +- tools/src/FilterNamedSection.cpp | 74 +++--- tools/src/FilterPrefixMapEntry.cpp | 6 + tools/src/FilterPrefixmap.cpp | 11 +- tools/src/FilterResourceFilter.cpp | 12 + tools/src/FilterResourcePathFile.cpp | 2 +- 7 files changed, 375 insertions(+), 110 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index d9c979d..af02b31 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -8,6 +8,7 @@ #include #include #include +#include TEST_F( ResourceFilterTest, Example1IniParsing ) { @@ -110,7 +111,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingIncludeBracketBef { // This test filter has a missing closing bracket for the first include filter, before the next exclude filter starts ResourceTools::FilterResourceFilter filter( "[ .in1 ! [ .ex1 ]" ); - FAIL() << "Expected std::invalid_argument (1)"; + FAIL() << "Expected std::invalid_argument to be thrown"; } catch( const std::invalid_argument& e ) { @@ -118,7 +119,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingIncludeBracketBef } catch( ... ) { - FAIL() << "Expected std::invalid_argument (2)"; + FAIL() << "Expected std::invalid_argument when constructing FilterResourceFilter"; } } @@ -127,7 +128,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ExcludeMarkerWithoutBracket_v1 try { ResourceTools::FilterResourceFilter filter( "! .ex1 ]" ); - FAIL() << "Expected std::invalid_argument (1)"; + FAIL() << "Expected std::invalid_argument to be thrown"; } catch( const std::invalid_argument& e ) { @@ -135,7 +136,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ExcludeMarkerWithoutBracket_v1 } catch( ... ) { - FAIL() << "Expected std::invalid_argument (2)"; + FAIL() << "Expected std::invalid_argument when constructing FilterResourceFilter"; } } @@ -144,7 +145,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ExcludeMarkerWithoutBracket_v2 try { ResourceTools::FilterResourceFilter filter( " [ .in1 ] ! .ex1 ]" ); - FAIL() << "Expected std::invalid_argument (1)"; + FAIL() << "Expected std::invalid_argument to be thrown"; } catch( const std::invalid_argument& e ) { @@ -152,7 +153,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ExcludeMarkerWithoutBracket_v2 } catch( ... ) { - FAIL() << "Expected std::invalid_argument (2)"; + FAIL() << "Expected std::invalid_argument when constructing FilterResourceFilter"; } } @@ -161,7 +162,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ExcludeMarkerWithoutBracket_v3 try { ResourceTools::FilterResourceFilter filter( " [ .in1 ] ![ .ex1 ] ! " ); - FAIL() << "Expected std::invalid_argument (1)"; + FAIL() << "Expected std::invalid_argument to be thrown"; } catch( const std::invalid_argument& e ) { @@ -169,7 +170,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ExcludeMarkerWithoutBracket_v3 } catch( ... ) { - FAIL() << "Expected std::invalid_argument (2)"; + FAIL() << "Expected std::invalid_argument when constructing FilterResourceFilter"; } } @@ -178,7 +179,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_MissingOpeningBracket_v1 ) try { ResourceTools::FilterResourceFilter filter( ".in1 .in2 ]" ); - FAIL() << "Expected std::invalid_argument (1)"; + FAIL() << "Expected std::invalid_argument to be thrown"; } catch( const std::invalid_argument& e ) { @@ -186,7 +187,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_MissingOpeningBracket_v1 ) } catch( ... ) { - FAIL() << "Expected std::invalid_argument (2)"; + FAIL() << "Expected std::invalid_argument when constructing FilterResourceFilter"; } } @@ -195,7 +196,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_MissingOpeningBracket_v2 ) try { ResourceTools::FilterResourceFilter filter( " [ .in1 .in2 ] .in3 ] " ); - FAIL() << "Expected std::invalid_argument (1)"; + FAIL() << "Expected std::invalid_argument to be thrown"; } catch( const std::invalid_argument& e ) { @@ -203,7 +204,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_MissingOpeningBracket_v2 ) } catch( ... ) { - FAIL() << "Expected std::invalid_argument (2)"; + FAIL() << "Expected std::invalid_argument when constructing FilterResourceFilter"; } } @@ -212,7 +213,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingBracket_v1 ) try { ResourceTools::FilterResourceFilter filter( "[ .in1 .in2 " ); - FAIL() << "Expected std::invalid_argument (1)"; + FAIL() << "Expected std::invalid_argument to be thrown"; } catch( const std::invalid_argument& e ) { @@ -220,7 +221,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingBracket_v1 ) } catch( ... ) { - FAIL() << "Expected std::invalid_argument (2)"; + FAIL() << "Expected std::invalid_argument when constructing FilterResourceFilter"; } } @@ -229,7 +230,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingBracket_v2 ) try { ResourceTools::FilterResourceFilter filter( "[ .in1 .in2 ] [ .in3 " ); - FAIL() << "Expected std::invalid_argument (1)"; + FAIL() << "Expected std::invalid_argument to be thrown"; } catch( const std::invalid_argument& e ) { @@ -237,7 +238,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingBracket_v2 ) } catch( ... ) { - FAIL() << "Expected std::invalid_argument (2)"; + FAIL() << "Expected std::invalid_argument when constructing FilterResourceFilter"; } } @@ -246,7 +247,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingBracket_v3 ) try { ResourceTools::FilterResourceFilter filter( "[ .in1 .in2 ] [ .in3 [ .in4 ]" ); - FAIL() << "Expected std::invalid_argument (1)"; + FAIL() << "Expected std::invalid_argument to be thrown"; } catch( const std::invalid_argument& e ) { @@ -254,7 +255,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingBracket_v3 ) } catch( ... ) { - FAIL() << "Expected std::invalid_argument (2)"; + FAIL() << "Expected std::invalid_argument when constructing FilterResourceFilter"; } } @@ -392,7 +393,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_Invalid_MissingColon ) try { ResourceTools::FilterPrefixMap prefixmap( "prefix1/path1" ); - FAIL() << "Expected std::invalid_argument (1)"; + FAIL() << "Expected std::invalid_argument to be thrown"; } catch( const std::invalid_argument& e ) { @@ -400,7 +401,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_Invalid_MissingColon ) } catch( ... ) { - FAIL() << "Expected std::invalid_argument (2)"; + FAIL() << "Expected std::invalid_argument when constructing FilterPrefixMap"; } } @@ -409,7 +410,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_Invalid_EmptyPrefix ) try { ResourceTools::FilterPrefixMap prefixmap( ":/path1" ); - FAIL() << "Expected std::invalid_argument (1)"; + FAIL() << "Expected std::invalid_argument to be thrown"; } catch( const std::invalid_argument& e ) { @@ -417,7 +418,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_Invalid_EmptyPrefix ) } catch( ... ) { - FAIL() << "Expected std::invalid_argument (2)"; + FAIL() << "Expected std::invalid_argument when constructing FilterPrefixMap"; } } @@ -426,7 +427,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_Invalid_NoPaths ) try { ResourceTools::FilterPrefixMap prefixmap( "prefix1:" ); - FAIL() << "Expected std::invalid_argument (1)"; + FAIL() << "Expected std::invalid_argument to be thrown"; } catch( const std::invalid_argument& e ) { @@ -434,7 +435,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_Invalid_NoPaths ) } catch( ... ) { - FAIL() << "Expected std::invalid_argument (2)"; + FAIL() << "Expected std::invalid_argument when constructing FilterPrefixMap"; } } @@ -444,7 +445,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMapEntry_PrefixMismatchOnAppend ) { ResourceTools::FilterPrefixMapEntry entry( "prefix1", "/path1" ); entry.AppendPaths( "prefix2", "/path2" ); - FAIL() << "Expected std::invalid_argument (1)"; + FAIL() << "Expected std::invalid_argument to be thrown"; } catch( const std::invalid_argument& e ) { @@ -452,7 +453,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMapEntry_PrefixMismatchOnAppend ) } catch( ... ) { - FAIL() << "Expected std::invalid_argument (2)"; + FAIL() << "Expected std::invalid_argument when appending paths to FilterPrefixMapEntry"; } } @@ -461,7 +462,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMapEntry_InvalidNoPathsOnAppend ) try { ResourceTools::FilterPrefixMapEntry entry( "prefix1", "" ); // Empty string for paths - FAIL() << "Expected std::invalid_argument (1)"; + FAIL() << "Expected std::invalid_argument to be thrown"; } catch( const std::invalid_argument& e ) { @@ -469,7 +470,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMapEntry_InvalidNoPathsOnAppend ) } catch( ... ) { - FAIL() << "Expected std::invalid_argument (2)"; + FAIL() << "Expected std::invalid_argument when constructing FilterPrefixMapEntry with no paths"; } } @@ -501,7 +502,7 @@ TEST_F( ResourceFilterTest, FilterDefaultSection_Initialize_InvalidMissingColon try { ResourceTools::FilterDefaultSection defaultSection( "prefix1/path1" ); - FAIL() << "Expected std::invalid_argument (1)"; + FAIL() << "Expected std::invalid_argument to be thrown"; } catch( const std::invalid_argument& e ) { @@ -509,7 +510,7 @@ TEST_F( ResourceFilterTest, FilterDefaultSection_Initialize_InvalidMissingColon } catch( ... ) { - FAIL() << "Expected std::invalid_argument (2)"; + FAIL() << "Expected std::invalid_argument when constructing FilterDefaultSection"; } } @@ -518,7 +519,7 @@ TEST_F( ResourceFilterTest, FilterDefaultSection_Initialize_InvalidEmptyPrefix ) try { ResourceTools::FilterDefaultSection defaultSection( ":/path1" ); - FAIL() << "Expected std::invalid_argument (1)"; + FAIL() << "Expected std::invalid_argument to be thrown"; } catch( const std::invalid_argument& e ) { @@ -526,12 +527,50 @@ TEST_F( ResourceFilterTest, FilterDefaultSection_Initialize_InvalidEmptyPrefix ) } catch( ... ) { - FAIL() << "Expected std::invalid_argument (2)"; + FAIL() << "Expected std::invalid_argument when constructing FilterDefaultSection"; } } // ----------------------------------------- +void MapContainsPaths( const std::set& allExpectedPaths, + const std::map& resolvedMap, + const std::string messagePrefix = "" ) +{ + for( const auto& path : allExpectedPaths ) + { + EXPECT_TRUE( resolvedMap.find( path ) != resolvedMap.end() ) << messagePrefix << " - Expected path not found in resolved map: " << path; + } + if( resolvedMap.size() != allExpectedPaths.size() ) + { + FAIL() << messagePrefix << " - Resolved map size (" << resolvedMap.size() << ") does not match expected paths size (" << allExpectedPaths.size() << ")"; + } +} + +void ValidatePathMap( const std::set& expectedPaths, + const std::map& resolvedPathMap, + const std::vector& expectedIncludes, + const std::vector& expectedExcludes, + const std::string messagePrefix = "" ) +{ + for( const auto& p : expectedPaths ) + { + EXPECT_TRUE( resolvedPathMap.count( p ) ) << messagePrefix << " - Expected path not found in resolved path map: " << p; + } + + for( const auto& kv : resolvedPathMap ) + { + // Ignore resolved paths that are not in the expectedPaths set (useful when checking partial expectedPaths, because of include/exclude overrides from default) + if( expectedPaths.find( kv.first ) == expectedPaths.end() ) + { + continue; + } + EXPECT_EQ( kv.second.GetIncludeFilter(), expectedIncludes ) << messagePrefix << " - Include filter does not match expected for path: " << kv.first; + EXPECT_EQ( kv.second.GetExcludeFilter(), expectedExcludes ) << messagePrefix << " - Exclude filter does not match expected for path: " << kv.first; + } +} + + TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_NoFilter ) { std::string prefixMapStr = "prefix1:/path1;../path2"; @@ -548,16 +587,7 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_NoFilter ) std::vector expectedIncludes = { ".in1", ".in2" }; std::vector expectedExcludes = { ".ex1" }; - for( const auto& p : expectedPaths ) - { - EXPECT_TRUE( resolvedPathMap.count( p ) ); - } - - for( const auto& kv : resolvedPathMap ) - { - EXPECT_EQ( kv.second.GetIncludeFilter(), expectedIncludes ); - EXPECT_EQ( kv.second.GetExcludeFilter(), expectedExcludes ); - } + ValidatePathMap( expectedPaths, resolvedPathMap, expectedIncludes, expectedExcludes ); } TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_InlineIncludeExclude ) @@ -576,16 +606,7 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_InlineIncludeExclu std::vector expectedIncludes = { ".in1", ".in2", ".inLine1" }; std::vector expectedExcludes = { ".ex1", ".exLine1" }; - for( const auto& p : expectedPaths ) - { - EXPECT_TRUE( resolvedPathMap.count( p ) ); - } - - for( const auto& kv : resolvedPathMap ) - { - EXPECT_EQ( kv.second.GetIncludeFilter(), expectedIncludes ); - EXPECT_EQ( kv.second.GetExcludeFilter(), expectedExcludes ); - } + ValidatePathMap( expectedPaths, resolvedPathMap, expectedIncludes, expectedExcludes ); } TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_InlineOverridesParentFilter ) @@ -605,16 +626,7 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_InlineOverridesPar std::vector expectedIncludes = { ".parIn1", ".lineIn1", ".parEx1" }; std::vector expectedExcludes = { ".parIn2", ".lineEx1" }; - for( const auto& p : expectedPaths ) - { - EXPECT_TRUE( resolvedPathMap.count( p ) ); - } - - for( const auto& kv : resolvedPathMap ) - { - EXPECT_EQ( kv.second.GetIncludeFilter(), expectedIncludes ); - EXPECT_EQ( kv.second.GetExcludeFilter(), expectedExcludes ); - } + ValidatePathMap( expectedPaths, resolvedPathMap, expectedIncludes, expectedExcludes ); } TEST_F( ResourceFilterTest, FilterResourcePathFile_MultiLine_MixedFiltersWithOverrides ) @@ -685,16 +697,7 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_DuplicateOverrides std::vector expectedIncludes = { ".parIn1", ".parIn2", ".lineIn1" }; std::vector expectedExcludes = { ".parEx1", ".lineEx1" }; - for( const auto& p : expectedPaths ) - { - EXPECT_TRUE( resolvedPathMap.count( p ) ); - } - - for( const auto& kv : resolvedPathMap ) - { - EXPECT_EQ( kv.second.GetIncludeFilter(), expectedIncludes ); - EXPECT_EQ( kv.second.GetExcludeFilter(), expectedExcludes ); - } + ValidatePathMap( expectedPaths, resolvedPathMap, expectedIncludes, expectedExcludes ); } TEST_F( ResourceFilterTest, FilterResourcePathFile_Invalid_MissingPrefix ) @@ -731,3 +734,230 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_Invalid_MalformedInlineFilter } // ----------------------------------------- + +TEST_F( ResourceFilterTest, FilterNamedSection_Valid_SingleLineRespath ) +{ + std::string sectionName = "FilterNamedSection_Valid_SingleLineRespath"; + std::string filter = "[ .in1 .in2 ] ![ .ex1 ]"; + std::string respaths = "testPrefix:/foo/bar"; + std::string defaultParentPrefixMapStr = "testPrefix:/myPath"; + + ResourceTools::FilterPrefixMap defaultPrefixMap( defaultParentPrefixMapStr ); + ResourceTools::FilterNamedSection namedSection( sectionName, filter, respaths, "", defaultPrefixMap ); + + // Expected values: + std::set expectedPaths = { "/myPath/foo/bar" }; + std::vector expectedIncludes = { ".in1", ".in2" }; + std::vector expectedExcludes = { ".ex1" }; + + const auto& resolvedRespathMap = namedSection.GetResolvedRespathsMap(); + const auto& resolvedResfileMap = namedSection.GetResolvedResfileMap(); + const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); + + ValidatePathMap( expectedPaths, resolvedRespathMap, expectedIncludes, expectedExcludes, "ResolvedRespathsMap" ); + EXPECT_TRUE( resolvedResfileMap.empty() ); + ValidatePathMap( expectedPaths, combinedMap, expectedIncludes, expectedExcludes, "CombinedResolvedPathMap" ); +} + + + +TEST_F( ResourceFilterTest, FilterNamedSection_Valid_MultiLineRespath ) +{ + std::string sectionName = "FilterNamedSection_Valid_MultiLineRespath"; + std::string filter = "[ .in1 .in2 ] ![ .ex1 ]"; + std::string respaths = + "prefix1:/firstLine [ .inLine1 ] ![ .exLine1 ]\n" // Add entries to both include and exclude filters + "prefix2:/secondLine"; // Just using vanilla parent filter + std::string defaultParentPrefixMapStr = "prefix1:/pathA;/pathB prefix2:/path2"; + + ResourceTools::FilterPrefixMap defaultPrefixMap( defaultParentPrefixMapStr ); + ResourceTools::FilterNamedSection namedSection( sectionName, filter, respaths, "", defaultPrefixMap ); + + // Expected values: + std::set allExpectedPaths = { "/pathA/firstLine", "/pathB/firstLine", "/path2/secondLine" }; + std::set firstLinePaths = { "/pathA/firstLine", "/pathB/firstLine" }; + std::set secondLinePaths = { "/path2/secondLine" }; + std::vector defaultIncludes = { ".in1", ".in2" }; + std::vector defaultExcludes = { ".ex1" }; + std::vector firstLineIncludes = { ".in1", ".in2", ".inLine1" }; + std::vector firstLineExcludes = { ".ex1", ".exLine1" }; + + const auto& resolvedRespathsMap = namedSection.GetResolvedRespathsMap(); + const auto& resolvedResfileMap = namedSection.GetResolvedResfileMap(); + const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); + + MapContainsPaths( allExpectedPaths, resolvedRespathsMap, "ResolvedRespathsMap" ); + ValidatePathMap( firstLinePaths, resolvedRespathsMap, firstLineIncludes, firstLineExcludes, "FirstLine ResolvedRespathsMap" ); + ValidatePathMap( secondLinePaths, resolvedRespathsMap, defaultIncludes, defaultExcludes, "SecondLine ResolvedRespathsMap" ); + EXPECT_TRUE( resolvedResfileMap.empty() ); + MapContainsPaths( allExpectedPaths, combinedMap, "CombinedResolvedMap" ); + ValidatePathMap( firstLinePaths, combinedMap, firstLineIncludes, firstLineExcludes, "FirstLine ResolvedRespathsMap" ); + ValidatePathMap( secondLinePaths, combinedMap, defaultIncludes, defaultExcludes, "SecondLine ResolvedRespathsMap" ); +} + +TEST_F( ResourceFilterTest, FilterNamedSection_Valid_RespathAndResfile ) +{ + std::string sectionName = "FilterNamedSection_Valid_RespathAndResfile"; + std::string defaultParentPrefixMapStr = "prefix1:/pathA;/pathB prefix2:/pathC"; + std::string filter = "[ .in1 .in2 ] ![ .ex1 ]"; + std::string respaths = "prefix1:/respaths"; + std::string resfile = "prefix2:/resfile"; + + ResourceTools::FilterPrefixMap defaultPrefixMap( defaultParentPrefixMapStr ); + ResourceTools::FilterNamedSection namedSection( sectionName, filter, respaths, resfile, defaultPrefixMap ); + + // Expected values: + std::set allExpectedPaths = { "/pathA/respaths", "/pathB/respaths", "/pathC/resfile" }; + std::set respathsPaths = { "/pathA/respaths", "/pathB/respaths" }; + std::set resfilesPaths = { "/pathC/resfile" }; + std::vector defaultIncludes = { ".in1", ".in2" }; + std::vector defaultExcludes = { ".ex1" }; + + const auto& respathsMap = namedSection.GetResolvedRespathsMap(); + const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); + + ASSERT_EQ( respathsMap.size(), 2 ); // prefix1 has two paths + MapContainsPaths( respathsPaths, respathsMap, "ResolvedRespathsMap" ); + ValidatePathMap( respathsPaths, respathsMap, defaultIncludes, defaultExcludes, "ResolvedRespathsMap" ); + + ASSERT_EQ( resfileMap.size(), 1 ); // prefix2 has one path + MapContainsPaths( resfilesPaths, resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( resfilesPaths, resfileMap, defaultIncludes, defaultExcludes, "ResolvedResfileMap" ); + + ASSERT_EQ( combinedMap.size(), 3 ); // 2 from respaths, 1 from resfile + MapContainsPaths( allExpectedPaths, combinedMap, "ResolvedCombinedMap" ); + ValidatePathMap( allExpectedPaths, combinedMap, defaultIncludes, defaultExcludes, "ResolvedCombinedMap" ); +} + + +TEST_F( ResourceFilterTest, FilterNamedSection_Valid_RespathSet_ResfileEmpty ) +{ + std::string sectionName = "FilterNamedSection_Valid_RespathSet_ResfileEmpty"; + std::string parentPrefixMapStr = "prefix1:/pathA"; + std::string filter = "[ .in1 .in2 ] ![ .ex1 ]"; + std::string respaths = "prefix1:/foo/bar"; + + ResourceTools::FilterPrefixMap defaultPrefixMap( parentPrefixMapStr ); + ResourceTools::FilterNamedSection namedSection( sectionName, filter, respaths, "", defaultPrefixMap ); + + // Expected values: + std::set onlyValidPaths = { "/pathA/foo/bar" }; + std::vector defaultIncludes = { ".in1", ".in2" }; + std::vector defaultExcludes = { ".ex1" }; + + const auto& respathsMap = namedSection.GetResolvedRespathsMap(); + const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); + + ASSERT_EQ( respathsMap.size(), 1 ); + MapContainsPaths( onlyValidPaths, respathsMap, "ResolvedRespathsMap" ); + ValidatePathMap( onlyValidPaths, respathsMap, defaultIncludes, defaultExcludes, "ResolvedRespathsMap" ); + + ASSERT_EQ( resfileMap.size(), 0 ); // Nothing in resfile + EXPECT_TRUE( resfileMap.empty() ); + + ASSERT_EQ( combinedMap.size(), 1 ); // 1 from respaths, 0 from resfile + MapContainsPaths( onlyValidPaths, combinedMap, "ResolvedCombinedMap" ); + ValidatePathMap( onlyValidPaths, combinedMap, defaultIncludes, defaultExcludes, "ResolvedCombinedMap" ); +} + +TEST_F( ResourceFilterTest, FilterNamedSection_Invalid_RespathMissing ) +{ + std::string sectionName = "FilterNamedSection_Invalid_RespathMissing"; + std::string defaultParentPrefixMapStr = "prefix1:/path1"; + std::string filter = "[ .in1 ]"; + std::string resfile = "prefix1:/foo/bar"; + + ResourceTools::FilterPrefixMap defaultPrefixMap( defaultParentPrefixMapStr ); + + // TODO: Should change code to throw defined error code/type + try + { + ResourceTools::FilterNamedSection namedSection( sectionName, filter, "", resfile, defaultPrefixMap ); + FAIL() << "Expected std::invalid_argument when constructing FilterNamedSection with missing respaths"; + } + catch( const std::invalid_argument& e ) + { + std::string errorString = "Respaths attribute is empty for section: " + sectionName; + EXPECT_STREQ( e.what(), errorString.c_str() ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument to be thrown"; + } +} + +TEST_F( ResourceFilterTest, FilterNamedSection_Valid_CombinedResolvedMap ) +{ + std::string sectionName = "FilterNamedSection_Valid_CombinedResolvedMap"; + std::string defaultParentPrefixMapStr = "prefixA:/path1"; + std::string filter = "[ .in1 ]"; + std::string respaths = "prefixA:/foo/bar"; + std::string resfile = "prefixA:/loo/car"; + + ResourceTools::FilterPrefixMap defaultPrefixMap( defaultParentPrefixMapStr ); + ResourceTools::FilterNamedSection namedSection( sectionName, filter, respaths, resfile, defaultPrefixMap ); + + // Expected values: + std::set combinedPaths = { "/path1/foo/bar", "/path1/loo/car" }; + std::set respathsPaths = { "/path1/foo/bar" }; + std::set resfilesPaths = { "/path1/loo/car" }; + std::vector defaultIncludes = { ".in1" }; + std::vector defaultExcludes = {}; + + const auto& respathsMap = namedSection.GetResolvedRespathsMap(); + const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); + + ASSERT_EQ( respathsMap.size(), 1 ); // "/path1/foo/bar" + MapContainsPaths( respathsPaths, respathsMap, "ResolvedRespathsMap" ); + ValidatePathMap( respathsPaths, respathsMap, defaultIncludes, defaultExcludes, "ResolvedRespathsMap" ); + + ASSERT_EQ( resfileMap.size(), 1 ); // "/path1/loo/bar" + MapContainsPaths( resfilesPaths, resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( resfilesPaths, resfileMap, defaultIncludes, defaultExcludes, "ResolvedResfileMap" ); + + ASSERT_EQ( combinedMap.size(), 2 ); // both + MapContainsPaths( combinedPaths, combinedMap, "ResolvedCombinedMap" ); + ValidatePathMap( combinedPaths, combinedMap, defaultIncludes, defaultExcludes, "ResolvedCombinedMap" ); +} + +TEST_F( ResourceFilterTest, FilterNamedSection_Valid_CombinedResolvedMap_OverwrittenByResfileMap ) +{ + std::string sectionName = "FilterNamedSection_Valid_CombinedResolvedMap_OverwrittenByResfileMap"; + std::string defaultParentPrefixMapStr = "prefix1:/pathA;/pathB"; + std::string filter = "[ .in1 .in2 ] ![ .ex1 ]"; + std::string respaths = "prefix1:/foo/bar"; + std::string resfile = "prefix1:/foo/bar [ .extra ]"; // Same path, extra include filter + + ResourceTools::FilterPrefixMap defaultPrefixMap( defaultParentPrefixMapStr ); + ResourceTools::FilterNamedSection namedSection( sectionName, filter, respaths, resfile, defaultPrefixMap ); + + // Expected values: + std::set allPaths = { "/pathA/foo/bar", "/pathB/foo/bar" }; + std::vector defaultIncludes = { ".in1", ".in2" }; + std::vector allExcludes = { ".ex1" }; + std::vector overrideIncludes = { ".in1", ".in2", ".extra" }; + + const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); + const auto& respathsMap = namedSection.GetResolvedRespathsMap(); + const auto& resfileMap = namedSection.GetResolvedResfileMap(); + + ASSERT_EQ( respathsMap.size(), 2 ); // "/path1/foo/bar" + MapContainsPaths( allPaths, respathsMap, "ResolvedRespathsMap" ); + ValidatePathMap( allPaths, respathsMap, defaultIncludes, allExcludes, "ResolvedRespathsMap" ); + + ASSERT_EQ( resfileMap.size(), 2 ); // "/path1/loo/bar" + MapContainsPaths( allPaths, resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( allPaths, resfileMap, overrideIncludes, allExcludes, "ResolvedResfileMap" ); + + ASSERT_EQ( combinedMap.size(), 2 ); // Both, same count but now with overrides + MapContainsPaths( allPaths, combinedMap, "ResolvedCombinedMap" ); + ValidatePathMap( allPaths, combinedMap, overrideIncludes, allExcludes, "ResolvedCombinedMap" ); + + // Re-validate that RespathsMap is unchanged + const auto& respathsAgainMap = namedSection.GetResolvedRespathsMap(); + MapContainsPaths( allPaths, respathsAgainMap, "ResolvedRespathsMap-Again" ); + ValidatePathMap( allPaths, respathsAgainMap, defaultIncludes, allExcludes, "ResolvedRespathsMap-Again" ); +} diff --git a/tools/include/FilterNamedSection.h b/tools/include/FilterNamedSection.h index a47a896..9702634 100644 --- a/tools/include/FilterNamedSection.h +++ b/tools/include/FilterNamedSection.h @@ -23,8 +23,10 @@ class FilterNamedSection const std::string& resfile, const FilterPrefixMap& parentPrefixMap ); + const std::string& GetSectionName() const; + // Return combined resolved path map from both respaths and optional resfile - const std::map& GetCombinedResolvedPathMap() const; + const std::map& GetCombinedResolvedPathMap(); const std::map& GetResolvedRespathsMap() const; @@ -43,8 +45,6 @@ class FilterNamedSection // Combined map of fully resolved respaths and resfile FilterResourceFilter objects std::map m_resolvedCombinedPathMap; - - void ParseNamedSection(); }; } diff --git a/tools/src/FilterNamedSection.cpp b/tools/src/FilterNamedSection.cpp index 173409b..88cf211 100644 --- a/tools/src/FilterNamedSection.cpp +++ b/tools/src/FilterNamedSection.cpp @@ -17,16 +17,55 @@ FilterNamedSection::FilterNamedSection( std::string sectionName, m_respaths( respaths, parentPrefixMap, m_filter ), m_resfile( resfile.empty() ? std::nullopt : std::make_optional( resfile, parentPrefixMap, m_filter ) ) { + m_resolvedCombinedPathMap.clear(); + if( respaths.empty() ) { + // TODO: Change this to a defined error code/type throw std::invalid_argument( "Respaths attribute is empty for section: " + m_sectionName ); } +} - ParseNamedSection(); +const std::string& FilterNamedSection::GetSectionName() const +{ + return m_sectionName; } -const std::map& FilterNamedSection::GetCombinedResolvedPathMap() const +const std::map& FilterNamedSection::GetCombinedResolvedPathMap() { + + // Only populate the Combined map if not already done so. + if ( m_resolvedCombinedPathMap.empty() ) + { + // Populate the combined map. + for( const auto& kv : m_respaths.GetResolvedPathMap() ) + { + m_resolvedCombinedPathMap.insert_or_assign( kv.first, kv.second ); + } + + // Add resfile to the combined map + if( m_resfile ) + { + // Allow "resfile" to contain multiple entries (future proofing) + for( const auto& kv : m_resfile->GetResolvedPathMap() ) + { + // Combine filters of both if same key already exists + auto it = m_resolvedCombinedPathMap.find( kv.first ); + if( it != m_resolvedCombinedPathMap.end() ) + { + // Combine the filters (using raw filter strings) + std::string combinedRawFilter = it->second.GetRawFilter() + " " + kv.second.GetRawFilter(); + FilterResourceFilter combinedFilter( combinedRawFilter ); + m_resolvedCombinedPathMap.insert_or_assign( kv.first, combinedFilter ); + } + else + { + m_resolvedCombinedPathMap.insert_or_assign( kv.first, kv.second ); + } + } + } + } + return m_resolvedCombinedPathMap; } @@ -47,35 +86,4 @@ const std::map& FilterNamedSection::GetResolv return emptyResfileMap; } -void FilterNamedSection::ParseNamedSection() -{ - // Populate the combined map. - for( const auto& kv : m_respaths.GetResolvedPathMap() ) - { - m_resolvedCombinedPathMap[kv.first] = kv.second; - } - - // Add resfile to the combined map - if( m_resfile ) - { - // Allow "resfile" to contain multiple entries (future proofing) - for( const auto& kv : m_resfile->GetResolvedPathMap() ) - { - // Combine filters of both if same key already exists - auto it = m_resolvedCombinedPathMap.find( kv.first ); - if( it != m_resolvedCombinedPathMap.end() ) - { - // Combine the filters (using raw filter strings) - std::string combinedRawFilter = it->second.GetRawFilter() + " " + kv.second.GetRawFilter(); - FilterResourceFilter combinedFilter( combinedRawFilter ); - m_resolvedCombinedPathMap[kv.first] = combinedFilter; - } - else - { - m_resolvedCombinedPathMap[kv.first] = kv.second; - } - } - } -} - } diff --git a/tools/src/FilterPrefixMapEntry.cpp b/tools/src/FilterPrefixMapEntry.cpp index 4688b32..cb1232b 100644 --- a/tools/src/FilterPrefixMapEntry.cpp +++ b/tools/src/FilterPrefixMapEntry.cpp @@ -16,7 +16,10 @@ FilterPrefixMapEntry::FilterPrefixMapEntry( const std::string& prefix, const std void FilterPrefixMapEntry::AppendPaths( const std::string& prefix, const std::string& rawPaths ) { if( prefix != m_prefix ) + { + // TODO: Change this to a defined error code/type throw std::invalid_argument( "Prefix mismatch while appending path(s): " + prefix + " (incoming) != " + m_prefix + " (existing)" ); + } std::size_t pos = 0; while( pos < rawPaths.size() ) @@ -32,7 +35,10 @@ void FilterPrefixMapEntry::AppendPaths( const std::string& prefix, const std::st pos = semicolon + 1; } if( m_paths.empty() ) + { + // TODO: Change this to a defined error code/type throw std::invalid_argument( "Invalid prefixmap format: No paths appended for prefix: " + m_prefix ); + } } const std::string& FilterPrefixMapEntry::GetPrefix() const diff --git a/tools/src/FilterPrefixmap.cpp b/tools/src/FilterPrefixmap.cpp index cc76fa5..e81bbdf 100644 --- a/tools/src/FilterPrefixmap.cpp +++ b/tools/src/FilterPrefixmap.cpp @@ -28,11 +28,17 @@ void FilterPrefixMap::ParsePrefixMap( const std::string& rawPrefixMap ) // Find the prefix (or error out if missing a colon) std::size_t colon = rawPrefixMap.find( ':', pos ); if( colon == std::string::npos ) + { + // TODO: Change this to a defined error code/type throw std::invalid_argument( "Invalid prefixmap format: missing ':'" ); + } std::string prefix = rawPrefixMap.substr( pos, colon - pos ); if( prefix.empty() ) + { + // TODO: Change this to a defined error code/type throw std::invalid_argument( "Invalid prefixmap format: empty prefix" ); + } // Move position past the colon pos = colon + 1; @@ -42,13 +48,16 @@ void FilterPrefixMap::ParsePrefixMap( const std::string& rawPrefixMap ) std::string rawPaths = ( nextSpace == std::string::npos ) ? rawPrefixMap.substr( pos ) : rawPrefixMap.substr( pos, nextSpace - pos ); if( rawPaths.empty() ) + { + // TODO: Change this to a defined error code/type throw std::invalid_argument( "Invalid prefixmap format: No paths defined for prefix: " + prefix ); + } auto it = m_prefixMap.find( prefix ); if( it == m_prefixMap.end() ) { // Prefix doesn't exist, create a map entry for it. - m_prefixMap.emplace( prefix, FilterPrefixMapEntry( prefix, rawPaths ) ); + m_prefixMap.insert_or_assign( prefix, FilterPrefixMapEntry( prefix, rawPaths ) ); } else { diff --git a/tools/src/FilterResourceFilter.cpp b/tools/src/FilterResourceFilter.cpp index c2809eb..d9f373b 100644 --- a/tools/src/FilterResourceFilter.cpp +++ b/tools/src/FilterResourceFilter.cpp @@ -65,20 +65,32 @@ void FilterResourceFilter::ParseFilters() while( pos < s.size() && std::isspace( static_cast( s[pos] ) ) ) ++pos; if( pos >= s.size() ) + { + // TODO: Change this to a defined error code/type throw std::invalid_argument( "Invalid filter format: exclude filter marker found without a [ token ] section" ); + } } if( pos >= s.size() || s[pos] != '[' ) + { + // TODO: Change this to a defined error code/type throw std::invalid_argument( "Invalid filter format: missing '['" ); + } ++pos; // skip '[' size_t endBracket = s.find( ']', pos ); size_t nextStartBracket = s.find( '[', pos ); if( nextStartBracket != std::string::npos && nextStartBracket < endBracket ) + { + // TODO: Change this to a defined error code/type throw std::invalid_argument( "Invalid filter format: matching end bracket ']' not present before the next start bracket '['" ); + } if( endBracket == std::string::npos ) + { + // TODO: Change this to a defined error code/type throw std::invalid_argument( "Invalid filter format: missing ']'" ); + } std::string entries = s.substr( pos, endBracket - pos ); std::istringstream iss( entries ); diff --git a/tools/src/FilterResourcePathFile.cpp b/tools/src/FilterResourcePathFile.cpp index c76a94f..9ab1d0d 100644 --- a/tools/src/FilterResourcePathFile.cpp +++ b/tools/src/FilterResourcePathFile.cpp @@ -50,7 +50,7 @@ void FilterResourcePathFile::ParseRawPathFileAttribute() const auto entryFilter = lineEntry.GetEntryFilter(); for( const auto& path : resolvedPaths ) { - m_resolvedPathMap.emplace( path, entryFilter ); + m_resolvedPathMap.insert_or_assign( path, entryFilter ); } } } From 073a220f97754ae455f18162b4b068b925a0d6bc Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 13 Jan 2026 15:38:42 +0000 Subject: [PATCH 029/124] Add FilterResourceFile class - Do some function name changes to reduce stuttering. --- tests/src/ResourceFilterTest.cpp | 76 +++++++++---------- tools/include/FilterDefaultSection.h | 4 +- tools/include/FilterPrefixmap.h | 6 +- tools/include/FilterResourceFile.h | 12 ++- tools/src/FilterDefaultSection.cpp | 4 +- tools/src/FilterPrefixmap.cpp | 12 +-- tools/src/FilterResourceFile.cpp | 91 +++++++++++++++++++++-- tools/src/FilterResourcePathFileEntry.cpp | 6 +- 8 files changed, 153 insertions(+), 58 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index af02b31..4a086b7 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -21,10 +21,10 @@ TEST_F( ResourceFilterTest, Example1IniParsing ) EXPECT_EQ( reader.Sections().size(), 2 ); // Check [default] section - ASSERT_TRUE( reader.HasSection( "default" ) ); - EXPECT_EQ( reader.Get( "default", "prefixmap", "" ), "res:./Indicies;./resourcesOnBranch res2:./ResourceGroups" ); - EXPECT_EQ( reader.Get( "default", "version", "" ), "1.2" ); - EXPECT_EQ( reader.Keys( "default" ).size(), 2 ); + ASSERT_TRUE( reader.HasSection( "DEFAULT" ) ); + EXPECT_EQ( reader.Get( "DEFAULT", "prefixmap", "" ), "res:./Indicies;./resourcesOnBranch res2:./ResourceGroups" ); + EXPECT_EQ( reader.Get( "DEFAULT", "version", "" ), "1.2" ); + EXPECT_EQ( reader.Keys( "DEFAULT" ).size(), 2 ); // Check [testyamlfilesovermultilinerespaths] section ASSERT_TRUE( reader.HasSection( "testYamlFilesOverMultiLineResPathsWithEmptyLines" ) ); @@ -286,12 +286,12 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_CondensedValidFilterStringv2 ) TEST_F( ResourceFilterTest, FilterPrefixMap_SinglePrefixMultiplePaths ) { ResourceTools::FilterPrefixMap map( "prefix1:/somePath;../otherPath" ); - const auto& prefixMap = map.GetPrefixMap(); - ASSERT_EQ( prefixMap.size(), 1 ) << "There should only be 1 prefix in the map"; + const auto& prefixMapEntries = map.GetMapEntries(); + ASSERT_EQ( prefixMapEntries.size(), 1 ) << "There should only be 1 prefix in the map"; // If iterator is at end, the prefix was not found - auto it = prefixMap.find( "prefix1" ); - ASSERT_NE( it, prefixMap.end() ) << "Prefix 'prefix1' not found in the map"; + auto it = prefixMapEntries.find( "prefix1" ); + ASSERT_NE( it, prefixMapEntries.end() ) << "Prefix 'prefix1' not found in the map"; std::set expected = { "/somePath", "../otherPath" }; EXPECT_EQ( it->second.GetPrefix(), "prefix1" ) << "Prefix should be 'prefix1'"; @@ -302,14 +302,14 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_SinglePrefixMultiplePaths ) TEST_F( ResourceFilterTest, FilterPrefixMap_MultiplePrefixes ) { ResourceTools::FilterPrefixMap map( "prefix1:/path1;/path2 prefix2:/newPath1" ); - const auto& prefixMap = map.GetPrefixMap(); - ASSERT_EQ( prefixMap.size(), 2 ) << "There should be 2 prefixes in the map"; + const auto& prefixMapEntries = map.GetMapEntries(); + ASSERT_EQ( prefixMapEntries.size(), 2 ) << "There should be 2 prefixes in the map"; // Make sure both prefixes exist - auto it1 = prefixMap.find( "prefix1" ); - auto it2 = prefixMap.find( "prefix2" ); - ASSERT_NE( it1, prefixMap.end() ) << "Prefix 'prefix1' not found in the map"; - ASSERT_NE( it2, prefixMap.end() ) << "Prefix 'prefix2' not found in the map"; + auto it1 = prefixMapEntries.find( "prefix1" ); + auto it2 = prefixMapEntries.find( "prefix2" ); + ASSERT_NE( it1, prefixMapEntries.end() ) << "Prefix 'prefix1' not found in the map"; + ASSERT_NE( it2, prefixMapEntries.end() ) << "Prefix 'prefix2' not found in the map"; std::set expected1 = { "/path1", "/path2" }; std::set expected2 = { "/newPath1" }; @@ -326,10 +326,10 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_DuplicateSamePrefixPathsInDifferentO ResourceTools::FilterPrefixMap map( "prefix1:/path1;/path2 prefix1:/path2;/path1" ); // There should only be one prefix (prefix1) - const auto& prefixMap = map.GetPrefixMap(); - ASSERT_EQ( prefixMap.size(), 1 ) << "There should only be 1 prefix in the map"; - auto it = prefixMap.find( "prefix1" ); - ASSERT_NE( it, prefixMap.end() ) << "Prefix 'prefix1' not found in the map"; + const auto& prefixMapEntries = map.GetMapEntries(); + ASSERT_EQ( prefixMapEntries.size(), 1 ) << "There should only be 1 prefix in the map"; + auto it = prefixMapEntries.find( "prefix1" ); + ASSERT_NE( it, prefixMapEntries.end() ) << "Prefix 'prefix1' not found in the map"; // There should be only 2 paths, sorted in set std::set expected_a = { "/path1", "/path2" }; @@ -343,12 +343,12 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_DuplicateSamePrefixPathsInDifferentO TEST_F( ResourceFilterTest, FilterPrefixMap_MultiplePrefixesAppendToPaths ) { ResourceTools::FilterPrefixMap map( "prefix1:/path2;/path1 prefix2:/otherPath1;/otherPath2 prefix1:/path3;/path1" ); - const auto& prefixMap = map.GetPrefixMap(); - ASSERT_EQ( prefixMap.size(), 2 ) << "There should be 2 prefixes in the map"; - auto it1 = prefixMap.find( "prefix1" ); - auto it2 = prefixMap.find( "prefix2" ); - ASSERT_NE( it1, prefixMap.end() ) << "Prefix 'prefix1' not found in the map"; - ASSERT_NE( it2, prefixMap.end() ) << "Prefix 'prefix2' not found in the map"; + const auto& prefixMapEntries = map.GetMapEntries(); + ASSERT_EQ( prefixMapEntries.size(), 2 ) << "There should be 2 prefixes in the map"; + auto it1 = prefixMapEntries.find( "prefix1" ); + auto it2 = prefixMapEntries.find( "prefix2" ); + ASSERT_NE( it1, prefixMapEntries.end() ) << "Prefix 'prefix1' not found in the map"; + ASSERT_NE( it2, prefixMapEntries.end() ) << "Prefix 'prefix2' not found in the map"; // Prefix1 should have 3 paths and prefix2 should have 2 std::set prefix1Paths = { "/path1", "/path2", "/path3" }; @@ -365,14 +365,14 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_DifferentWhitespacesBetweenPrefixes { std::string input = "prefix1:/path1\tprefixTab:/path2\nprefixNewLine:/path3"; ResourceTools::FilterPrefixMap map( input ); - const auto& prefixMap = map.GetPrefixMap(); - ASSERT_EQ( prefixMap.size(), 3 ) << "There should only be 3 prefix in the map"; - auto it1 = prefixMap.find( "prefix1" ); - auto it2 = prefixMap.find( "prefixTab" ); - auto it3 = prefixMap.find( "prefixNewLine" ); - EXPECT_NE( it1, prefixMap.end() ) << "Prefix 'prefix1' not found in the map"; - EXPECT_NE( it2, prefixMap.end() ) << "Prefix 'prefixTab' not found in the map"; - EXPECT_NE( it3, prefixMap.end() ) << "Prefix 'prefixNewLine' not found in the map"; + const auto& prefixMapEntries = map.GetMapEntries(); + ASSERT_EQ( prefixMapEntries.size(), 3 ) << "There should only be 3 prefix in the map"; + auto it1 = prefixMapEntries.find( "prefix1" ); + auto it2 = prefixMapEntries.find( "prefixTab" ); + auto it3 = prefixMapEntries.find( "prefixNewLine" ); + EXPECT_NE( it1, prefixMapEntries.end() ) << "Prefix 'prefix1' not found in the map"; + EXPECT_NE( it2, prefixMapEntries.end() ) << "Prefix 'prefixTab' not found in the map"; + EXPECT_NE( it3, prefixMapEntries.end() ) << "Prefix 'prefixNewLine' not found in the map"; std::set prefix1Paths = { "/path1" }; std::set prefixTabPaths = { "/path2" }; @@ -480,12 +480,12 @@ TEST_F( ResourceFilterTest, FilterDefaultSection_InitializeValid ) { std::string input = "prefix1:/path1;../path2 prefix2:/path3"; ResourceTools::FilterDefaultSection defaultSection( input ); - const auto& prefixMap = defaultSection.GetPrefixMap(); - ASSERT_EQ( prefixMap.size(), 2 ) << "There should be 2 prefixes in the map"; - auto it1 = prefixMap.find( "prefix1" ); - auto it2 = prefixMap.find( "prefix2" ); - ASSERT_NE( it1, prefixMap.end() ) << "Prefix 'prefix1' not found in the map"; - ASSERT_NE( it2, prefixMap.end() ) << "Prefix 'prefix2' not found in the map"; + const auto& prefixMapEntries = defaultSection.GetPrefixMap().GetMapEntries(); + ASSERT_EQ( prefixMapEntries.size(), 2 ) << "There should be 2 prefixes in the map"; + auto it1 = prefixMapEntries.find( "prefix1" ); + auto it2 = prefixMapEntries.find( "prefix2" ); + ASSERT_NE( it1, prefixMapEntries.end() ) << "Prefix 'prefix1' not found in the map"; + ASSERT_NE( it2, prefixMapEntries.end() ) << "Prefix 'prefix2' not found in the map"; std::set prefix1Paths = { "/path1", "../path2" }; std::set prefix2Paths = { "/path3" }; diff --git a/tools/include/FilterDefaultSection.h b/tools/include/FilterDefaultSection.h index e8a313d..3000b8d 100644 --- a/tools/include/FilterDefaultSection.h +++ b/tools/include/FilterDefaultSection.h @@ -12,9 +12,11 @@ namespace ResourceTools class FilterDefaultSection { public: + FilterDefaultSection() = default; + explicit FilterDefaultSection( const std::string& prefixmapStr ); - const std::map& GetPrefixMap() const; + const FilterPrefixMap& GetPrefixMap() const; private: FilterPrefixMap m_prefixMap; diff --git a/tools/include/FilterPrefixmap.h b/tools/include/FilterPrefixmap.h index 6836b16..bd066c9 100644 --- a/tools/include/FilterPrefixmap.h +++ b/tools/include/FilterPrefixmap.h @@ -15,13 +15,15 @@ namespace ResourceTools class FilterPrefixMap { public: + FilterPrefixMap() = default; + explicit FilterPrefixMap( const std::string& rawPrefixMap ); - const std::map& GetPrefixMap() const; + const std::map& GetMapEntries() const; private: // Map of prefixes to FilterPrefixMapEntry objects. - std::map m_prefixMap; + std::map m_prefixMapEntries; void ParsePrefixMap( const std::string& rawPrefixMap ); }; diff --git a/tools/include/FilterResourceFile.h b/tools/include/FilterResourceFile.h index 7d78391..4c447ad 100644 --- a/tools/include/FilterResourceFile.h +++ b/tools/include/FilterResourceFile.h @@ -3,6 +3,7 @@ #ifndef FILTERRESOURCEFILE_H #define FILTERRESOURCEFILE_H +#include #include #include #include @@ -13,12 +14,21 @@ namespace ResourceTools class FilterResourceFile { public: - explicit FilterResourceFile( const FilterDefaultSection& defaultSection, const std::vector& namedSections ); + explicit FilterResourceFile( const std::filesystem::path& iniFilePath ); + + const std::map& GetFullResolvedPathMap(); private: + std::filesystem::path m_iniFilePath; + FilterDefaultSection m_defaultSection; std::vector m_namedSections; + + // Resolved PathMap for all named sections defined in a resource .ini file + std::map m_fullResolvedPathMap; + + void ParseIniFile(); }; } diff --git a/tools/src/FilterDefaultSection.cpp b/tools/src/FilterDefaultSection.cpp index 7e8f7a9..9e10caa 100644 --- a/tools/src/FilterDefaultSection.cpp +++ b/tools/src/FilterDefaultSection.cpp @@ -11,9 +11,9 @@ FilterDefaultSection::FilterDefaultSection( const std::string& prefixmapStr ) : { } -const std::map& FilterDefaultSection::GetPrefixMap() const +const FilterPrefixMap& FilterDefaultSection::GetPrefixMap() const { - return m_prefixMap.GetPrefixMap(); + return m_prefixMap; } diff --git a/tools/src/FilterPrefixmap.cpp b/tools/src/FilterPrefixmap.cpp index e81bbdf..b9831cc 100644 --- a/tools/src/FilterPrefixmap.cpp +++ b/tools/src/FilterPrefixmap.cpp @@ -10,14 +10,14 @@ namespace ResourceTools FilterPrefixMap::FilterPrefixMap( const std::string& rawPrefixMap ) { - m_prefixMap.clear(); + m_prefixMapEntries.clear(); ParsePrefixMap( rawPrefixMap ); } -const std::map& FilterPrefixMap::GetPrefixMap() const +const std::map& FilterPrefixMap::GetMapEntries() const { - return m_prefixMap; + return m_prefixMapEntries; } void FilterPrefixMap::ParsePrefixMap( const std::string& rawPrefixMap ) @@ -53,11 +53,11 @@ void FilterPrefixMap::ParsePrefixMap( const std::string& rawPrefixMap ) throw std::invalid_argument( "Invalid prefixmap format: No paths defined for prefix: " + prefix ); } - auto it = m_prefixMap.find( prefix ); - if( it == m_prefixMap.end() ) + auto it = m_prefixMapEntries.find( prefix ); + if( it == m_prefixMapEntries.end() ) { // Prefix doesn't exist, create a map entry for it. - m_prefixMap.insert_or_assign( prefix, FilterPrefixMapEntry( prefix, rawPaths ) ); + m_prefixMapEntries.insert_or_assign( prefix, FilterPrefixMapEntry( prefix, rawPaths ) ); } else { diff --git a/tools/src/FilterResourceFile.cpp b/tools/src/FilterResourceFile.cpp index 6e53450..028326d 100644 --- a/tools/src/FilterResourceFile.cpp +++ b/tools/src/FilterResourceFile.cpp @@ -1,17 +1,98 @@ // Copyright © 2025 CCP ehf. #include - +#include #include namespace ResourceTools { -FilterResourceFile::FilterResourceFile( const FilterDefaultSection& defaultSection, const std::vector& namedSections ) : - m_defaultSection( defaultSection ), - m_namedSections( namedSections ) +FilterResourceFile::FilterResourceFile( const std::filesystem::path& iniFilePath ) : + m_iniFilePath( iniFilePath ) +{ + m_fullResolvedPathMap.clear(); + + ParseIniFile(); +} + +const std::map& FilterResourceFile::GetFullResolvedPathMap() +{ + if( m_fullResolvedPathMap.empty() ) + { + // Populate the full resolved path map from all named sections + for( auto& namedSection : m_namedSections ) + { + auto& sectionPathMap = namedSection.GetCombinedResolvedPathMap(); + for( const auto& kv : sectionPathMap ) + { + // Combine filters if the same path already exists + auto it = m_fullResolvedPathMap.find( kv.first ); + if( it != m_fullResolvedPathMap.end() ) + { + // Combine the filters (using raw filter strings) + std::string combinedRawFilter = it->second.GetRawFilter() + " " + kv.second.GetRawFilter(); + FilterResourceFilter combinedFilter( combinedRawFilter ); + m_fullResolvedPathMap.insert_or_assign( kv.first, combinedFilter ); + } + else + { + m_fullResolvedPathMap.insert_or_assign( kv.first, kv.second ); + } + } + } + } + + return m_fullResolvedPathMap; +} + +void FilterResourceFile::ParseIniFile() { - throw std::logic_error( "Not implemented yet exception" ); + // Open, read and parse the resource INI file. + INIReader reader( m_iniFilePath.string() ); + if( reader.ParseError() != 0 ) + { + // TODO: Change this to a defined error code/type + throw std::runtime_error( "Failed to parse INI file: " + m_iniFilePath.string() + " - " + reader.ParseErrorMessage() ); + } + + // Parse the [DEFAULT] section + if( !reader.HasSection( "DEFAULT" ) ) + { + // TODO: Change this to a defined error code/type + throw std::runtime_error( "Missing [DEFAULT] section in INI file: " + m_iniFilePath.string() ); + } + m_defaultSection = FilterDefaultSection( reader.Get( "DEFAULT", "prefixmap", "" ) ); + + + // Validate that non-DEFAULT section(s) exist + std::vector allSections = reader.Sections(); + if( allSections.size() <= 1 ) + { + // No namedSections defined + throw std::runtime_error( "No namedSections defined in INI file: " + m_iniFilePath.string() ); + } + + // Parse all other named sections + for( const auto& sectionName : reader.Sections() ) + { + if( sectionName == "default" || sectionName == "DEFAULT" ) + { + continue; // Already loaded, skip it + } + + std::string filter = reader.Get( sectionName, "filter", "" ); + std::string respaths = reader.Get( sectionName, "respaths", "" ); + std::string resfile = reader.Get( sectionName, "resfile", "" ); + + if( respaths.empty() ) + { + // TODO: Change this to a defined error code/type + throw std::invalid_argument( "Respaths attribute is empty for section: " + sectionName ); + } + + FilterNamedSection namedSection( sectionName, filter, respaths, resfile, m_defaultSection.GetPrefixMap() ); + m_namedSections.push_back( namedSection ); + } } } diff --git a/tools/src/FilterResourcePathFileEntry.cpp b/tools/src/FilterResourcePathFileEntry.cpp index 5881c89..bebf157 100644 --- a/tools/src/FilterResourcePathFileEntry.cpp +++ b/tools/src/FilterResourcePathFileEntry.cpp @@ -62,9 +62,9 @@ void FilterResourcePathFileEntry::ParseRawPathLine() std::string prefix = rawPathToken.substr( 0, colon ); std::string rest = rawPathToken.substr( colon + 1 ); - const auto& prefixMap = m_parentPrefixMap.GetPrefixMap(); - auto it = prefixMap.find( prefix ); - if( it == prefixMap.end() ) + const auto& prefixMapEntries = m_parentPrefixMap.GetMapEntries(); + auto it = prefixMapEntries.find( prefix ); + if( it == prefixMapEntries.end() ) { // TODO: Change this to a defined error code/type throw std::invalid_argument( std::string( "Prefix '" ) + prefix + "' not present in prefixMap for line: " + m_rawPathLine ); From c88598208686e7a349cb6c17751d6ba5226a1071 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 13 Jan 2026 16:12:19 +0000 Subject: [PATCH 030/124] Add test for FilterResourceFile class (example1.ini) --- tests/src/ResourceFilterTest.cpp | 43 ++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 4a086b7..9658075 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -9,6 +9,7 @@ #include #include #include +#include TEST_F( ResourceFilterTest, Example1IniParsing ) { @@ -759,8 +760,6 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_SingleLineRespath ) ValidatePathMap( expectedPaths, combinedMap, expectedIncludes, expectedExcludes, "CombinedResolvedPathMap" ); } - - TEST_F( ResourceFilterTest, FilterNamedSection_Valid_MultiLineRespath ) { std::string sectionName = "FilterNamedSection_Valid_MultiLineRespath"; @@ -961,3 +960,43 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_CombinedResolvedMap_Overwri MapContainsPaths( allPaths, respathsAgainMap, "ResolvedRespathsMap-Again" ); ValidatePathMap( allPaths, respathsAgainMap, defaultIncludes, allExcludes, "ResolvedRespathsMap-Again" ); } + +// ------------------------------------------ + +TEST_F( ResourceFilterTest, FilterResourceFile_Load_example1_ini ) +{ + // Use the test fixture's helper to get the absolute path + const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/example1.ini" ); + + try + { + ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + const auto& fullPathMap = resourceFile.GetFullResolvedPathMap(); + + // Validate the paths: + std::set expectedPaths = { + // From the respaths attribute: + "./Indicies/firstLine/...", // "res:/firstLine/..." + "./resourcesOnBranch/firstLine/...", // "res:/firstLine/..." + "./Indicies/secondLine/...", // "res:/secondLine/..." + "./resourcesOnBranch/secondLine/...", // "res:/secondLine/..." + "./ResourceGroups/thirdLine/...", // "res2:/thirdLine/..." + // From the resfile attribute: + "./Indicies/binaryFileIndex_v0_0_0.txt", // "res:/binaryFileIndex_v0_0_0.txt" + "./resourcesOnBranch/binaryFileIndex_v0_0_0.txt" // "res:/binaryFileIndex_v0_0_0.txt" + }; + std::vector expectedIncludes = { ".yaml" }; + std::vector expectedExcludes = {}; + + MapContainsPaths( expectedPaths, fullPathMap, "FullResolvedPathMap from example1.ini" ); + ValidatePathMap( expectedPaths, fullPathMap, expectedIncludes, expectedExcludes, "FullResolvedPathMap from example1.ini" ); + } + catch( const std::exception& e ) + { + FAIL() << "Exception thrown while loading example1.ini: " << e.what(); + } + catch( ... ) + { + FAIL() << "Unknown exception thrown while loading example1.ini"; + } +} \ No newline at end of file From b842fb7e20bfd5761ae095ca056e4d88ee56c979 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 15 Jan 2026 15:26:05 +0000 Subject: [PATCH 031/124] Treat empty top-level and inline filters differently Needed to support: - Empty or undefined top-level filter (which should add a "*" include) - Inline exclude filter ONLY (which should NOT add a "*" include) in the combined output. Fixed and added a bunch of tests --- tests/src/ResourceFilterTest.cpp | 446 +++++++++++++++++- .../invalidMissingDefaultSection.ini | 6 + .../invalidMissingNamedSection.ini | 4 + tools/include/FilterResourceFilter.h | 4 +- tools/include/FilterResourcePathFile.h | 1 - tools/src/FilterNamedSection.cpp | 2 +- tools/src/FilterResourceFile.cpp | 4 +- tools/src/FilterResourceFilter.cpp | 18 +- tools/src/FilterResourcePathFile.cpp | 2 +- 9 files changed, 474 insertions(+), 13 deletions(-) create mode 100644 tests/testData/ExampleIniFiles/invalidMissingDefaultSection.ini create mode 100644 tests/testData/ExampleIniFiles/invalidMissingNamedSection.ini diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 9658075..a507fed 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -52,15 +52,32 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_OnlyIncludeFilter ) EXPECT_TRUE( excludes.empty() ); } -TEST_F( ResourceFilterTest, FilterResourceFilter_OnlyExcludeFilter ) +TEST_F( ResourceFilterTest, FilterResourceFilter_OnlyExcludeFilter_Toplevel ) +{ + ResourceTools::FilterResourceFilter filter( "![ .excluded .extension ]", true ); + const auto& includes = filter.GetIncludeFilter(); + const auto& excludes = filter.GetExcludeFilter(); + + EXPECT_EQ( excludes.size(), 2 ); + EXPECT_EQ( excludes[0], ".excluded" ); + EXPECT_EQ( excludes[1], ".extension" ); + + EXPECT_EQ( includes.size(), 1 ); + EXPECT_EQ( includes[0], "*" ); // Wild-card added when no include filter specified for TOP-LEVEL filter +} + +TEST_F( ResourceFilterTest, FilterResourceFilter_OnlyExcludeFilter_Inline ) { ResourceTools::FilterResourceFilter filter( "![ .excluded .extension ]" ); const auto& includes = filter.GetIncludeFilter(); const auto& excludes = filter.GetExcludeFilter(); - EXPECT_TRUE( includes.empty() ); + EXPECT_EQ( excludes.size(), 2 ); EXPECT_EQ( excludes[0], ".excluded" ); EXPECT_EQ( excludes[1], ".extension" ); + + EXPECT_EQ( includes.size(), 0 ); + EXPECT_TRUE( includes.empty() ); // No wild-card added when no include filter specified INLINE } TEST_F( ResourceFilterTest, FilterResourceFilter_ComplexIncludeExcludeFilter ) @@ -282,6 +299,30 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_CondensedValidFilterStringv2 ) EXPECT_EQ( excludes, expectedExcludes ); } +TEST_F( ResourceFilterTest, FilterResourceFilter_EmptyFilterString_TopLevel ) +{ + ResourceTools::FilterResourceFilter filter( "", true ); + const auto& includes = filter.GetIncludeFilter(); + const auto& excludes = filter.GetExcludeFilter(); + + EXPECT_EQ( includes.size(), 1 ); + EXPECT_EQ( includes[0], "*" ); // Wild-card added when no include filter specified on TOP-LEVEL filter + + EXPECT_TRUE( excludes.empty() ); +} + +TEST_F( ResourceFilterTest, FilterResourceFilter_EmptyFilterString_Inline ) +{ + ResourceTools::FilterResourceFilter filter( "" ); + const auto& includes = filter.GetIncludeFilter(); + const auto& excludes = filter.GetExcludeFilter(); + + EXPECT_EQ( includes.size(), 0 ); + EXPECT_TRUE( includes.empty() ); // No wild-card added when no include filter specified INLINE + + EXPECT_TRUE( excludes.empty() ); +} + // ----------------------------------------- TEST_F( ResourceFilterTest, FilterPrefixMap_SinglePrefixMultiplePaths ) @@ -760,6 +801,54 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_SingleLineRespath ) ValidatePathMap( expectedPaths, combinedMap, expectedIncludes, expectedExcludes, "CombinedResolvedPathMap" ); } +TEST_F( ResourceFilterTest, FilterNamedSection_Valid_EmptyFilter_TopLevel ) +{ + std::string sectionName = "FilterNamedSection_Valid_EmptyFilter_TopLevel"; + std::string defaultParentPrefixMapStr = "testPrefix:/myPath"; + std::string filter = ""; // Empty filter string at top-level should add wildcard include + std::string respaths = "testPrefix:/foo/bar"; + + ResourceTools::FilterPrefixMap defaultPrefixMap( defaultParentPrefixMapStr ); + ResourceTools::FilterNamedSection namedSection( sectionName, filter, respaths, "", defaultPrefixMap ); + + // Expected values: + std::set expectedPaths = { "/myPath/foo/bar" }; + std::vector expectedIncludes = { "*" }; + std::vector expectedExcludes = { }; + + const auto& resolvedRespathMap = namedSection.GetResolvedRespathsMap(); + const auto& resolvedResfileMap = namedSection.GetResolvedResfileMap(); + const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); + + ValidatePathMap( expectedPaths, resolvedRespathMap, expectedIncludes, expectedExcludes, "ResolvedRespathsMap" ); + EXPECT_TRUE( resolvedResfileMap.empty() ); + ValidatePathMap( expectedPaths, combinedMap, expectedIncludes, expectedExcludes, "CombinedResolvedPathMap" ); +} + +TEST_F( ResourceFilterTest, FilterNamedSection_Valid_OnlyExcludeFilter_TopLevel ) +{ + std::string sectionName = "FilterNamedSection_Valid_OnlyExcludeFilter_TopLevel"; + std::string defaultParentPrefixMapStr = "testPrefix:/myPath"; + std::string filter = "![ .ex1 ]"; // When there is only an exclude filter at top-level, it should add wildcard include + std::string respaths = "testPrefix:/foo/bar"; + + ResourceTools::FilterPrefixMap defaultPrefixMap( defaultParentPrefixMapStr ); + ResourceTools::FilterNamedSection namedSection( sectionName, filter, respaths, "", defaultPrefixMap ); + + // Expected values: + std::set expectedPaths = { "/myPath/foo/bar" }; + std::vector expectedIncludes = { "*" }; + std::vector expectedExcludes = { ".ex1" }; + + const auto& resolvedRespathMap = namedSection.GetResolvedRespathsMap(); + const auto& resolvedResfileMap = namedSection.GetResolvedResfileMap(); + const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); + + ValidatePathMap( expectedPaths, resolvedRespathMap, expectedIncludes, expectedExcludes, "ResolvedRespathsMap" ); + EXPECT_TRUE( resolvedResfileMap.empty() ); + ValidatePathMap( expectedPaths, combinedMap, expectedIncludes, expectedExcludes, "CombinedResolvedPathMap" ); +} + TEST_F( ResourceFilterTest, FilterNamedSection_Valid_MultiLineRespath ) { std::string sectionName = "FilterNamedSection_Valid_MultiLineRespath"; @@ -922,6 +1011,92 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_CombinedResolvedMap ) ValidatePathMap( combinedPaths, combinedMap, defaultIncludes, defaultExcludes, "ResolvedCombinedMap" ); } +TEST_F( ResourceFilterTest, FilterNamedSection_Valid_CombinedResolvedMap_EmptyTopLevelFilter ) +{ + std::string sectionName = "FilterNamedSection_Valid_CombinedResolvedMap_EmptyTopLevelFilter"; + std::string defaultParentPrefixMapStr = "prefixA:/path1"; + std::string filter = ""; // Empty top-level filter should add wildcard ("*") include + std::string respaths = "prefixA:/foo/bar"; + std::string resfile = "prefixA:/loo/car"; + + ResourceTools::FilterPrefixMap defaultPrefixMap( defaultParentPrefixMapStr ); + ResourceTools::FilterNamedSection namedSection( sectionName, filter, respaths, resfile, defaultPrefixMap ); + + // Expected values: + std::set combinedPaths = { "/path1/foo/bar", "/path1/loo/car" }; + std::set respathsPaths = { "/path1/foo/bar" }; + std::set resfilesPaths = { "/path1/loo/car" }; + std::vector defaultIncludes = { "*" }; + std::vector defaultExcludes = {}; + + const auto& respathsMap = namedSection.GetResolvedRespathsMap(); + const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); + + ASSERT_EQ( respathsMap.size(), 1 ); // "/path1/foo/bar" + MapContainsPaths( respathsPaths, respathsMap, "ResolvedRespathsMap" ); + ValidatePathMap( respathsPaths, respathsMap, defaultIncludes, defaultExcludes, "ResolvedRespathsMap" ); + + ASSERT_EQ( resfileMap.size(), 1 ); // "/path1/loo/bar" + MapContainsPaths( resfilesPaths, resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( resfilesPaths, resfileMap, defaultIncludes, defaultExcludes, "ResolvedResfileMap" ); + + ASSERT_EQ( combinedMap.size(), 2 ); // both + MapContainsPaths( combinedPaths, combinedMap, "ResolvedCombinedMap" ); + ValidatePathMap( combinedPaths, combinedMap, defaultIncludes, defaultExcludes, "ResolvedCombinedMap" ); + for( const auto& kv : combinedMap ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 1 ); + EXPECT_EQ( kv.second.GetIncludeFilter()[0], "*" ); // Wild-card added when no include filter specified for TOP-LEVEL filter + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 0 ); + EXPECT_TRUE( kv.second.GetExcludeFilter().empty() ); + } +} + +TEST_F( ResourceFilterTest, FilterNamedSection_Valid_CombinedResolvedMap_OnlyExcludeTopLevelFilter ) +{ + std::string sectionName = "FilterNamedSection_Valid_CombinedResolvedMap_OnlyExcludeTopLevelFilter"; + std::string defaultParentPrefixMapStr = "prefixA:/path1"; + std::string filter = " ![ .ex1 ] "; // Only exclude filter at top-level should add wildcard ("*") include as well + std::string respaths = "prefixA:/foo/bar"; + std::string resfile = "prefixA:/loo/car"; + + ResourceTools::FilterPrefixMap defaultPrefixMap( defaultParentPrefixMapStr ); + ResourceTools::FilterNamedSection namedSection( sectionName, filter, respaths, resfile, defaultPrefixMap ); + + // Expected values: + std::set combinedPaths = { "/path1/foo/bar", "/path1/loo/car" }; + std::set respathsPaths = { "/path1/foo/bar" }; + std::set resfilesPaths = { "/path1/loo/car" }; + std::vector defaultIncludes = { "*" }; // Default added + std::vector defaultExcludes = { ".ex1" }; + + const auto& respathsMap = namedSection.GetResolvedRespathsMap(); + const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); + + ASSERT_EQ( respathsMap.size(), 1 ); // "/path1/foo/bar" + MapContainsPaths( respathsPaths, respathsMap, "ResolvedRespathsMap" ); + ValidatePathMap( respathsPaths, respathsMap, defaultIncludes, defaultExcludes, "ResolvedRespathsMap" ); + + ASSERT_EQ( resfileMap.size(), 1 ); // "/path1/loo/bar" + MapContainsPaths( resfilesPaths, resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( resfilesPaths, resfileMap, defaultIncludes, defaultExcludes, "ResolvedResfileMap" ); + + ASSERT_EQ( combinedMap.size(), 2 ); // both + MapContainsPaths( combinedPaths, combinedMap, "ResolvedCombinedMap" ); + ValidatePathMap( combinedPaths, combinedMap, defaultIncludes, defaultExcludes, "ResolvedCombinedMap" ); + for( const auto& kv : combinedMap ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 1 ); + EXPECT_EQ( kv.second.GetIncludeFilter()[0], "*" ); // Wild-card added when no include filter specified for TOP-LEVEL filter + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 1 ); + EXPECT_EQ( kv.second.GetExcludeFilter()[0], ".ex1" ); + } +} + TEST_F( ResourceFilterTest, FilterNamedSection_Valid_CombinedResolvedMap_OverwrittenByResfileMap ) { std::string sectionName = "FilterNamedSection_Valid_CombinedResolvedMap_OverwrittenByResfileMap"; @@ -943,17 +1118,165 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_CombinedResolvedMap_Overwri const auto& respathsMap = namedSection.GetResolvedRespathsMap(); const auto& resfileMap = namedSection.GetResolvedResfileMap(); - ASSERT_EQ( respathsMap.size(), 2 ); // "/path1/foo/bar" + ASSERT_EQ( respathsMap.size(), 2 ); + MapContainsPaths( allPaths, respathsMap, "ResolvedRespathsMap" ); + ValidatePathMap( allPaths, respathsMap, defaultIncludes, allExcludes, "ResolvedRespathsMap" ); + + ASSERT_EQ( resfileMap.size(), 2 ); + MapContainsPaths( allPaths, resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( allPaths, resfileMap, overrideIncludes, allExcludes, "ResolvedResfileMap" ); + + ASSERT_EQ( combinedMap.size(), 2 ); // Both, same count but now with overrides + MapContainsPaths( allPaths, combinedMap, "ResolvedCombinedMap" ); + ValidatePathMap( allPaths, combinedMap, overrideIncludes, allExcludes, "ResolvedCombinedMap" ); + + // Re-validate that RespathsMap is unchanged + const auto& respathsAgainMap = namedSection.GetResolvedRespathsMap(); + MapContainsPaths( allPaths, respathsAgainMap, "ResolvedRespathsMap-Again" ); + ValidatePathMap( allPaths, respathsAgainMap, defaultIncludes, allExcludes, "ResolvedRespathsMap-Again" ); +} + +TEST_F( ResourceFilterTest, FilterNamedSection_Valid_SameCombinedResolvedMap_EmptyTopLevelFilter_OverwrittenByResfileMap ) +{ + std::string sectionName = "FilterNamedSection_Valid_SameCombinedResolvedMap_EmptyTopLevelFilter_OverwrittenByResfileMap"; + std::string defaultParentPrefixMapStr = "prefix1:/pathA;/pathB"; + std::string filter = ""; // Empty top-level filter should add wildcard ("*") include + std::string respaths = "prefix1:/foo/bar"; + std::string resfile = "prefix1:/foo/bar [ .extra ]"; // Same path, extra include filter + + ResourceTools::FilterPrefixMap defaultPrefixMap( defaultParentPrefixMapStr ); + ResourceTools::FilterNamedSection namedSection( sectionName, filter, respaths, resfile, defaultPrefixMap ); + + // Expected values: + std::set allPaths = { "/pathA/foo/bar", "/pathB/foo/bar" }; + std::vector defaultIncludes = { "*" }; + std::vector allExcludes = {}; + std::vector overrideIncludes = { "*", ".extra" }; + + const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); + const auto& respathsMap = namedSection.GetResolvedRespathsMap(); + const auto& resfileMap = namedSection.GetResolvedResfileMap(); + + ASSERT_EQ( respathsMap.size(), 2 ); + MapContainsPaths( allPaths, respathsMap, "ResolvedRespathsMap" ); + ValidatePathMap( allPaths, respathsMap, defaultIncludes, allExcludes, "ResolvedRespathsMap" ); + + ASSERT_EQ( resfileMap.size(), 2 ); + MapContainsPaths( allPaths, resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( allPaths, resfileMap, overrideIncludes, allExcludes, "ResolvedResfileMap" ); + + ASSERT_EQ( combinedMap.size(), 2 ); // Both, same count but now with overrides + MapContainsPaths( allPaths, combinedMap, "ResolvedCombinedMap" ); + ValidatePathMap( allPaths, combinedMap, overrideIncludes, allExcludes, "ResolvedCombinedMap" ); + for( const auto& kv : combinedMap ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_EQ( kv.second.GetIncludeFilter()[0], "*" ); // Wild-card added when no include filter specified for TOP-LEVEL filter + EXPECT_EQ( kv.second.GetIncludeFilter()[1], ".extra" ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 0 ); + EXPECT_TRUE( kv.second.GetExcludeFilter().empty() ); + } + + // Re-validate that RespathsMap is unchanged + const auto& respathsAgainMap = namedSection.GetResolvedRespathsMap(); + MapContainsPaths( allPaths, respathsAgainMap, "ResolvedRespathsMap-Again" ); + ValidatePathMap( allPaths, respathsAgainMap, defaultIncludes, allExcludes, "ResolvedRespathsMap-Again" ); +} + +TEST_F( ResourceFilterTest, FilterNamedSection_Valid_SameCombinedResolvedMap_EmptyTopLevelFilter_OverwrittenByRespathMap ) +{ + std::string sectionName = "FilterNamedSection_Valid_SameCombinedResolvedMap_EmptyTopLevelFilter_OverwrittenByRespathMap"; + std::string defaultParentPrefixMapStr = "prefix1:/pathA;/pathB"; + std::string filter = ""; // Empty top-level filter should add wildcard ("*") include + std::string respaths = "prefix1:/foo/bar [ .extra ]"; // Same path, extra include filter + std::string resfile = "prefix1:/foo/bar"; + + ResourceTools::FilterPrefixMap defaultPrefixMap( defaultParentPrefixMapStr ); + ResourceTools::FilterNamedSection namedSection( sectionName, filter, respaths, resfile, defaultPrefixMap ); + + // Expected values: + std::set allPaths = { "/pathA/foo/bar", "/pathB/foo/bar" }; + std::vector defaultIncludes = { "*" }; + std::vector allExcludes = {}; + std::vector overrideIncludes = { "*", ".extra" }; + + const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); + const auto& respathsMap = namedSection.GetResolvedRespathsMap(); + const auto& resfileMap = namedSection.GetResolvedResfileMap(); + + ASSERT_EQ( respathsMap.size(), 2 ); + MapContainsPaths( allPaths, respathsMap, "ResolvedRespathsMap" ); + ValidatePathMap( allPaths, respathsMap, overrideIncludes, allExcludes, "ResolvedRespathsMap" ); + + ASSERT_EQ( resfileMap.size(), 2 ); + MapContainsPaths( allPaths, resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( allPaths, resfileMap, defaultIncludes, allExcludes, "ResolvedResfileMap" ); + + ASSERT_EQ( combinedMap.size(), 2 ); // Both, same count but now with overrides + MapContainsPaths( allPaths, combinedMap, "ResolvedCombinedMap" ); + ValidatePathMap( allPaths, combinedMap, overrideIncludes, allExcludes, "ResolvedCombinedMap" ); + for( const auto& kv : combinedMap ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_EQ( kv.second.GetIncludeFilter()[0], "*" ); // Wild-card added when no include filter specified for TOP-LEVEL filter + EXPECT_EQ( kv.second.GetIncludeFilter()[1], ".extra" ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 0 ); + EXPECT_TRUE( kv.second.GetExcludeFilter().empty() ); + } + + // Re-validate original Respaths/files Maps + const auto& respathsAgainMap = namedSection.GetResolvedRespathsMap(); + MapContainsPaths( allPaths, respathsAgainMap, "ResolvedRespathsMap-Again" ); + ValidatePathMap( allPaths, respathsAgainMap, overrideIncludes, allExcludes, "ResolvedRespathsMap-Again" ); + + const auto& resfileAgainMap = namedSection.GetResolvedResfileMap(); + MapContainsPaths( allPaths, resfileAgainMap, "ResolvedResfileMap-Again" ); + ValidatePathMap( allPaths, resfileAgainMap, defaultIncludes, allExcludes, "ResolvedResfileMap-Again" ); +} + +TEST_F( ResourceFilterTest, FilterNamedSection_Valid_SameCombinedResolvedMap_ExcludeOnlyTopLevelFilter_OverwrittenByResfileMap ) +{ + std::string sectionName = "FilterNamedSection_Valid_SameCombinedResolvedMap_ExcludeOnlyTopLevelFilter_OverwrittenByResfileMap"; + std::string defaultParentPrefixMapStr = "prefix1:/pathA;/pathB"; + std::string filter = " ![ .topLevelExclude ]"; // Only exclude filter at top-level should add wildcard ("*") include + std::string respaths = "prefix1:/foo/bar"; + std::string resfile = "prefix1:/foo/bar [ .extra ]"; // Same path, extra include filter + + ResourceTools::FilterPrefixMap defaultPrefixMap( defaultParentPrefixMapStr ); + ResourceTools::FilterNamedSection namedSection( sectionName, filter, respaths, resfile, defaultPrefixMap ); + + // Expected values: + std::set allPaths = { "/pathA/foo/bar", "/pathB/foo/bar" }; + std::vector defaultIncludes = { "*" }; + std::vector allExcludes = { ".topLevelExclude" }; + std::vector overrideIncludes = { "*", ".extra" }; + + const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); + const auto& respathsMap = namedSection.GetResolvedRespathsMap(); + const auto& resfileMap = namedSection.GetResolvedResfileMap(); + + ASSERT_EQ( respathsMap.size(), 2 ); MapContainsPaths( allPaths, respathsMap, "ResolvedRespathsMap" ); ValidatePathMap( allPaths, respathsMap, defaultIncludes, allExcludes, "ResolvedRespathsMap" ); - ASSERT_EQ( resfileMap.size(), 2 ); // "/path1/loo/bar" + ASSERT_EQ( resfileMap.size(), 2 ); MapContainsPaths( allPaths, resfileMap, "ResolvedResfileMap" ); ValidatePathMap( allPaths, resfileMap, overrideIncludes, allExcludes, "ResolvedResfileMap" ); ASSERT_EQ( combinedMap.size(), 2 ); // Both, same count but now with overrides MapContainsPaths( allPaths, combinedMap, "ResolvedCombinedMap" ); ValidatePathMap( allPaths, combinedMap, overrideIncludes, allExcludes, "ResolvedCombinedMap" ); + for( const auto& kv : combinedMap ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_EQ( kv.second.GetIncludeFilter()[0], "*" ); // Wild-card added when no include filter specified for TOP-LEVEL filter + EXPECT_EQ( kv.second.GetIncludeFilter()[1], ".extra" ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 1 ); + EXPECT_EQ( kv.second.GetExcludeFilter()[0], ".topLevelExclude" ); + } // Re-validate that RespathsMap is unchanged const auto& respathsAgainMap = namedSection.GetResolvedRespathsMap(); @@ -961,6 +1284,58 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_CombinedResolvedMap_Overwri ValidatePathMap( allPaths, respathsAgainMap, defaultIncludes, allExcludes, "ResolvedRespathsMap-Again" ); } +TEST_F( ResourceFilterTest, FilterNamedSection_Valid_SameCombinedResolvedMap_ExcludeOnlyTopLevelFilter_OverwrittenByRespathMap ) +{ + std::string sectionName = "FilterNamedSection_Valid_SameCombinedResolvedMap_ExcludeOnlyTopLevelFilter_OverwrittenByRespathMap"; + std::string defaultParentPrefixMapStr = "prefix1:/pathA;/pathB"; + std::string filter = " ![ .topLevelExclude ]"; // Only exclude filter at top-level should add wildcard ("*") include + std::string respaths = "prefix1:/foo/bar [ .extra ]"; // Same path, extra include filter + std::string resfile = "prefix1:/foo/bar"; + + ResourceTools::FilterPrefixMap defaultPrefixMap( defaultParentPrefixMapStr ); + ResourceTools::FilterNamedSection namedSection( sectionName, filter, respaths, resfile, defaultPrefixMap ); + + // Expected values: + std::set allPaths = { "/pathA/foo/bar", "/pathB/foo/bar" }; + std::vector defaultIncludes = { "*" }; + std::vector allExcludes = { ".topLevelExclude" }; + std::vector overrideIncludes = { "*", ".extra" }; + + const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); + const auto& respathsMap = namedSection.GetResolvedRespathsMap(); + const auto& resfileMap = namedSection.GetResolvedResfileMap(); + + ASSERT_EQ( respathsMap.size(), 2 ); + MapContainsPaths( allPaths, respathsMap, "ResolvedRespathsMap" ); + ValidatePathMap( allPaths, respathsMap, overrideIncludes, allExcludes, "ResolvedRespathsMap" ); + + ASSERT_EQ( resfileMap.size(), 2 ); + MapContainsPaths( allPaths, resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( allPaths, resfileMap, defaultIncludes, allExcludes, "ResolvedResfileMap" ); + + ASSERT_EQ( combinedMap.size(), 2 ); // Both, same count but now with overrides + MapContainsPaths( allPaths, combinedMap, "ResolvedCombinedMap" ); + ValidatePathMap( allPaths, combinedMap, overrideIncludes, allExcludes, "ResolvedCombinedMap" ); + for( const auto& kv : combinedMap ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_EQ( kv.second.GetIncludeFilter()[0], "*" ); // Wild-card added when no include filter specified for TOP-LEVEL filter + EXPECT_EQ( kv.second.GetIncludeFilter()[1], ".extra" ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 1 ); + EXPECT_EQ( kv.second.GetExcludeFilter()[0], ".topLevelExclude" ); + } + + // Re-validate original Respaths/files Maps + const auto& respathsAgainMap = namedSection.GetResolvedRespathsMap(); + MapContainsPaths( allPaths, respathsAgainMap, "ResolvedRespathsMap-Again" ); + ValidatePathMap( allPaths, respathsAgainMap, overrideIncludes, allExcludes, "ResolvedRespathsMap-Again" ); + + const auto& resfileAgainMap = namedSection.GetResolvedResfileMap(); + MapContainsPaths( allPaths, resfileAgainMap, "ResolvedResfileMap-Again" ); + ValidatePathMap( allPaths, resfileAgainMap, defaultIncludes, allExcludes, "ResolvedResfileMap-Again" ); +} + // ------------------------------------------ TEST_F( ResourceFilterTest, FilterResourceFile_Load_example1_ini ) @@ -999,4 +1374,65 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_example1_ini ) { FAIL() << "Unknown exception thrown while loading example1.ini"; } -} \ No newline at end of file +} + +TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidMissingDefaultSection_ini ) +{ + const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidMissingDefaultSection.ini" ); + + try + { + ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + FAIL() << "Expected std::invalid_argument when loading ini file missing [DEFAULT] section"; + } + catch( const std::invalid_argument& e ) + { + std::string expectedError = "Missing [DEFAULT] section in INI file: " + iniPath.string(); + EXPECT_STREQ( e.what(), expectedError.c_str() ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument when loading ini file missing [DEFAULT] section"; + } +} + +TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidMissingNamedSection_ini ) +{ + const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidMissingNamedSection.ini" ); + + try + { + ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + FAIL() << "Expected std::invalid_argument when loading ini file missing [NamedSection] section"; + } + catch( const std::invalid_argument& e ) + { + std::string expectedError = "No namedSections defined in INI file: " + iniPath.string(); + EXPECT_STREQ( e.what(), expectedError.c_str() ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument when loading ini file missing [NamedSection] section"; + } +} + +TEST_F( ResourceFilterTest, FilterResourceFile_Load_iniFileNotFound ) +{ + const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/iniFileNotFound.ini" ); + + try + { + ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + FAIL() << "Expected std::runtime_error when loading non-existent ini file"; + } + catch( const std::runtime_error& e ) + { + std::string expectedError = "Failed to parse INI file: " + iniPath.string() + " - unable to open file"; + EXPECT_STREQ( e.what(), expectedError.c_str() ); + } + catch( ... ) + { + FAIL() << "Expected std::runtime_error when loading non-existent ini file"; + } +} + diff --git a/tests/testData/ExampleIniFiles/invalidMissingDefaultSection.ini b/tests/testData/ExampleIniFiles/invalidMissingDefaultSection.ini new file mode 100644 index 0000000..7d52b24 --- /dev/null +++ b/tests/testData/ExampleIniFiles/invalidMissingDefaultSection.ini @@ -0,0 +1,6 @@ +# This INI file is invalid because it lacks a DEFAULT section. + +[iniFileWithNoDEFAULTSection_andMissingPrefixMap] +filter = [ .in1 ] ![ .ex1 ] +respaths = res:/firstLine/... + res:/secondLine/... diff --git a/tests/testData/ExampleIniFiles/invalidMissingNamedSection.ini b/tests/testData/ExampleIniFiles/invalidMissingNamedSection.ini new file mode 100644 index 0000000..5cb561c --- /dev/null +++ b/tests/testData/ExampleIniFiles/invalidMissingNamedSection.ini @@ -0,0 +1,4 @@ +# This INI file is invalid because it lacks a named section. + +[DEFAULT] +prefixmap = res:ThereIsNoNamedSection diff --git a/tools/include/FilterResourceFilter.h b/tools/include/FilterResourceFilter.h index 73fbc1c..cf31f81 100644 --- a/tools/include/FilterResourceFilter.h +++ b/tools/include/FilterResourceFilter.h @@ -16,7 +16,7 @@ class FilterResourceFilter public: FilterResourceFilter() = default; - explicit FilterResourceFilter( const std::string& rawFilter ); + explicit FilterResourceFilter( const std::string& rawFilter, bool isToplevelFilter = false ); // Used as input when constructing a combined resolved filter for a respaths/resfile line. const std::string& GetRawFilter() const; @@ -26,6 +26,8 @@ class FilterResourceFilter const std::vector& GetExcludeFilter() const; private: + bool m_isToplevelFilter = false; + std::string m_rawFilter; std::vector m_includeFilter; diff --git a/tools/include/FilterResourcePathFile.h b/tools/include/FilterResourcePathFile.h index 5b2c6be..1c00b02 100644 --- a/tools/include/FilterResourcePathFile.h +++ b/tools/include/FilterResourcePathFile.h @@ -6,7 +6,6 @@ #include #include #include -#include namespace ResourceTools { diff --git a/tools/src/FilterNamedSection.cpp b/tools/src/FilterNamedSection.cpp index 88cf211..15022cd 100644 --- a/tools/src/FilterNamedSection.cpp +++ b/tools/src/FilterNamedSection.cpp @@ -13,7 +13,7 @@ FilterNamedSection::FilterNamedSection( std::string sectionName, const FilterPrefixMap& parentPrefixMap ) : m_sectionName( std::move( sectionName ) ), m_parentPrefixMap( parentPrefixMap ), - m_filter( filter ), + m_filter( filter, true ), // isToplevelFilter = true m_respaths( respaths, parentPrefixMap, m_filter ), m_resfile( resfile.empty() ? std::nullopt : std::make_optional( resfile, parentPrefixMap, m_filter ) ) { diff --git a/tools/src/FilterResourceFile.cpp b/tools/src/FilterResourceFile.cpp index 028326d..493f689 100644 --- a/tools/src/FilterResourceFile.cpp +++ b/tools/src/FilterResourceFile.cpp @@ -59,7 +59,7 @@ void FilterResourceFile::ParseIniFile() if( !reader.HasSection( "DEFAULT" ) ) { // TODO: Change this to a defined error code/type - throw std::runtime_error( "Missing [DEFAULT] section in INI file: " + m_iniFilePath.string() ); + throw std::invalid_argument( "Missing [DEFAULT] section in INI file: " + m_iniFilePath.string() ); } m_defaultSection = FilterDefaultSection( reader.Get( "DEFAULT", "prefixmap", "" ) ); @@ -69,7 +69,7 @@ void FilterResourceFile::ParseIniFile() if( allSections.size() <= 1 ) { // No namedSections defined - throw std::runtime_error( "No namedSections defined in INI file: " + m_iniFilePath.string() ); + throw std::invalid_argument( "No namedSections defined in INI file: " + m_iniFilePath.string() ); } // Parse all other named sections diff --git a/tools/src/FilterResourceFilter.cpp b/tools/src/FilterResourceFilter.cpp index d9f373b..6b6eb0b 100644 --- a/tools/src/FilterResourceFilter.cpp +++ b/tools/src/FilterResourceFilter.cpp @@ -7,8 +7,9 @@ namespace ResourceTools { -FilterResourceFilter::FilterResourceFilter( const std::string& rawFilter ) : - m_rawFilter( rawFilter ) +FilterResourceFilter::FilterResourceFilter( const std::string& rawFilter, bool isToplevelFilter /* = false */ ) : + m_rawFilter( rawFilter ), + m_isToplevelFilter( isToplevelFilter ) { ParseFilters(); } @@ -113,6 +114,19 @@ void FilterResourceFilter::ParseFilters() } pos = endBracket + 1; } + + // Make sure that we have a wild-card ("*") in the TOP-LEVEL include filter if the include filter is empty + if( m_isToplevelFilter && m_includeFilter.empty() ) + { + m_includeFilter.push_back( "*" ); + + // Also make sure we add the wild-card include to the raw filter (in case filters are concatenated later) + if( !m_rawFilter.empty() ) + { + m_rawFilter += " "; + } + m_rawFilter += "[ * ]"; + } } } diff --git a/tools/src/FilterResourcePathFile.cpp b/tools/src/FilterResourcePathFile.cpp index 9ab1d0d..08a71a1 100644 --- a/tools/src/FilterResourcePathFile.cpp +++ b/tools/src/FilterResourcePathFile.cpp @@ -3,7 +3,7 @@ #include #include #include - +#include namespace ResourceTools { From c82711e1bd7a937d0b362aab2ce87b1be1ccbd63 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:49:10 +0000 Subject: [PATCH 032/124] Add tests for NamedSection different inline filters --- tests/src/ResourceFilterTest.cpp | 236 +++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index a507fed..5b4672f 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -1097,6 +1097,242 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_CombinedResolvedMap_OnlyExc } } +TEST_F( ResourceFilterTest, FilterNamedSection_Valid_DiffCombinedResolvedMap_EmptyTopLevelFilter_OverrideRespath ) +{ + std::string sectionName = "FilterNamedSection_Valid_DiffCombinedResolvedMap_EmptyTopLevelFilter_OverrideRespath"; + std::string defaultParentPrefixMapStr = "prefixA:/path1"; + std::string filter = ""; // Empty top-level filter should add wildcard ("*") include filter + std::string respaths = "prefixA:/foo/bar [ .inlineInclude ] ![ .inlineExclude ]"; // Inline filters to override + std::string resfile = "prefixA:/loo/car"; + + ResourceTools::FilterPrefixMap defaultPrefixMap( defaultParentPrefixMapStr ); + ResourceTools::FilterNamedSection namedSection( sectionName, filter, respaths, resfile, defaultPrefixMap ); + + // Expected values: + std::set combinedPaths = { "/path1/foo/bar", "/path1/loo/car" }; + std::set respathsPaths = { "/path1/foo/bar" }; + std::set resfilesPaths = { "/path1/loo/car" }; + std::vector defaultIncludes = { "*" }; // Default "*" added to top-level include + std::vector defaultExcludes = {}; + std::vector overrideIncludes = { "*", ".inlineInclude" }; + std::vector overrideExcludes = { ".inlineExclude" }; + + const auto& respathsMap = namedSection.GetResolvedRespathsMap(); + const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); + + ASSERT_EQ( respathsMap.size(), 1 ); // "/path1/foo/bar" + "[ *, .inlineInclude ] ![ .inlineExclude ]" + MapContainsPaths( respathsPaths, respathsMap, "ResolvedRespathsMap" ); + ValidatePathMap( respathsPaths, respathsMap, overrideIncludes, overrideExcludes, "ResolvedRespathsMap" ); + + ASSERT_EQ( resfileMap.size(), 1 ); // "/path1/loo/bar" + "[ * ]" + MapContainsPaths( resfilesPaths, resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( resfilesPaths, resfileMap, defaultIncludes, defaultExcludes, "ResolvedResfileMap" ); + + ASSERT_EQ( combinedMap.size(), 2 ); // both + MapContainsPaths( combinedPaths, combinedMap, "ResolvedCombinedMap" ); + // Manually validate combined map since it combines both overrides and defaults + for( const auto& kv : combinedMap ) + { + // The "respaths" part: + if( kv.first == "/path1/foo/bar" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), "*" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".inlineInclude" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 1 ); + EXPECT_EQ( kv.second.GetExcludeFilter()[0], ".inlineExclude" ); + } + // The "resfile" part: + else if( kv.first == "/path1/loo/car" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 1 ); + EXPECT_EQ( kv.second.GetIncludeFilter()[0], "*" ); // Wild-card added when no include filter specified for TOP-LEVEL filter + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 0 ); + } + } +} + +TEST_F( ResourceFilterTest, FilterNamedSection_Valid_DiffCombinedResolvedMap_EmptyTopLevelFilter_OverrideResfile ) +{ + std::string sectionName = "FilterNamedSection_Valid_DiffCombinedResolvedMap_EmptyTopLevelFilter_OverrideResfile"; + std::string defaultParentPrefixMapStr = "prefixA:/path1"; + std::string filter = ""; // Empty top-level filter should add wildcard ("*") include filter + std::string respaths = "prefixA:/foo/bar"; + std::string resfile = "prefixA:/loo/car [ .inlineInclude ] ![ .inlineExclude ]"; // Inline filters to override + + ResourceTools::FilterPrefixMap defaultPrefixMap( defaultParentPrefixMapStr ); + ResourceTools::FilterNamedSection namedSection( sectionName, filter, respaths, resfile, defaultPrefixMap ); + + // Expected values: + std::set combinedPaths = { "/path1/foo/bar", "/path1/loo/car" }; + std::set respathsPaths = { "/path1/foo/bar" }; + std::set resfilesPaths = { "/path1/loo/car" }; + std::vector defaultIncludes = { "*" }; // Default "*" added to top-level include + std::vector defaultExcludes = {}; + std::vector overrideIncludes = { "*", ".inlineInclude" }; + std::vector overrideExcludes = { ".inlineExclude" }; + + const auto& respathsMap = namedSection.GetResolvedRespathsMap(); + const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); + + ASSERT_EQ( respathsMap.size(), 1 ); // "/path1/foo/bar" + "[ * ]" + MapContainsPaths( respathsPaths, respathsMap, "ResolvedRespathsMap" ); + ValidatePathMap( respathsPaths, respathsMap, defaultIncludes, defaultExcludes, "ResolvedRespathsMap" ); + + ASSERT_EQ( resfileMap.size(), 1 ); // "/path1/loo/bar" + "[ *, .inlineInclude ] ![ .inlineExclude ]" + MapContainsPaths( resfilesPaths, resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( resfilesPaths, resfileMap, overrideIncludes, overrideExcludes, "ResolvedResfileMap" ); + + ASSERT_EQ( combinedMap.size(), 2 ); // both + MapContainsPaths( combinedPaths, combinedMap, "ResolvedCombinedMap" ); + // Manually validate combined map since it combines both overrides and defaults + for( const auto& kv : combinedMap ) + { + // The "resfile" part: + if( kv.first == "/path1/loo/car" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), "*" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".inlineInclude" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 1 ); + EXPECT_EQ( kv.second.GetExcludeFilter()[0], ".inlineExclude" ); + } + // The "respaths" part: + else if( kv.first == "/path1/foo/bar" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 1 ); + EXPECT_EQ( kv.second.GetIncludeFilter()[0], "*" ); // Wild-card added when no include filter specified for TOP-LEVEL filter + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 0 ); + } + } +} + +TEST_F( ResourceFilterTest, FilterNamedSection_Valid_DiffCombinedResolvedMap_OnlyExcludeTopLevelFilter_OverrideRespath ) +{ + std::string sectionName = "FilterNamedSection_Valid_DiffCombinedResolvedMap_OnlyExcludeTopLevelFilter_OverrideRespath"; + std::string defaultParentPrefixMapStr = "prefixA:/path1"; + std::string filter = "![ .toplevelExclude ]"; // Only exclude filter at top-level should add wildcard ("*") include as well + std::string respaths = "prefixA:/foo/bar [ .inlineInclude ] ![ .inlineExclude ]"; // Inline filters to override + std::string resfile = "prefixA:/loo/car"; + + ResourceTools::FilterPrefixMap defaultPrefixMap( defaultParentPrefixMapStr ); + ResourceTools::FilterNamedSection namedSection( sectionName, filter, respaths, resfile, defaultPrefixMap ); + + // Expected values: + std::set combinedPaths = { "/path1/foo/bar", "/path1/loo/car" }; + std::set respathsPaths = { "/path1/foo/bar" }; + std::set resfilesPaths = { "/path1/loo/car" }; + std::vector defaultIncludes = { "*" }; // Default "*" added to top-level include + std::vector defaultExcludes = { ".toplevelExclude" }; + std::vector overrideIncludes = { "*", ".inlineInclude" }; + std::vector overrideExcludes = { ".toplevelExclude", ".inlineExclude" }; + + const auto& respathsMap = namedSection.GetResolvedRespathsMap(); + const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); + + ASSERT_EQ( respathsMap.size(), 1 ); // "/path1/foo/bar" + "[ *, .inlineInclude ] ![ .toplevelExclude .inlineExclude ]" + MapContainsPaths( respathsPaths, respathsMap, "ResolvedRespathsMap" ); + ValidatePathMap( respathsPaths, respathsMap, overrideIncludes, overrideExcludes, "ResolvedRespathsMap" ); + + ASSERT_EQ( resfileMap.size(), 1 ); // "/path1/loo/bar" + "[ * ]" + MapContainsPaths( resfilesPaths, resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( resfilesPaths, resfileMap, defaultIncludes, defaultExcludes, "ResolvedResfileMap" ); + + ASSERT_EQ( combinedMap.size(), 2 ); // both + MapContainsPaths( combinedPaths, combinedMap, "ResolvedCombinedMap" ); + // Manually validate combined map since it combines both overrides and defaults + for( const auto& kv : combinedMap ) + { + // The "respaths" part: + if( kv.first == "/path1/foo/bar" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), "*" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".inlineInclude" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetExcludeFilter().begin(), kv.second.GetExcludeFilter().end(), ".toplevelExclude" ) != kv.second.GetExcludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetExcludeFilter().begin(), kv.second.GetExcludeFilter().end(), ".inlineExclude" ) != kv.second.GetExcludeFilter().end() ); + } + // The "resfile" part: + else if( kv.first == "/path1/loo/car" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 1 ); + EXPECT_EQ( kv.second.GetIncludeFilter()[0], "*" ); // Wild-card added when no include filter specified for TOP-LEVEL filter + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 1 ); + EXPECT_EQ( kv.second.GetExcludeFilter()[0], ".toplevelExclude" ); // Top-level exclude filter + } + } +} + +TEST_F( ResourceFilterTest, FilterNamedSection_Valid_DiffCombinedResolvedMap_OnlyExcludeTopLevelFilter_OverrideResfile ) +{ + std::string sectionName = "FilterNamedSection_Valid_DiffCombinedResolvedMap_OnlyExcludeTopLevelFilter_OverrideRespath"; + std::string defaultParentPrefixMapStr = "prefixA:/path1"; + std::string filter = "![ .toplevelExclude ]"; // Only exclude filter at top-level should add wildcard ("*") include as well + std::string respaths = "prefixA:/foo/bar"; + std::string resfile = "prefixA:/loo/car [ .inlineInclude ] ![ .inlineExclude ]"; // Inline filters to override"; + + ResourceTools::FilterPrefixMap defaultPrefixMap( defaultParentPrefixMapStr ); + ResourceTools::FilterNamedSection namedSection( sectionName, filter, respaths, resfile, defaultPrefixMap ); + + // Expected values: + std::set combinedPaths = { "/path1/foo/bar", "/path1/loo/car" }; + std::set respathsPaths = { "/path1/foo/bar" }; + std::set resfilesPaths = { "/path1/loo/car" }; + std::vector defaultIncludes = { "*" }; // Default "*" added to top-level include + std::vector defaultExcludes = { ".toplevelExclude" }; + std::vector overrideIncludes = { "*", ".inlineInclude" }; + std::vector overrideExcludes = { ".toplevelExclude", ".inlineExclude" }; + + const auto& respathsMap = namedSection.GetResolvedRespathsMap(); + const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); + + ASSERT_EQ( respathsMap.size(), 1 ); // "/path1/foo/bar" + "[ * ]" + MapContainsPaths( respathsPaths, respathsMap, "ResolvedRespathsMap" ); + ValidatePathMap( respathsPaths, respathsMap, defaultIncludes, defaultExcludes, "ResolvedRespathsMap" ); + + ASSERT_EQ( resfileMap.size(), 1 ); // "/path1/loo/bar" + "[ *, .inlineInclude ] ![ .toplevelExclude .inlineExclude ]" + MapContainsPaths( resfilesPaths, resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( resfilesPaths, resfileMap, overrideIncludes, overrideExcludes, "ResolvedResfileMap" ); + + ASSERT_EQ( combinedMap.size(), 2 ); // both + MapContainsPaths( combinedPaths, combinedMap, "ResolvedCombinedMap" ); + // Manually validate combined map since it combines both overrides and defaults + for( const auto& kv : combinedMap ) + { + // The "resfile" part: + if( kv.first == "/path1/loo/car" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), "*" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".inlineInclude" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetExcludeFilter().begin(), kv.second.GetExcludeFilter().end(), ".toplevelExclude" ) != kv.second.GetExcludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetExcludeFilter().begin(), kv.second.GetExcludeFilter().end(), ".inlineExclude" ) != kv.second.GetExcludeFilter().end() ); + } + // The "respaths" part: + else if( kv.first == "/path1/foo/bar" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 1 ); + EXPECT_EQ( kv.second.GetIncludeFilter()[0], "*" ); // Wild-card added when no include filter specified for TOP-LEVEL filter + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 1 ); + EXPECT_EQ( kv.second.GetExcludeFilter()[0], ".toplevelExclude" ); // Top-level exclude filter + } + } +} + TEST_F( ResourceFilterTest, FilterNamedSection_Valid_CombinedResolvedMap_OverwrittenByResfileMap ) { std::string sectionName = "FilterNamedSection_Valid_CombinedResolvedMap_OverwrittenByResfileMap"; From 825ffeeb5777487af9bf98ca5d9c740b580d1d7a Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:43:19 +0000 Subject: [PATCH 033/124] Add tests for loading invalid example .ini files --- tests/src/ResourceFilterTest.cpp | 75 +++++++++++++++++++ .../ExampleIniFiles/invalidInlineFilter.ini | 8 ++ .../ExampleIniFiles/invalidPrefixMismatch.ini | 8 ++ .../ExampleIniFiles/invalidPrefixmap.ini | 8 ++ .../ExampleIniFiles/invalidSectionFilter.ini | 8 ++ 5 files changed, 107 insertions(+) create mode 100644 tests/testData/ExampleIniFiles/invalidInlineFilter.ini create mode 100644 tests/testData/ExampleIniFiles/invalidPrefixMismatch.ini create mode 100644 tests/testData/ExampleIniFiles/invalidPrefixmap.ini create mode 100644 tests/testData/ExampleIniFiles/invalidSectionFilter.ini diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 5b4672f..0f50aef 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -1672,3 +1672,78 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_iniFileNotFound ) } } +TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidPrefixmap_ini ) +{ + const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidPrefixmap.ini" ); + try + { + ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + FAIL() << "Expected std::invalid_argument when loading ini file with invalid prefixmap"; + } + catch( const std::invalid_argument& e ) + { + std::string expectedError = "Invalid prefixmap format: No paths defined for prefix: prefix1"; + EXPECT_STREQ( e.what(), expectedError.c_str() ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument when loading invalidPrefixmap.ini file"; + } +} + +TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidSectionFilter_ini ) +{ + const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidSectionFilter.ini" ); + try + { + ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + FAIL() << "Expected std::invalid_argument when loading ini file with invalid section filter"; + } + catch( const std::invalid_argument& e ) + { + std::string expectedError = "Invalid filter format: missing ']'"; + EXPECT_STREQ( e.what(), expectedError.c_str() ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument when loading invalidSectionFilter.ini file"; + } +} + +TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidInlineFilter_ini ) +{ + const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidInlineFilter.ini" ); + try + { + ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + FAIL() << "Expected std::invalid_argument when loading ini file with invalid inline filter"; + } + catch( const std::invalid_argument& e ) + { + std::string expectedError = "Invalid filter format: missing '['"; + EXPECT_STREQ( e.what(), expectedError.c_str() ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument when loading invalidInlineFilter.ini file"; + } +} + +TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidPrefixMismatch_ini ) +{ + const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidPrefixMismatch.ini" ); + try + { + ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + FAIL() << "Expected std::invalid_argument when loading ini file with prefix mismatch"; + } + catch( const std::invalid_argument& e ) + { + std::string expectedError = "Prefix 'prefixDoesNotExist' not present in prefixMap for line: prefixDoesNotExist:/firstLine/*"; + EXPECT_STREQ( e.what(), expectedError.c_str() ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument when loading invalidPrefixMismatch.ini file"; + } +} \ No newline at end of file diff --git a/tests/testData/ExampleIniFiles/invalidInlineFilter.ini b/tests/testData/ExampleIniFiles/invalidInlineFilter.ini new file mode 100644 index 0000000..fa31010 --- /dev/null +++ b/tests/testData/ExampleIniFiles/invalidInlineFilter.ini @@ -0,0 +1,8 @@ +[DEFAULT] +prefixmap = prefix1:okPath + +[invalidInlineFilterSection] +filter = [ .txt ] +# This INI file is invalid because the inline filter is missing an opening bracket "[" +respaths = prefix1:/firstLine/* .csv ] + prefix2:/secondLine/... \ No newline at end of file diff --git a/tests/testData/ExampleIniFiles/invalidPrefixMismatch.ini b/tests/testData/ExampleIniFiles/invalidPrefixMismatch.ini new file mode 100644 index 0000000..ed43f75 --- /dev/null +++ b/tests/testData/ExampleIniFiles/invalidPrefixMismatch.ini @@ -0,0 +1,8 @@ +[DEFAULT] +prefixmap = prefix1:okPath + +[invalidPrefixmapMismatchSection] +filter = [ .txt ] +# This INI file is invalid because the prefixDoesNotExist prefix does not exist in the parent prefixmap +respaths = prefixDoesNotExist:/firstLine/* + prefix2:/secondLine/... \ No newline at end of file diff --git a/tests/testData/ExampleIniFiles/invalidPrefixmap.ini b/tests/testData/ExampleIniFiles/invalidPrefixmap.ini new file mode 100644 index 0000000..b2c5610 --- /dev/null +++ b/tests/testData/ExampleIniFiles/invalidPrefixmap.ini @@ -0,0 +1,8 @@ +[DEFAULT] +# This INI file is invalid because the prefixmap entry "prefix1:" has no actual paths defined +prefixmap = prefix1: prefix2:someValidPrefix + +[invalidPrefixMapSection] +filter = [ .txt ] +respaths = prefix1:/firstLine/... + prefix2:/secondLine/... diff --git a/tests/testData/ExampleIniFiles/invalidSectionFilter.ini b/tests/testData/ExampleIniFiles/invalidSectionFilter.ini new file mode 100644 index 0000000..2007f6e --- /dev/null +++ b/tests/testData/ExampleIniFiles/invalidSectionFilter.ini @@ -0,0 +1,8 @@ +[DEFAULT] +prefixmap = prefix1:okPath prefix2:anotherPath + +[invalidSectionFilterSection] +# This INI file is invalid because the filter is missing a closing bracket "]" +filter = [ .txt +respaths = prefix1:/firstLine/... + prefix2:/secondLine/... From 183d0c010c91dc9479f4aff0f8cb32e965f2c9d3 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 21 Jan 2026 11:39:08 +0000 Subject: [PATCH 034/124] Add ResourceFilter class - Hook it up to the ResourceGroupImplementation - Add some basic tests for the functionality --- include/Enums.h | 6 + include/ResourceGroup.h | 9 +- src/Enums.cpp | 8 + src/ResourceGroupImpl.cpp | 35 +++ tests/src/ResourceFilterTest.cpp | 162 ++++++++++++- tests/src/ResourceFilterTest.h | 49 ++++ tests/testData/ExampleIniFiles/example1.ini | 4 +- .../ExampleIniFiles/validSimpleExample1.ini | 15 ++ tools/CMakeLists.txt | 2 + tools/include/FilterResourceFile.h | 6 +- tools/include/ResourceFilter.h | 49 ++++ tools/src/FilterNamedSection.cpp | 1 - tools/src/FilterResourceFile.cpp | 18 +- tools/src/ResourceFilter.cpp | 216 ++++++++++++++++++ 14 files changed, 556 insertions(+), 24 deletions(-) create mode 100644 tests/testData/ExampleIniFiles/validSimpleExample1.ini create mode 100644 tools/include/ResourceFilter.h create mode 100644 tools/src/ResourceFilter.cpp diff --git a/include/Enums.h b/include/Enums.h index 119f94c..6923c81 100644 --- a/include/Enums.h +++ b/include/Enums.h @@ -135,6 +135,10 @@ using StatusCallback = std::function resourceFilterIniFiles = {}; }; /** @struct ResourceGroupMergeParams @@ -328,4 +332,5 @@ class API ResourceGroup } -#endif // ResourceGroup_H \ No newline at end of file +#endif // ResourceGroup_H + diff --git a/src/Enums.cpp b/src/Enums.cpp index a7a303b..677ebf2 100644 --- a/src/Enums.cpp +++ b/src/Enums.cpp @@ -156,6 +156,14 @@ bool ResultTypeToString( ResultType resultType, std::string& output ) case ResultType::REQUIRED_INPUT_PARAMETER_NOT_SET: output = "A required parameter was not set"; return true; + + case ResultType::FAILED_TO_INITIALIZE_RESOURCE_FILTER: + output = "Failed to initialize ResourceFilter from .ini file"; + return true; + + case ResultType::FAILED_TO_APPLY_RESOURCE_FILTER_RULES: + output = "Unable to decide on include/exclude filtering rules for resource"; + return true; } output = "Error code unrecognised. This is an internal library error which shouldn't be encountered. If you encounter this error contact API addministrators."; diff --git a/src/ResourceGroupImpl.cpp b/src/ResourceGroupImpl.cpp index 4a0be56..4bc9110 100644 --- a/src/ResourceGroupImpl.cpp +++ b/src/ResourceGroupImpl.cpp @@ -18,6 +18,7 @@ #include "BundleResourceGroupImpl.h" #include "ChunkIndex.h" #include "ResourceGroupFactory.h" +#include namespace CarbonResources { @@ -65,6 +66,21 @@ Result ResourceGroup::ResourceGroupImpl::CreateFromDirectory( const CreateResour return Result{ ResultType::DOCUMENT_VERSION_UNSUPPORTED }; } + // Initialize ResourceFilter if .ini files are supplied + ResourceTools::ResourceFilter resourceFilter; + if( !params.resourceFilterIniFiles.empty() ) + { + try + { + resourceFilter.Initialize( params.resourceFilterIniFiles ); + } + catch( const std::exception& e ) + { + std::string errorMsg = "Unable to create ResourceFilter - because of: " + std::string( e.what() ); + return Result{ ResultType::FAILED_TO_INITIALIZE_RESOURCE_FILTER, errorMsg }; + } + } + // Walk directory and create a resource from each file using data auto recursiveDirectoryIter = std::filesystem::recursive_directory_iterator( params.directory ); @@ -72,6 +88,25 @@ Result ResourceGroup::ResourceGroupImpl::CreateFromDirectory( const CreateResour { if( entry.is_regular_file() ) { + // Apply Resource filtering (in case any filters are supplied) + if( resourceFilter.HasFilters() ) + { + try + { + // Resource filtering: + // Check if the file, i.e. entry.path() should be included or excluded based on filtering rules + if( !resourceFilter.ShouldInclude( entry.path() ) ) + { + continue; + } + } + catch( const std::exception& e ) + { + std::string errorMsg = "Unable to decide on include/exclude filtering for: " + entry.path().generic_string() + " - because of: " + std::string( e.what() ); + return Result{ ResultType::FAILED_TO_APPLY_RESOURCE_FILTER_RULES, errorMsg }; + } + } + // Update status if( params.statusCallback ) { diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 0f50aef..5de0f27 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -10,6 +10,7 @@ #include #include #include +#include TEST_F( ResourceFilterTest, Example1IniParsing ) { @@ -34,7 +35,7 @@ TEST_F( ResourceFilterTest, Example1IniParsing ) auto respathValueGet = reader.Get( "testYamlFilesOverMultiLineResPathsWithEmptyLines", "respaths", "" ); std::string respathValueGetString = reader.Get( "testYamlFilesOverMultiLineResPathsWithEmptyLines", "respaths", "" ); EXPECT_EQ( respathValueGet, respathValueGetString ); - EXPECT_EQ( respathValueGet, "res:/firstLine/...\nres:/secondLine/...\nres2:/thirdLine/..." ); // Note: Under the hood, INIReader converts multi-empty-lines to a single \n line breaks + EXPECT_EQ( respathValueGet, "res:/firstLine/...\nres:/secondLine/*\nres2:/thirdLine/..." ); // Note: Under the hood, INIReader converts multi-empty-lines to a single \n line breaks EXPECT_EQ( reader.Keys( "testYamlFilesOverMultiLineResPathsWithEmptyLines" ).size(), 3 ); } @@ -1582,15 +1583,15 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_example1_ini ) try { ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); - const auto& fullPathMap = resourceFile.GetFullResolvedPathMap(); + const auto& iniFilePathMap = resourceFile.GetIniFileResolvedPathMap(); // Validate the paths: std::set expectedPaths = { // From the respaths attribute: "./Indicies/firstLine/...", // "res:/firstLine/..." "./resourcesOnBranch/firstLine/...", // "res:/firstLine/..." - "./Indicies/secondLine/...", // "res:/secondLine/..." - "./resourcesOnBranch/secondLine/...", // "res:/secondLine/..." + "./Indicies/secondLine/*", // "res:/secondLine/*" + "./resourcesOnBranch/secondLine/*", // "res:/secondLine/*" "./ResourceGroups/thirdLine/...", // "res2:/thirdLine/..." // From the resfile attribute: "./Indicies/binaryFileIndex_v0_0_0.txt", // "res:/binaryFileIndex_v0_0_0.txt" @@ -1599,8 +1600,8 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_example1_ini ) std::vector expectedIncludes = { ".yaml" }; std::vector expectedExcludes = {}; - MapContainsPaths( expectedPaths, fullPathMap, "FullResolvedPathMap from example1.ini" ); - ValidatePathMap( expectedPaths, fullPathMap, expectedIncludes, expectedExcludes, "FullResolvedPathMap from example1.ini" ); + MapContainsPaths( expectedPaths, iniFilePathMap, "FullResolvedPathMap from example1.ini" ); + ValidatePathMap( expectedPaths, iniFilePathMap, expectedIncludes, expectedExcludes, "FullResolvedPathMap from example1.ini" ); } catch( const std::exception& e ) { @@ -1746,4 +1747,151 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidPrefixMismatch_ini ) { FAIL() << "Expected std::invalid_argument when loading invalidPrefixMismatch.ini file"; } -} \ No newline at end of file +} + +// ------------------------------------------ + +TEST_F( ResourceFilterTest, ResourceFilter_Load_SingleFile_ThatDoesNotExist ) +{ + const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/noSuchFile.ini" ); + std::vector paths = { iniPath }; + + ResourceTools::ResourceFilter resourceFilter; + + try + { + resourceFilter.Initialize( paths ); + FAIL() << "Expected this test to fail: ResourceFilter_Load_SingleFile_ThatDoesNotExist"; + } + catch( const std::exception& e ) + { + std::string errorMessage = e.what(); + ASSERT_NE( errorMessage.find( "Unable to create ResourceFilter for:" ), std::string::npos ); + ASSERT_NE( errorMessage.find( "Failed to parse INI file" ), std::string::npos ); + ASSERT_NE( errorMessage.find( "unable to open file" ), std::string::npos ); + } + catch( ... ) + { + FAIL() << "Unknown exception thrown while initializing ResourceFilter with example1.ini"; + } +} + +TEST_F( ResourceFilterTest, ResourceFilter_Load_MultipleFiles_OneThatDoesNotExist ) +{ + const std::filesystem::path iniPath1 = GetTestFileFileAbsolutePath( "ExampleIniFiles/example1.ini" ); + const std::filesystem::path iniPath2 = GetTestFileFileAbsolutePath( "ExampleIniFiles/noSuchFile.ini" ); + std::vector paths = { iniPath1, iniPath2 }; + + ResourceTools::ResourceFilter resourceFilter; + + try + { + resourceFilter.Initialize( paths ); + FAIL() << "Expected this test to fail: ResourceFilter_Load_MultipleFiles_OneThatDoesNotExist"; + } + catch( const std::exception& e ) + { + std::string errorMessage = e.what(); + ASSERT_NE( errorMessage.find( "Unable to create ResourceFilter for:" ), std::string::npos ); + ASSERT_NE( errorMessage.find( "Failed to parse INI file" ), std::string::npos ); + ASSERT_NE( errorMessage.find( "unable to open file" ), std::string::npos ); + } + catch( ... ) + { + FAIL() << "Unknown exception thrown while initializing ResourceFilter with example1.ini"; + } +} + +TEST_F( ResourceFilterTest, ResourceFilter_JustLoad_example1_ini ) +{ + const std::filesystem::path iniPath1 = GetTestFileFileAbsolutePath( "ExampleIniFiles/example1.ini" ); + std::vector paths = { iniPath1 }; + + ResourceTools::ResourceFilter resourceFilter; + + try + { + resourceFilter.Initialize( paths ); + } + catch( const std::exception& e ) + { + FAIL() << "Exception in test, should not have failed: " << e.what(); + } + catch( ... ) + { + FAIL() << "Unknown exception thrown while initializing ResourceFilter with example1.ini"; + } +} + +TEST_F( ResourceFilterTest, ResourceFilter_Test_CurrentWorkingDirectoryChanger ) +{ + // RAII class to change the current working directory for the duration of this test + // Needed so both relative paths in the .ini file resolve correctly, based on paths to the location of the example .ini files + CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); + + const std::filesystem::path iniPath1 = "ExampleIniFiles/example1.ini"; + std::vector paths = { iniPath1 }; + + ResourceTools::ResourceFilter resourceFilter; + try + { + resourceFilter.Initialize( paths ); + ASSERT_EQ( resourceFilter.HasFilters(), true ); + ASSERT_EQ( resourceFilter.GetFullResolvedPathMap().size(), 7 ); + + // Check that the "binaryFileIndex_v0_0_0.txt" file (and it's path) is included correctly + std::filesystem::path oneValidRelativePath = "./Indicies/binaryFileIndex_v0_0_0.txt"; + ASSERT_EQ( resourceFilter.ShouldInclude( oneValidRelativePath ), true ); + } + catch( const std::exception& e ) + { + FAIL() << "Exception in test, should not have failed: " << e.what(); + } + catch( ... ) + { + FAIL() << "Unknown exception thrown while initializing ResourceFilter with example1.ini"; + } +} + +TEST_F( ResourceFilterTest, ResourceFilter_Load_validSimpleExample1_ini_usingRelativePaths ) +{ + // Alter the current working directory for the duration of this test + CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); + + try + { + const std::filesystem::path iniPath1 = "ExampleIniFiles/validSimpleExample1.ini"; + std::vector paths = { iniPath1 }; + ResourceTools::ResourceFilter resourceFilter; + resourceFilter.Initialize( paths ); + + // Validate correct included paths via the resourceFilter: + std::set validResolvedRelativePaths = { + "resourcesOnBranch/introMovie.txt", + "resourcesOnBranch/videoCardCategories.yaml" + }; + + ASSERT_EQ( resourceFilter.HasFilters(), true ); + for( const auto& resolvedRelativePath : validResolvedRelativePaths ) + { + ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativePath ), true ) << "Should have included relative path: " << resolvedRelativePath.generic_string(); + } + + // Additional check to make sure the FullResolvedPathMap contains correct data (either include or exclude): + const auto& fullPathMap = resourceFilter.GetFullResolvedPathMap(); + ASSERT_EQ( fullPathMap.size(), 1 ); + + std::set expectedPaths = { + "./resourcesOnBranch/*" + }; + std::vector expectedIncludes = { ".yaml", ".txt" }; + std::vector expectedExcludes = {}; + + MapContainsPaths( expectedPaths, fullPathMap, "FullResolvedPathMap from validSimpleExample1.ini" ); + ValidatePathMap( expectedPaths, fullPathMap, expectedIncludes, expectedExcludes, "FullResolvedPathMap from validSimpleExample1.ini" ); + } + catch( ... ) + { + FAIL() << "Test [ResourceFilter_Load_validSimpleExample1_ini] failed when it should have passed."; + } +} diff --git a/tests/src/ResourceFilterTest.h b/tests/src/ResourceFilterTest.h index 12a635c..70c9005 100644 --- a/tests/src/ResourceFilterTest.h +++ b/tests/src/ResourceFilterTest.h @@ -5,10 +5,59 @@ #define ResourceFilterTest_H #include "ResourcesTestFixture.h" +#include // Inherit from ResourcesTestFixture to gain access to file and directory helper functions class ResourceFilterTest : public ResourcesTestFixture { }; +// RAII helper class to change the current working directory temporarily (within a scope) +class CurrentWorkingDirectoryChanger { +public: + // Constructor acquires the current path and changes it + explicit CurrentWorkingDirectoryChanger(const std::filesystem::path& new_path) : + original_path_(std::filesystem::current_path()) + { + // Acquire original path + try + { + std::cout << "CurrentWorkingDirectoryChanger - Original directory: " << original_path_.generic_string() << std::endl; + std::filesystem::current_path(new_path); // Change to new path + std::cout << "CurrentWorkingDirectoryChanger - Changed directory to: " << std::filesystem::current_path().generic_string() << std::endl; + } + catch (const std::filesystem::filesystem_error& e) + { + std::cerr << "CurrentWorkingDirectoryChanger - Error changing directory: " << e.what() << std::endl; + // Handle error, maybe throw an exception or set a flag + } + } + + // Destructor restores the original path + ~CurrentWorkingDirectoryChanger() + { + try + { + std::filesystem::current_path(original_path_); // Restore original path + std::cout << "CurrentWorkingDirectoryChanger - Restored directory to: " << std::filesystem::current_path().generic_string() << std::endl; + } + catch (const std::filesystem::filesystem_error& e) + { + std::cerr << "CurrentWorkingDirectoryChanger - Error restoring directory: " << e.what() << std::endl; + } + } + + // Disable copy and move operations to ensure single ownership and prevent issues + CurrentWorkingDirectoryChanger(const CurrentWorkingDirectoryChanger&) = delete; + + CurrentWorkingDirectoryChanger& operator=(const CurrentWorkingDirectoryChanger&) = delete; + + CurrentWorkingDirectoryChanger(CurrentWorkingDirectoryChanger&&) = delete; + + CurrentWorkingDirectoryChanger& operator=(CurrentWorkingDirectoryChanger&&) = delete; + +private: + std::filesystem::path original_path_; +}; + #endif // ResourceFilterTest_H \ No newline at end of file diff --git a/tests/testData/ExampleIniFiles/example1.ini b/tests/testData/ExampleIniFiles/example1.ini index a4dff71..84a45bb 100644 --- a/tests/testData/ExampleIniFiles/example1.ini +++ b/tests/testData/ExampleIniFiles/example1.ini @@ -15,9 +15,7 @@ prefixmap = res:./Indicies;./resourcesOnBranch res2:./ResourceGroups [testYamlFilesOverMultiLineResPathsWithEmptyLines] filter = [ .yaml ] respaths = res:/firstLine/... - res:/secondLine/... - - + res:/secondLine/* res2:/thirdLine/... diff --git a/tests/testData/ExampleIniFiles/validSimpleExample1.ini b/tests/testData/ExampleIniFiles/validSimpleExample1.ini new file mode 100644 index 0000000..9bf2c03 --- /dev/null +++ b/tests/testData/ExampleIniFiles/validSimpleExample1.ini @@ -0,0 +1,15 @@ +[DEFAULT] +prefixmap = res:. + +#============================================================================= +# validSimpleExample1.ini - test file +# - Wildcard include any .txt and .yaml files found (expected 2) +# - introMovie.txt +# - videoCardCategories.yaml +# - Note the prefixmap is set to the current folder (.) +;============================================================================= + +[allFilesInResourcesOnBranchFolderExcludingSubfolders] +filter = [ .yaml .txt] +respaths = res:/resourcesOnBranch/* + diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 03e11ab..bc71deb 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -28,6 +28,7 @@ set(SRC_FILES include/FilterResourcePathFile.h include/FilterPrefixMapEntry.h include/FilterResourcePathFileEntry.h + include/ResourceFilter.h src/BundleStreamIn.cpp src/BundleStreamOut.cpp @@ -51,6 +52,7 @@ set(SRC_FILES src/FilterResourcePathFile.cpp src/FilterPrefixMapEntry.cpp src/FilterResourcePathFileEntry.cpp + src/ResourceFilter.cpp ) add_library(resources-tools STATIC ${SRC_FILES}) diff --git a/tools/include/FilterResourceFile.h b/tools/include/FilterResourceFile.h index 4c447ad..03ddbba 100644 --- a/tools/include/FilterResourceFile.h +++ b/tools/include/FilterResourceFile.h @@ -16,7 +16,9 @@ class FilterResourceFile public: explicit FilterResourceFile( const std::filesystem::path& iniFilePath ); - const std::map& GetFullResolvedPathMap(); + // Returns the full resolved PathMaps for all named sections defined in the resource .ini file + // Key is the "resolved path", Value is the associated FilterResourceFilter (include and exclude filters) + const std::map& GetIniFileResolvedPathMap(); private: std::filesystem::path m_iniFilePath; @@ -26,7 +28,7 @@ class FilterResourceFile std::vector m_namedSections; // Resolved PathMap for all named sections defined in a resource .ini file - std::map m_fullResolvedPathMap; + std::map m_iniFileResolvedPathMap; void ParseIniFile(); }; diff --git a/tools/include/ResourceFilter.h b/tools/include/ResourceFilter.h new file mode 100644 index 0000000..c4a9129 --- /dev/null +++ b/tools/include/ResourceFilter.h @@ -0,0 +1,49 @@ +// Copyright © 2026 CCP ehf. + +#ifndef RESOURCEFILTER_H +#define RESOURCEFILTER_H + +#include +#include +#include +#include + +namespace ResourceTools +{ + +class ResourceFilter +{ +public: + ResourceFilter() = default; + + explicit ResourceFilter( const std::vector& iniFilePaths ); + + void Initialize( const std::vector& iniFilePaths ); + + bool HasFilters() const + { + return !m_filterFiles.empty(); + } + + // Returns the full relative resolved PathMaps from all resource .ini file + // Key is the "relative resolved path", Value is the associated FilterResourceFilter (include and exclude filters) + const std::map& GetFullResolvedPathMap(); + + // Check if the inFilePath should be included or excluded based on filtering rules + bool ShouldInclude( const std::filesystem::path& inFilePath ); + +private: + bool m_initialized{ false }; + + std::vector> m_filterFiles; + + // Resolved PathMap for all .ini files + std::map m_fullResolvedPathMap; + + // Helper function for wildcard matching paths (supports "*" and "...") + static bool WildcardMatch( const std::string& pattern, const std::string& checkStr ); +}; + +} + +#endif // RESOURCEFILTER_H diff --git a/tools/src/FilterNamedSection.cpp b/tools/src/FilterNamedSection.cpp index 15022cd..4e27e62 100644 --- a/tools/src/FilterNamedSection.cpp +++ b/tools/src/FilterNamedSection.cpp @@ -33,7 +33,6 @@ const std::string& FilterNamedSection::GetSectionName() const const std::map& FilterNamedSection::GetCombinedResolvedPathMap() { - // Only populate the Combined map if not already done so. if ( m_resolvedCombinedPathMap.empty() ) { diff --git a/tools/src/FilterResourceFile.cpp b/tools/src/FilterResourceFile.cpp index 493f689..36b2d36 100644 --- a/tools/src/FilterResourceFile.cpp +++ b/tools/src/FilterResourceFile.cpp @@ -10,39 +10,39 @@ namespace ResourceTools FilterResourceFile::FilterResourceFile( const std::filesystem::path& iniFilePath ) : m_iniFilePath( iniFilePath ) { - m_fullResolvedPathMap.clear(); + m_iniFileResolvedPathMap.clear(); ParseIniFile(); } -const std::map& FilterResourceFile::GetFullResolvedPathMap() +const std::map& FilterResourceFile::GetIniFileResolvedPathMap() { - if( m_fullResolvedPathMap.empty() ) + if( m_iniFileResolvedPathMap.empty() ) { - // Populate the full resolved path map from all named sections + // Populate the full resolved path map from all named sections in this INI file for( auto& namedSection : m_namedSections ) { auto& sectionPathMap = namedSection.GetCombinedResolvedPathMap(); for( const auto& kv : sectionPathMap ) { // Combine filters if the same path already exists - auto it = m_fullResolvedPathMap.find( kv.first ); - if( it != m_fullResolvedPathMap.end() ) + auto it = m_iniFileResolvedPathMap.find( kv.first ); + if( it != m_iniFileResolvedPathMap.end() ) { // Combine the filters (using raw filter strings) std::string combinedRawFilter = it->second.GetRawFilter() + " " + kv.second.GetRawFilter(); FilterResourceFilter combinedFilter( combinedRawFilter ); - m_fullResolvedPathMap.insert_or_assign( kv.first, combinedFilter ); + m_iniFileResolvedPathMap.insert_or_assign( kv.first, combinedFilter ); } else { - m_fullResolvedPathMap.insert_or_assign( kv.first, kv.second ); + m_iniFileResolvedPathMap.insert_or_assign( kv.first, kv.second ); } } } } - return m_fullResolvedPathMap; + return m_iniFileResolvedPathMap; } void FilterResourceFile::ParseIniFile() diff --git a/tools/src/ResourceFilter.cpp b/tools/src/ResourceFilter.cpp new file mode 100644 index 0000000..b5d6394 --- /dev/null +++ b/tools/src/ResourceFilter.cpp @@ -0,0 +1,216 @@ +// Copyright © 2026 CCP ehf. + +#include +#include +#include + +namespace ResourceTools +{ + +ResourceFilter::ResourceFilter( const std::vector& iniFilePaths ) +{ + Initialize( iniFilePaths ); +} + +void ResourceFilter::Initialize( const std::vector& iniFilePaths ) +{ + m_fullResolvedPathMap.clear(); + + if( m_initialized ) + { + throw std::runtime_error( "ResourceFilter is already initialized." ); + } + + std::set uniquePaths( iniFilePaths.begin(), iniFilePaths.end() ); + for( const auto& path : uniquePaths ) + { + try + { + m_filterFiles.emplace_back( std::make_unique( path ) ); + } + catch( const std::exception& e ) + { + // Optionally log or handle error + std::string errorMsg = "Unable to create ResourceFilter for: " + path.string() + " - because of: " + e.what(); + throw std::runtime_error( errorMsg ); + } + } + + m_initialized = true; +} + +const std::map& ResourceFilter::GetFullResolvedPathMap() +{ + if( m_fullResolvedPathMap.empty() ) + { + // Populate the full resolved path map from all Filter INI files + for( auto& iniFile : m_filterFiles ) + { + auto& iniFilePathMap = iniFile->GetIniFileResolvedPathMap(); + for( const auto& kv : iniFilePathMap ) + { + // Combine filters if the same path already exists + auto it = m_fullResolvedPathMap.find( kv.first ); + if( it != m_fullResolvedPathMap.end() ) + { + // Combine the filters (using raw filter strings) + std::string combinedRawFilter = it->second.GetRawFilter() + " " + kv.second.GetRawFilter(); + FilterResourceFilter combinedFilter( combinedRawFilter ); + m_fullResolvedPathMap.insert_or_assign( kv.first, combinedFilter ); + } + else + { + m_fullResolvedPathMap.insert_or_assign( kv.first, kv.second ); + } + } + } + } + + return m_fullResolvedPathMap; +} + +bool ResourceFilter::ShouldInclude( const std::filesystem::path& inFilePath ) +{ + // Make sure we work with the absolute path representation of the input file + std::filesystem::path inFilePathAbs = std::filesystem::absolute( inFilePath ); + std::string inFilePathAbsStr = inFilePathAbs.generic_string(); + + // Priority: lower is higher priority: + // -1 = exact match on filename (or folder) + // 0 = wildcard match on same folder level + // 1 = wildcard match, 1 folder up, etc... + int bestIncludePriority = std::numeric_limits::max(); + int bestExcludePriority = std::numeric_limits::max(); + + // Get the full resolved path map and iterate through it (contains relative paths) + const auto& resolvedPathMap = GetFullResolvedPathMap(); + + for( const auto& [resolvedRelativePathStr, filter] : resolvedPathMap ) + { + // Make sure to work with absolute paths for comparison + std::filesystem::path resolvedRelativePath( resolvedRelativePathStr ); + std::filesystem::path resolvedPathAbs = std::filesystem::absolute( resolvedRelativePath ); + std::string resolvedPathAbsStr = resolvedPathAbs.generic_string(); + + if( resolvedPathAbsStr == inFilePathAbsStr ) + { + // If there is an exact match on the full filename path, this means highest priority and + // SHOULD BE considered an "INCLUDE" even though resolvedPath has filters that might say otherwise. + bestIncludePriority = -1; + continue; + } + + if( !WildcardMatch( resolvedPathAbsStr, inFilePathAbsStr ) ) + { + // There was NO wildcard match on paths, ignore this resolvedRelativePath entry + continue; + } + + // There is a Wildcard match - determine the folder depth difference + auto inFileIt = inFilePathAbs.begin(); + auto resolvedIt = resolvedPathAbs.begin(); + while( inFileIt != inFilePathAbs.end() && resolvedIt != resolvedPathAbs.end() && *inFileIt == *resolvedIt ) + { + ++inFileIt; + ++resolvedIt; + } + + std::filesystem::path remainingPath; + int folderDiffDepthPriority = -1; // Start at -1 (making first iteration priority 0 = same folder level) + while( inFileIt != inFilePathAbs.end() ) + { + ++folderDiffDepthPriority; + remainingPath /= *inFileIt; + ++inFileIt; + } + std::string remainingPathStr = remainingPath.generic_string(); + + // Next step is to check the include/exclude filters: + for( const auto& includeToken : filter.GetIncludeFilter() ) + { + if( includeToken == "*" || remainingPathStr.find( includeToken ) != std::string::npos ) + { + if( folderDiffDepthPriority < bestIncludePriority ) + bestIncludePriority = folderDiffDepthPriority; + } + } + + for( const auto& excludeToken : filter.GetExcludeFilter() ) + { + if( excludeToken == "*" || remainingPathStr.find( excludeToken ) != std::string::npos ) + { + if( folderDiffDepthPriority < bestExcludePriority ) + bestExcludePriority = folderDiffDepthPriority; + } + } + } + + // Apply priority rules: + if( bestIncludePriority == std::numeric_limits::max() ) + { + return false; // No include match found => Exclude the file + } + if( bestExcludePriority == std::numeric_limits::max() ) + { + return true; // No exclude match found (but includePriority less than max => Include the file + } + if( bestIncludePriority < bestExcludePriority ) + { + return true; // Include priority is lower => Include the file + } + if( bestExcludePriority < bestIncludePriority ) + { + return false; // Exclude priority is lower => Exclude the file + } + // Both include and exclude have same priority => Exclude the file + return false; +} + + +// pattern = The resolved path from the .ini file (can contain wildcards) +// checkStr = The input file path to check against the pattern +bool ResourceFilter::WildcardMatch( const std::string& pattern, const std::string& checkStr ) +{ + // Replace ... with a unique token, then process * + std::string pat = pattern; + std::string token = "\x01"; + size_t pos; + while( ( pos = pat.find( "..." ) ) != std::string::npos ) + { + pat.replace( pos, 3, token ); + } + std::string regexPat; + for( size_t i = 0; i < pat.size(); ++i ) + { + if( pat[i] == '*' ) + { + regexPat += "[^/]*"; + } + else if( pat[i] == '\x01' ) + { + regexPat += ".*"; + } + else if( std::string( ".^$|()[]{}+?\\" ).find( pat[i] ) != std::string::npos ) + { + // Regex special characters that need escaping + regexPat += '\\'; + regexPat += pat[i]; + } + else + { + regexPat += pat[i]; + } + } + try + { + std::regex re( regexPat, std::regex::ECMAScript | std::regex::icase ); + bool regexResult = std::regex_match( checkStr, re ); + return regexResult; + } + catch( ... ) + { + return false; + } +} + +} // namespace ResourceTools From 42f2006cc434accf91b448c3cedca343e5b880dc Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 21 Jan 2026 15:33:23 +0000 Subject: [PATCH 035/124] Fix formatting --- src/ResourceGroupImpl.cpp | 93 +++++++++++----------- tests/src/ResourceFilterTest.cpp | 122 ++++++++++++++--------------- tools/src/FilterNamedSection.cpp | 3 +- tools/src/FilterResourceFilter.cpp | 18 ++++- 4 files changed, 121 insertions(+), 115 deletions(-) diff --git a/src/ResourceGroupImpl.cpp b/src/ResourceGroupImpl.cpp index 2aa97e5..5ff17f8 100644 --- a/src/ResourceGroupImpl.cpp +++ b/src/ResourceGroupImpl.cpp @@ -161,22 +161,22 @@ Result ResourceGroup::ResourceGroupImpl::CreateFromDirectory( const CreateResour return addResourceResult; } - // If resources are set to be exported, then export as specified - if (params.exportResources) - { + // If resources are set to be exported, then export as specified + if( params.exportResources ) + { ResourcePutDataParams putDataParams; - putDataParams.resourceDestinationSettings = params.exportResourcesDestinationSettings; + putDataParams.resourceDestinationSettings = params.exportResourcesDestinationSettings; - putDataParams.data = &resourceData; + putDataParams.data = &resourceData; Result putDataResult = resource->PutData( putDataParams ); - if( putDataResult.type != ResultType::SUCCESS ) + if( putDataResult.type != ResultType::SUCCESS ) { return putDataResult; } - } + } } else { @@ -188,13 +188,13 @@ Result ResourceGroup::ResourceGroupImpl::CreateFromDirectory( const CreateResour ResourceTools::FileDataStreamIn fileStreamIn( params.resourceStreamThreshold ); - if (params.calculateCompressions) - { + if( params.calculateCompressions ) + { if( !compressionStream.Start() ) { return Result{ ResultType::FAILED_TO_COMPRESS_DATA }; } - } + } if( !fileStreamIn.StartRead( entry.path() ) ) { @@ -287,31 +287,31 @@ Result ResourceGroup::ResourceGroupImpl::CreateFromDirectory( const CreateResour return addResourceResult; } - // If resources are set to be exported, then export as specified. - // This is slow with large files as each need to be streamed again - // The problem is that checksum of the whole file needs to be calculated first - // in order to get the correct destination CDN path - // If compression is not skipped and REMOTE_CDN is chosen as destination then - // compression will also be calculated twice. - // This can be improved with a refactor but currently this code path not + // If resources are set to be exported, then export as specified. + // This is slow with large files as each need to be streamed again + // The problem is that checksum of the whole file needs to be calculated first + // in order to get the correct destination CDN path + // If compression is not skipped and REMOTE_CDN is chosen as destination then + // compression will also be calculated twice. + // This can be improved with a refactor but currently this code path not // likely to be relied upon often if( params.exportResources ) { ResourcePutDataStreamParams putDataStreamParams; - // Create the correct file data streaming for the desination - std::unique_ptr resourceDataStreamOut; + // Create the correct file data streaming for the desination + std::unique_ptr resourceDataStreamOut; - if (params.exportResourcesDestinationSettings.destinationType == ResourceDestinationType::REMOTE_CDN) - { - // REMOTE_CDN requires compression + if( params.exportResourcesDestinationSettings.destinationType == ResourceDestinationType::REMOTE_CDN ) + { + // REMOTE_CDN requires compression resourceDataStreamOut = std::make_unique(); - } - else - { - // Else just stream out uncompressed + } + else + { + // Else just stream out uncompressed resourceDataStreamOut = std::make_unique(); - } + } putDataStreamParams.resourceDestinationSettings = params.exportResourcesDestinationSettings; @@ -324,44 +324,42 @@ Result ResourceGroup::ResourceGroupImpl::CreateFromDirectory( const CreateResour return putDataStreamResult; } - // Export resource using streaming + // Export resource using streaming ResourceTools::FileDataStreamIn fileStreamIn( params.resourceStreamThreshold ); - if( !fileStreamIn.StartRead( entry.path() ) ) + if( !fileStreamIn.StartRead( entry.path() ) ) { return Result{ ResultType::FAILED_TO_OPEN_FILE_STREAM }; } - while( !fileStreamIn.IsFinished() ) + while( !fileStreamIn.IsFinished() ) { std::string data = ""; - if (!(fileStreamIn >> data)) - { + if( !( fileStreamIn >> data ) ) + { return Result{ ResultType::FAILED_TO_READ_FROM_STREAM }; - } + } - if (!(resourceDataStreamOut->operator<<(data))) - { + if( !( resourceDataStreamOut->operator<<( data ) ) ) + { return Result{ ResultType::FAILED_TO_SAVE_TO_STREAM }; - } + } } - if (!resourceDataStreamOut->Finish()) - { + if( !resourceDataStreamOut->Finish() ) + { return Result{ ResultType::FAILED_TO_SAVE_TO_STREAM }; - } - + } } - } } } - if (!params.calculateCompressions) - { + if( !params.calculateCompressions ) + { m_totalResourcesSizeCompressed.Reset(); - } + } if( params.statusCallback ) { @@ -912,14 +910,13 @@ Result ResourceGroup::ResourceGroupImpl::ExportYaml( const VersionInternal& outp out << YAML::Key << m_numberOfResources.GetTag(); out << YAML::Value << m_numberOfResources.GetValue(); - if (m_totalResourcesSizeCompressed.HasValue()) - { + if( m_totalResourcesSizeCompressed.HasValue() ) + { uintmax_t compressedSize = m_totalResourcesSizeCompressed.GetValue(); out << YAML::Key << m_totalResourcesSizeCompressed.GetTag(); out << YAML::Value << compressedSize; - - } + } out << YAML::Key << m_totalResourcesSizeUncompressed.GetTag(); out << YAML::Value << m_totalResourcesSizeUncompressed.GetValue(); diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 5de0f27..7bfd2be 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -815,7 +815,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_EmptyFilter_TopLevel ) // Expected values: std::set expectedPaths = { "/myPath/foo/bar" }; std::vector expectedIncludes = { "*" }; - std::vector expectedExcludes = { }; + std::vector expectedExcludes = {}; const auto& resolvedRespathMap = namedSection.GetResolvedRespathsMap(); const auto& resolvedResfileMap = namedSection.GetResolvedResfileMap(); @@ -1675,78 +1675,78 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_iniFileNotFound ) TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidPrefixmap_ini ) { - const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidPrefixmap.ini" ); - try - { - ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); - FAIL() << "Expected std::invalid_argument when loading ini file with invalid prefixmap"; - } - catch( const std::invalid_argument& e ) - { - std::string expectedError = "Invalid prefixmap format: No paths defined for prefix: prefix1"; - EXPECT_STREQ( e.what(), expectedError.c_str() ); - } - catch( ... ) - { - FAIL() << "Expected std::invalid_argument when loading invalidPrefixmap.ini file"; - } + const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidPrefixmap.ini" ); + try + { + ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + FAIL() << "Expected std::invalid_argument when loading ini file with invalid prefixmap"; + } + catch( const std::invalid_argument& e ) + { + std::string expectedError = "Invalid prefixmap format: No paths defined for prefix: prefix1"; + EXPECT_STREQ( e.what(), expectedError.c_str() ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument when loading invalidPrefixmap.ini file"; + } } TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidSectionFilter_ini ) { - const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidSectionFilter.ini" ); - try - { - ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); - FAIL() << "Expected std::invalid_argument when loading ini file with invalid section filter"; - } - catch( const std::invalid_argument& e ) - { - std::string expectedError = "Invalid filter format: missing ']'"; - EXPECT_STREQ( e.what(), expectedError.c_str() ); - } - catch( ... ) - { - FAIL() << "Expected std::invalid_argument when loading invalidSectionFilter.ini file"; - } + const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidSectionFilter.ini" ); + try + { + ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + FAIL() << "Expected std::invalid_argument when loading ini file with invalid section filter"; + } + catch( const std::invalid_argument& e ) + { + std::string expectedError = "Invalid filter format: missing ']'"; + EXPECT_STREQ( e.what(), expectedError.c_str() ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument when loading invalidSectionFilter.ini file"; + } } TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidInlineFilter_ini ) { - const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidInlineFilter.ini" ); - try - { - ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); - FAIL() << "Expected std::invalid_argument when loading ini file with invalid inline filter"; - } - catch( const std::invalid_argument& e ) - { - std::string expectedError = "Invalid filter format: missing '['"; - EXPECT_STREQ( e.what(), expectedError.c_str() ); - } - catch( ... ) - { - FAIL() << "Expected std::invalid_argument when loading invalidInlineFilter.ini file"; - } + const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidInlineFilter.ini" ); + try + { + ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + FAIL() << "Expected std::invalid_argument when loading ini file with invalid inline filter"; + } + catch( const std::invalid_argument& e ) + { + std::string expectedError = "Invalid filter format: missing '['"; + EXPECT_STREQ( e.what(), expectedError.c_str() ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument when loading invalidInlineFilter.ini file"; + } } TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidPrefixMismatch_ini ) { - const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidPrefixMismatch.ini" ); - try - { - ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); - FAIL() << "Expected std::invalid_argument when loading ini file with prefix mismatch"; - } - catch( const std::invalid_argument& e ) - { - std::string expectedError = "Prefix 'prefixDoesNotExist' not present in prefixMap for line: prefixDoesNotExist:/firstLine/*"; - EXPECT_STREQ( e.what(), expectedError.c_str() ); - } - catch( ... ) - { - FAIL() << "Expected std::invalid_argument when loading invalidPrefixMismatch.ini file"; - } + const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidPrefixMismatch.ini" ); + try + { + ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + FAIL() << "Expected std::invalid_argument when loading ini file with prefix mismatch"; + } + catch( const std::invalid_argument& e ) + { + std::string expectedError = "Prefix 'prefixDoesNotExist' not present in prefixMap for line: prefixDoesNotExist:/firstLine/*"; + EXPECT_STREQ( e.what(), expectedError.c_str() ); + } + catch( ... ) + { + FAIL() << "Expected std::invalid_argument when loading invalidPrefixMismatch.ini file"; + } } // ------------------------------------------ diff --git a/tools/src/FilterNamedSection.cpp b/tools/src/FilterNamedSection.cpp index 4e27e62..aa20076 100644 --- a/tools/src/FilterNamedSection.cpp +++ b/tools/src/FilterNamedSection.cpp @@ -21,7 +21,6 @@ FilterNamedSection::FilterNamedSection( std::string sectionName, if( respaths.empty() ) { - // TODO: Change this to a defined error code/type throw std::invalid_argument( "Respaths attribute is empty for section: " + m_sectionName ); } } @@ -34,7 +33,7 @@ const std::string& FilterNamedSection::GetSectionName() const const std::map& FilterNamedSection::GetCombinedResolvedPathMap() { // Only populate the Combined map if not already done so. - if ( m_resolvedCombinedPathMap.empty() ) + if( m_resolvedCombinedPathMap.empty() ) { // Populate the combined map. for( const auto& kv : m_respaths.GetResolvedPathMap() ) diff --git a/tools/src/FilterResourceFilter.cpp b/tools/src/FilterResourceFilter.cpp index 6b6eb0b..d41747d 100644 --- a/tools/src/FilterResourceFilter.cpp +++ b/tools/src/FilterResourceFilter.cpp @@ -34,11 +34,15 @@ void FilterResourceFilter::PlaceTokenInCorrectVector( const std::string& token, // Remove token from the fromVector if present auto it = std::find( fromVector.begin(), fromVector.end(), token ); if( it != fromVector.end() ) + { fromVector.erase( it ); + } // Add token to the toVector if not already present in it. if( std::find( toVector.begin(), toVector.end(), token ) == toVector.end() ) + { toVector.push_back( token ); + } } void FilterResourceFilter::ParseFilters() @@ -52,9 +56,13 @@ void FilterResourceFilter::ParseFilters() { // Skip whitespace while( pos < s.size() && std::isspace( static_cast( s[pos] ) ) ) + { ++pos; + } if( pos >= s.size() ) + { break; + } // Check for exclude filter marker '!' bool isExclude = false; @@ -64,17 +72,17 @@ void FilterResourceFilter::ParseFilters() isExclude = true; ++pos; while( pos < s.size() && std::isspace( static_cast( s[pos] ) ) ) + { ++pos; + } if( pos >= s.size() ) { - // TODO: Change this to a defined error code/type throw std::invalid_argument( "Invalid filter format: exclude filter marker found without a [ token ] section" ); } } if( pos >= s.size() || s[pos] != '[' ) { - // TODO: Change this to a defined error code/type throw std::invalid_argument( "Invalid filter format: missing '['" ); } ++pos; // skip '[' @@ -83,13 +91,11 @@ void FilterResourceFilter::ParseFilters() size_t nextStartBracket = s.find( '[', pos ); if( nextStartBracket != std::string::npos && nextStartBracket < endBracket ) { - // TODO: Change this to a defined error code/type throw std::invalid_argument( "Invalid filter format: matching end bracket ']' not present before the next start bracket '['" ); } if( endBracket == std::string::npos ) { - // TODO: Change this to a defined error code/type throw std::invalid_argument( "Invalid filter format: missing ']'" ); } @@ -102,11 +108,15 @@ void FilterResourceFilter::ParseFilters() size_t start = token.find_first_not_of( " \t\r\n" ); size_t end = token.find_last_not_of( " \t\r\n" ); if( start == std::string::npos || end == std::string::npos ) + { continue; + } token = token.substr( start, end - start + 1 ); if( token.empty() ) + { continue; + } PlaceTokenInCorrectVector( token, isExclude ? m_includeFilter : m_excludeFilter, From eb4917eb7e9ffb4587d5337932dd7485a808a34e Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 21 Jan 2026 15:35:12 +0000 Subject: [PATCH 036/124] Remove TODOs - No need as the top-level ResourceGroupImpl one will return the correct top-level error code to the caller. --- tools/src/FilterPrefixMapEntry.cpp | 2 -- tools/src/FilterPrefixmap.cpp | 3 --- tools/src/FilterResourceFile.cpp | 3 --- tools/src/FilterResourcePathFileEntry.cpp | 2 -- 4 files changed, 10 deletions(-) diff --git a/tools/src/FilterPrefixMapEntry.cpp b/tools/src/FilterPrefixMapEntry.cpp index cb1232b..2334283 100644 --- a/tools/src/FilterPrefixMapEntry.cpp +++ b/tools/src/FilterPrefixMapEntry.cpp @@ -17,7 +17,6 @@ void FilterPrefixMapEntry::AppendPaths( const std::string& prefix, const std::st { if( prefix != m_prefix ) { - // TODO: Change this to a defined error code/type throw std::invalid_argument( "Prefix mismatch while appending path(s): " + prefix + " (incoming) != " + m_prefix + " (existing)" ); } @@ -36,7 +35,6 @@ void FilterPrefixMapEntry::AppendPaths( const std::string& prefix, const std::st } if( m_paths.empty() ) { - // TODO: Change this to a defined error code/type throw std::invalid_argument( "Invalid prefixmap format: No paths appended for prefix: " + m_prefix ); } } diff --git a/tools/src/FilterPrefixmap.cpp b/tools/src/FilterPrefixmap.cpp index b9831cc..5b5aa68 100644 --- a/tools/src/FilterPrefixmap.cpp +++ b/tools/src/FilterPrefixmap.cpp @@ -29,14 +29,12 @@ void FilterPrefixMap::ParsePrefixMap( const std::string& rawPrefixMap ) std::size_t colon = rawPrefixMap.find( ':', pos ); if( colon == std::string::npos ) { - // TODO: Change this to a defined error code/type throw std::invalid_argument( "Invalid prefixmap format: missing ':'" ); } std::string prefix = rawPrefixMap.substr( pos, colon - pos ); if( prefix.empty() ) { - // TODO: Change this to a defined error code/type throw std::invalid_argument( "Invalid prefixmap format: empty prefix" ); } @@ -49,7 +47,6 @@ void FilterPrefixMap::ParsePrefixMap( const std::string& rawPrefixMap ) if( rawPaths.empty() ) { - // TODO: Change this to a defined error code/type throw std::invalid_argument( "Invalid prefixmap format: No paths defined for prefix: " + prefix ); } diff --git a/tools/src/FilterResourceFile.cpp b/tools/src/FilterResourceFile.cpp index 36b2d36..d657e78 100644 --- a/tools/src/FilterResourceFile.cpp +++ b/tools/src/FilterResourceFile.cpp @@ -51,14 +51,12 @@ void FilterResourceFile::ParseIniFile() INIReader reader( m_iniFilePath.string() ); if( reader.ParseError() != 0 ) { - // TODO: Change this to a defined error code/type throw std::runtime_error( "Failed to parse INI file: " + m_iniFilePath.string() + " - " + reader.ParseErrorMessage() ); } // Parse the [DEFAULT] section if( !reader.HasSection( "DEFAULT" ) ) { - // TODO: Change this to a defined error code/type throw std::invalid_argument( "Missing [DEFAULT] section in INI file: " + m_iniFilePath.string() ); } m_defaultSection = FilterDefaultSection( reader.Get( "DEFAULT", "prefixmap", "" ) ); @@ -86,7 +84,6 @@ void FilterResourceFile::ParseIniFile() if( respaths.empty() ) { - // TODO: Change this to a defined error code/type throw std::invalid_argument( "Respaths attribute is empty for section: " + sectionName ); } diff --git a/tools/src/FilterResourcePathFileEntry.cpp b/tools/src/FilterResourcePathFileEntry.cpp index bebf157..d4d0ecf 100644 --- a/tools/src/FilterResourcePathFileEntry.cpp +++ b/tools/src/FilterResourcePathFileEntry.cpp @@ -56,7 +56,6 @@ void FilterResourcePathFileEntry::ParseRawPathLine() size_t colon = rawPathToken.find( ':' ); if( colon == std::string::npos ) { - // TODO: Change this to a defined error code/type throw std::invalid_argument( std::string( "Missing prefix in path for: " ) + m_rawPathLine ); } std::string prefix = rawPathToken.substr( 0, colon ); @@ -66,7 +65,6 @@ void FilterResourcePathFileEntry::ParseRawPathLine() auto it = prefixMapEntries.find( prefix ); if( it == prefixMapEntries.end() ) { - // TODO: Change this to a defined error code/type throw std::invalid_argument( std::string( "Prefix '" ) + prefix + "' not present in prefixMap for line: " + m_rawPathLine ); } From 31989f892cb046e2000051532472578096851263 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 22 Jan 2026 15:51:42 +0000 Subject: [PATCH 037/124] Add a complex include/exclude via different relative paths test - The test should cover most (if not all) realistic mutations of same file(s) via different prefixmap relative paths includes/excludes. --- tests/src/ResourceFilterTest.cpp | 158 ++++++++++++++++++ .../ExampleIniFiles/validComplexExample1.ini | 29 ++++ .../ExampleIniFiles/validSimpleExample1.ini | 2 +- tools/src/ResourceFilter.cpp | 10 ++ 4 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 tests/testData/ExampleIniFiles/validComplexExample1.ini diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 7bfd2be..aafa271 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -1895,3 +1895,161 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load_validSimpleExample1_ini_usingRel FAIL() << "Test [ResourceFilter_Load_validSimpleExample1_ini] failed when it should have passed."; } } + +TEST_F( ResourceFilterTest, ResourceFilter_Load_validSimpleExample1_ini_usingAbsolutePaths ) +{ + // Alter the current working directory for the duration of this test + CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); + + try + { + const std::filesystem::path iniPath1 = "ExampleIniFiles/validSimpleExample1.ini"; + std::filesystem::path iniPath1Abs = std::filesystem::absolute( iniPath1 ); + std::vector paths = { iniPath1Abs }; + ResourceTools::ResourceFilter resourceFilter; + resourceFilter.Initialize( paths ); + + // Validate correct included paths via the resourceFilter: + std::set validResolvedAbsolutePaths = { + std::filesystem::absolute( "resourcesOnBranch/introMovie.txt" ), + std::filesystem::absolute( "resourcesOnBranch/videoCardCategories.yaml" ) + }; + + ASSERT_EQ( resourceFilter.HasFilters(), true ); + for( const auto& resolvedAbsPath : validResolvedAbsolutePaths ) + { + ASSERT_EQ( resourceFilter.ShouldInclude( resolvedAbsPath ), true ) << "Should have included absolute path: " << resolvedAbsPath.generic_string(); + } + + // Additional check to make sure the FullResolvedPathMap contains correct data (either include or exclude): + const auto& fullPathMap = resourceFilter.GetFullResolvedPathMap(); + ASSERT_EQ( fullPathMap.size(), 1 ); + + std::set expectedPaths = { + "./resourcesOnBranch/*" + }; + std::vector expectedIncludes = { ".yaml", ".txt" }; + std::vector expectedExcludes = {}; + + MapContainsPaths( expectedPaths, fullPathMap, "FullResolvedPathMap from validSimpleExample1.ini" ); + ValidatePathMap( expectedPaths, fullPathMap, expectedIncludes, expectedExcludes, "FullResolvedPathMap from validSimpleExample1.ini" ); + } + catch( ... ) + { + FAIL() << "Test [ResourceFilter_Load_validSimpleExample1_ini_usingAbsolutePaths] failed when it should have passed."; + } +} + +TEST_F( ResourceFilterTest, ResourceFilter_Load_validComplexExample1_ini_usingRelativePaths ) +{ + // Alter the current working directory for the duration of this test + CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); + + try + { + const std::filesystem::path iniPath1 = "ExampleIniFiles/validComplexExample1.ini"; + std::vector paths = { iniPath1 }; + ResourceTools::ResourceFilter resourceFilter; + resourceFilter.Initialize( paths ); + + // Validate correct included paths via the resourceFilter: + std::set validResolvedRelativePaths = { + //"PatchWithInputChunk/NextBuildResources/introMovie.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + "PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + resLocalCDN:/../NextBuildResources/introMoviePrefixed.txt + //"PatchWithInputChunk/NextBuildResources/introMovieSomewhatChanged.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + "PatchWithInputChunk/NextBuildResources/testResource2.txt", + "PatchWithInputChunk/NextBuildResources/videoCardCategories.yaml", + "PatchWithInputChunk/PreviousBuildResources/introMovie.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] + "PatchWithInputChunk/PreviousBuildResources/introMoviePrefixed.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] + "PatchWithInputChunk/PreviousBuildResources/introMovieSomewhatChanged.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] + //"PatchWithInputChunk/PreviousBuildResources/testResource.txt", // resRoot:/PatchWithInputChunk/PreviousBuildResources/* ![ testResource.txt ] + "PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml", + "PatchWithInputChunk/PatchResourceGroup_previousBuild_latestBuild.yaml", + "PatchWithInputChunk/resFileIndexShort_build_next.txt", + "PatchWithInputChunk/resFileIndexShort_build_previous.txt", + }; + + ASSERT_EQ( resourceFilter.HasFilters(), true ); + for( const auto& resolvedRelativePath : validResolvedRelativePaths ) + { + ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativePath ), true ) << "Should have included relative path: " << resolvedRelativePath.generic_string(); + } + + // Additional check to make sure the FullResolvedPathMap contains correct data (either include or exclude): + std::set expectedPaths = { + "PatchWithInputChunk/...", + "./PatchWithInputChunk/...", + "PatchWithInputChunk/PreviousBuildResources/*", + "PatchWithInputChunk/LocalCDNPatches/../NextBuildResources/introMoviePrefixed.txt", + "./PatchWithInputChunk/PreviousBuildResources/*" + }; + const auto& fullPathMap = resourceFilter.GetFullResolvedPathMap(); + ASSERT_EQ( fullPathMap.size(), expectedPaths.size() ); + + std::vector expectedIncludes = { ".yaml", ".txt" }; + std::vector expectedExcludes = {}; + std::vector overrideExcludes = { "Movie" }; + + MapContainsPaths( expectedPaths, fullPathMap, "FullResolvedPathMap from validSimpleExample1.ini" ); + //ValidatePathMap( expectedPaths, fullPathMap, expectedIncludes, expectedExcludes, "FullResolvedPathMap from validSimpleExample1.ini" ); + // Manually validate the fullPathMap, as it has several different prefixPathCombos + some inline filter overrides + for( const auto& kv : fullPathMap ) + { + if( kv.first == "PatchWithInputChunk/..." ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".yaml" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".txt" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 0 ); + } + else if( kv.first == "./PatchWithInputChunk/..." ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".yaml" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".txt" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 1 ); + EXPECT_EQ( kv.second.GetExcludeFilter()[0], "Movie" ); + } + else if( kv.first == "PatchWithInputChunk/PreviousBuildResources/*" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 3 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".yaml" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".txt" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), "Movie" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 0 ); + } + else if( kv.first == "PatchWithInputChunk/LocalCDNPatches/../NextBuildResources/introMoviePrefixed.txt" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".yaml" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".txt" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 0 ); + } + else if( kv.first == "./PatchWithInputChunk/PreviousBuildResources/*" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".yaml" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".txt" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 1 ); + EXPECT_EQ( kv.second.GetExcludeFilter()[0], "testResource.txt" ); + } + else + { + FAIL() << "Unexpected path found in FullResolvedPathMap: " << kv.first; + } + } + } + catch( const std::exception& e ) + { + FAIL() << "Test [ResourceFilter_Load_validComplexExample1_ini_usingRelativePaths] failed with: " << e.what(); + } + catch( ... ) + { + FAIL() << "Test [ResourceFilter_Load_validComplexExample1_ini_usingRelativePaths] failed when it should have passed."; + } +} \ No newline at end of file diff --git a/tests/testData/ExampleIniFiles/validComplexExample1.ini b/tests/testData/ExampleIniFiles/validComplexExample1.ini new file mode 100644 index 0000000..59db777 --- /dev/null +++ b/tests/testData/ExampleIniFiles/validComplexExample1.ini @@ -0,0 +1,29 @@ +[DEFAULT] +prefixmap = resRoot:. resPatch:PatchWithInputChunk resLocalCDN:PatchWithInputChunk/LocalCDNPatches resPrevious:PatchWithInputChunk/PreviousBuildResources + +#============================================================================= +# validComplexExample1.ini - test file +# - The prefixmap contains four entries: +# * resRoot: => The testData (root) folder, in order to have access to resourceOnBranch folder +# * resPatch: => A folder containing 3 sub folders and both .yaml and .txt files in its root +# * resLocalCDN: => A subfolder that we can use to create different relative patsh to same actual path from either resRoot or resPatch +# * resPrevious: => A subfolder to be used to create different priority levels for same actual path from either resRoot or resPatch +;============================================================================= + +# This test should: +# A. In resRoot/PatchWithInputChunk/.., Exclude ![ Movie ] ==> Should exclude *Movie* files in the two sub folders +# B. In resPrevious/*, Include [ Movie ] ==> Should include *Movie* files in the previous folder only +# C. In resPatch/NextBuildResources/introMoviePrefixed.txt, ==> Should include only this file +# D. In resRoot/PatchWithInputChunk/PreviousBuildResources/*, Exclude ![ testResource.txt ] ==> Should exclude testResource.txt only + + +[resPatch_EllipseIncludeBothYamlAndTxtFiles] +filter = [ .yaml .txt ] +respaths = resPatch:/... + resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] + resLocalCDN:/../NextBuildResources/introMoviePrefixed.txt + resRoot:/PatchWithInputChunk/PreviousBuildResources/* ![ testResource.txt ] + + + diff --git a/tests/testData/ExampleIniFiles/validSimpleExample1.ini b/tests/testData/ExampleIniFiles/validSimpleExample1.ini index 9bf2c03..27f3f8e 100644 --- a/tests/testData/ExampleIniFiles/validSimpleExample1.ini +++ b/tests/testData/ExampleIniFiles/validSimpleExample1.ini @@ -10,6 +10,6 @@ prefixmap = res:. ;============================================================================= [allFilesInResourcesOnBranchFolderExcludingSubfolders] -filter = [ .yaml .txt] +filter = [ .yaml .txt ] respaths = res:/resourcesOnBranch/* diff --git a/tools/src/ResourceFilter.cpp b/tools/src/ResourceFilter.cpp index b5d6394..691f503 100644 --- a/tools/src/ResourceFilter.cpp +++ b/tools/src/ResourceFilter.cpp @@ -100,6 +100,16 @@ bool ResourceFilter::ShouldInclude( const std::filesystem::path& inFilePath ) continue; } + // std::filesystem::path does not support "..." (recursive wildcard). + // We need to append it to the absolute path (if specified) before WildcardMatching + if( resolvedRelativePathStr.find( "..." ) != std::string::npos ) + { + if( resolvedPathAbsStr.back() != '/' ) + { + resolvedPathAbsStr += '/'; + } + resolvedPathAbsStr += "..."; + } if( !WildcardMatch( resolvedPathAbsStr, inFilePathAbsStr ) ) { // There was NO wildcard match on paths, ignore this resolvedRelativePath entry From 93b694211d0b09e488e0a0d2edcfa38214e6be91 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 23 Jan 2026 09:49:17 +0000 Subject: [PATCH 038/124] Add ResourceFilter test using two .ini files --- tests/src/ResourceFilterTest.cpp | 131 +++++++++++++++++++++++++++++-- 1 file changed, 125 insertions(+), 6 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index aafa271..1a2e64a 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -1985,13 +1985,8 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load_validComplexExample1_ini_usingRe }; const auto& fullPathMap = resourceFilter.GetFullResolvedPathMap(); ASSERT_EQ( fullPathMap.size(), expectedPaths.size() ); - - std::vector expectedIncludes = { ".yaml", ".txt" }; - std::vector expectedExcludes = {}; - std::vector overrideExcludes = { "Movie" }; - MapContainsPaths( expectedPaths, fullPathMap, "FullResolvedPathMap from validSimpleExample1.ini" ); - //ValidatePathMap( expectedPaths, fullPathMap, expectedIncludes, expectedExcludes, "FullResolvedPathMap from validSimpleExample1.ini" ); + // Manually validate the fullPathMap, as it has several different prefixPathCombos + some inline filter overrides for( const auto& kv : fullPathMap ) { @@ -2052,4 +2047,128 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load_validComplexExample1_ini_usingRe { FAIL() << "Test [ResourceFilter_Load_validComplexExample1_ini_usingRelativePaths] failed when it should have passed."; } +} + +TEST_F( ResourceFilterTest, ResourceFilter_Load2iniFiles_validComplexExample1_and_validSimpleExample1 ) +{ + // Alter the current working directory for the duration of this test + CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); + + try + { + std::vector paths = { + "ExampleIniFiles/validComplexExample1.ini", + "ExampleIniFiles/validSimpleExample1.ini" + }; + ResourceTools::ResourceFilter resourceFilter; + resourceFilter.Initialize( paths ); + + // Validate correct included paths via the resourceFilter: + std::set validResolvedRelativePaths = { + // From validComplexExample1: + //"PatchWithInputChunk/NextBuildResources/introMovie.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + "PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + resLocalCDN:/../NextBuildResources/introMoviePrefixed.txt + //"PatchWithInputChunk/NextBuildResources/introMovieSomewhatChanged.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + "PatchWithInputChunk/NextBuildResources/testResource2.txt", + "PatchWithInputChunk/NextBuildResources/videoCardCategories.yaml", + "PatchWithInputChunk/PreviousBuildResources/introMovie.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] + "PatchWithInputChunk/PreviousBuildResources/introMoviePrefixed.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] + "PatchWithInputChunk/PreviousBuildResources/introMovieSomewhatChanged.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] + //"PatchWithInputChunk/PreviousBuildResources/testResource.txt", // resRoot:/PatchWithInputChunk/PreviousBuildResources/* ![ testResource.txt ] + "PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml", + "PatchWithInputChunk/PatchResourceGroup_previousBuild_latestBuild.yaml", + "PatchWithInputChunk/resFileIndexShort_build_next.txt", + "PatchWithInputChunk/resFileIndexShort_build_previous.txt", + // From validSimpleExample1.ini: + "resourcesOnBranch/introMovie.txt", + "resourcesOnBranch/videoCardCategories.yaml" + }; + + ASSERT_EQ( resourceFilter.HasFilters(), true ); + for( const auto& resolvedRelativePath : validResolvedRelativePaths ) + { + ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativePath ), true ) << "Should have included relative path: " << resolvedRelativePath.generic_string(); + } + + // Additional check to make sure the FullResolvedPathMap contains correct data (either include or exclude): + std::set expectedPaths = { + "PatchWithInputChunk/...", + "./PatchWithInputChunk/...", + "PatchWithInputChunk/PreviousBuildResources/*", + "PatchWithInputChunk/LocalCDNPatches/../NextBuildResources/introMoviePrefixed.txt", + "./PatchWithInputChunk/PreviousBuildResources/*", + "./resourcesOnBranch/*" + }; + const auto& fullPathMap = resourceFilter.GetFullResolvedPathMap(); + ASSERT_EQ( fullPathMap.size(), expectedPaths.size() ); + MapContainsPaths( expectedPaths, fullPathMap, "FullResolvedPathMap from two ini files" ); + + // Manually validate the fullPathMap, as it has several different prefixPathCombos + some inline filter overrides + for( const auto& kv : fullPathMap ) + { + if( kv.first == "PatchWithInputChunk/..." ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".yaml" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".txt" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 0 ); + } + else if( kv.first == "./PatchWithInputChunk/..." ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".yaml" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".txt" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 1 ); + EXPECT_EQ( kv.second.GetExcludeFilter()[0], "Movie" ); + } + else if( kv.first == "PatchWithInputChunk/PreviousBuildResources/*" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 3 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".yaml" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".txt" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), "Movie" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 0 ); + } + else if( kv.first == "PatchWithInputChunk/LocalCDNPatches/../NextBuildResources/introMoviePrefixed.txt" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".yaml" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".txt" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 0 ); + } + else if( kv.first == "./PatchWithInputChunk/PreviousBuildResources/*" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".yaml" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".txt" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 1 ); + EXPECT_EQ( kv.second.GetExcludeFilter()[0], "testResource.txt" ); + } + else if( kv.first == "./resourcesOnBranch/*" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".yaml" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".txt" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 0 ); + } + else + { + FAIL() << "Unexpected path found in FullResolvedPathMap: " << kv.first; + } + } + } + catch( const std::exception& e ) + { + FAIL() << "Test [ResourceFilter_Load2iniFiles_validComplexExample1_and_validSimpleExample1] failed with: " << e.what(); + } + catch( ... ) + { + FAIL() << "Test [ResourceFilter_Load2iniFiles_validComplexExample1_and_validSimpleExample1] failed when it should have passed."; + } } \ No newline at end of file From cf592523433be3b079f222b464596bb2a2972c61 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 26 Jan 2026 09:54:15 +0000 Subject: [PATCH 039/124] Hook up CLI argument --filter-files --- cli/src/CreateResourceGroupCliOperation.cpp | 66 ++++++++++++++------- cli/src/CreateResourceGroupCliOperation.h | 22 ++++--- 2 files changed, 59 insertions(+), 29 deletions(-) diff --git a/cli/src/CreateResourceGroupCliOperation.cpp b/cli/src/CreateResourceGroupCliOperation.cpp index d4b567a..3239c30 100644 --- a/cli/src/CreateResourceGroupCliOperation.cpp +++ b/cli/src/CreateResourceGroupCliOperation.cpp @@ -15,7 +15,8 @@ CreateResourceGroupCliOperation::CreateResourceGroupCliOperation() : m_createResourceGroupSkipCompressionCalculationId( "--skip-compression" ), m_createResourceGroupExportResourcesId( "--export-resources" ), m_createResourceGroupExportResourcesDestinationTypeId( "--export-resources-destination-type" ), - m_createResourceGroupExportResourcesDestinationPathId( "--export-resources-destination-path" ) + m_createResourceGroupExportResourcesDestinationPathId( "--export-resources-destination-path" ), + m_createResourceGroupIniFilterFilesArgumentId( "--filter-files" ) { AddRequiredPositionalArgument( m_createResourceGroupPathArgumentId, "Base directory to create resource group from." ); @@ -32,25 +33,27 @@ CreateResourceGroupCliOperation::CreateResourceGroupCliOperation() : AddArgument( m_createResourceGroupDocumentVersionArgumentId, "Document version for created resource group.", false, false, VersionToString( defaultImportParams.outputDocumentVersion ) ); AddArgument( m_createResourceGroupResourcePrefixArgumentId, R"(Optional resource path prefix, such as "res" or "app")", false, false, "" ); - - AddArgumentFlag( m_createResourceGroupSkipCompressionCalculationId, "Set skip compression calculations on resources." ); - AddArgumentFlag( m_createResourceGroupExportResourcesId, "Export resources after processing. see --export-resources-destination-type and --export-resources-destination-path" ); + AddArgumentFlag( m_createResourceGroupSkipCompressionCalculationId, "Set skip compression calculations on resources." ); - AddArgument( m_createResourceGroupExportResourcesDestinationTypeId, "Represents the type of repository where exported resources will be saved. Requires --export-resources", false, false, DestinationTypeToString( defaultImportParams.exportResourcesDestinationSettings.destinationType ), ResourceDestinationTypeChoicesAsString() ); + AddArgumentFlag( m_createResourceGroupExportResourcesId, "Export resources after processing. see --export-resources-destination-type and --export-resources-destination-path" ); + + AddArgument( m_createResourceGroupExportResourcesDestinationTypeId, "Represents the type of repository where exported resources will be saved. Requires --export-resources", false, false, DestinationTypeToString( defaultImportParams.exportResourcesDestinationSettings.destinationType ), ResourceDestinationTypeChoicesAsString() ); AddArgument( m_createResourceGroupExportResourcesDestinationPathId, "Represents the base path where the exported resources will be saved. Requires --export-resources", false, false, defaultImportParams.exportResourcesDestinationSettings.basePath.string() ); + + AddArgument( m_createResourceGroupIniFilterFilesArgumentId, "Path to INI file(s) for resource filtering. Can be specified multiple times.", false, true, "" ); } bool CreateResourceGroupCliOperation::Execute( std::string& returnErrorMessage ) const { CarbonResources::CreateResourceGroupFromDirectoryParams createResourceGroupParams; - CarbonResources::ResourceGroupExportToFileParams exportParams; + CarbonResources::ResourceGroupExportToFileParams exportParams; - createResourceGroupParams.directory = m_argumentParser->get( m_createResourceGroupPathArgumentId ); + createResourceGroupParams.directory = m_argumentParser->get( m_createResourceGroupPathArgumentId ); - bool versionIsValid = ParseDocumentVersion( m_argumentParser->get( m_createResourceGroupDocumentVersionArgumentId ), createResourceGroupParams.outputDocumentVersion ); + bool versionIsValid = ParseDocumentVersion( m_argumentParser->get( m_createResourceGroupDocumentVersionArgumentId ), createResourceGroupParams.outputDocumentVersion ); if( !versionIsValid ) { @@ -63,7 +66,7 @@ bool CreateResourceGroupCliOperation::Execute( std::string& returnErrorMessage ) createResourceGroupParams.calculateCompressions = !m_argumentParser->get( m_createResourceGroupSkipCompressionCalculationId ); - createResourceGroupParams.exportResources = m_argumentParser->get( m_createResourceGroupExportResourcesId ); + createResourceGroupParams.exportResources = m_argumentParser->get( m_createResourceGroupExportResourcesId ); if( createResourceGroupParams.exportResources ) { @@ -79,17 +82,36 @@ bool CreateResourceGroupCliOperation::Execute( std::string& returnErrorMessage ) createResourceGroupParams.exportResourcesDestinationSettings.basePath = m_argumentParser->get( m_createResourceGroupExportResourcesDestinationPathId ); } + exportParams.filename = m_argumentParser->get( m_createResourceGroupOutputFileArgumentId ); - exportParams.filename = m_argumentParser->get( m_createResourceGroupOutputFileArgumentId ); + exportParams.outputDocumentVersion = createResourceGroupParams.outputDocumentVersion; - exportParams.outputDocumentVersion = createResourceGroupParams.outputDocumentVersion; + if( m_argumentParser->is_used( m_createResourceGroupIniFilterFilesArgumentId )) + { + std::vector filterIniFilePaths; + auto iniFileStringVector = m_argumentParser->get>( m_createResourceGroupIniFilterFilesArgumentId ); + + for( const auto& iniPathStr : iniFileStringVector ) + { + if( !iniPathStr.empty() ) + { + filterIniFilePaths.push_back( iniPathStr ); + } + } + if ( !filterIniFilePaths.empty() ) + { + createResourceGroupParams.resourceFilterIniFiles = filterIniFilePaths; + } + } PrintStartBanner( createResourceGroupParams, exportParams ); return CreateResourceGroup( createResourceGroupParams, exportParams ); } -void CreateResourceGroupCliOperation::PrintStartBanner( CarbonResources::CreateResourceGroupFromDirectoryParams& createResourceGroupFromDirectoryParams, CarbonResources::ResourceGroupExportToFileParams& ResourceGroupExportToFileParams ) const +void CreateResourceGroupCliOperation::PrintStartBanner( + CarbonResources::CreateResourceGroupFromDirectoryParams& createResourceGroupFromDirectoryParams, + CarbonResources::ResourceGroupExportToFileParams& ResourceGroupExportToFileParams ) const { if( s_verbosityLevel == CarbonResources::StatusLevel::OFF ) { @@ -104,26 +126,26 @@ void CreateResourceGroupCliOperation::PrintStartBanner( CarbonResources::CreateR std::cout << "Output File: " << ResourceGroupExportToFileParams.filename << std::endl; - std::cout << "Output Document Version: " << VersionToString(ResourceGroupExportToFileParams.outputDocumentVersion) << std::endl; + std::cout << "Output Document Version: " << VersionToString( ResourceGroupExportToFileParams.outputDocumentVersion ) << std::endl; std::cout << "Resource Prefix: " << createResourceGroupFromDirectoryParams.resourcePrefix << std::endl; - if( createResourceGroupFromDirectoryParams.calculateCompressions) - { + if( createResourceGroupFromDirectoryParams.calculateCompressions ) + { std::cout << "Calculate Compression: On" << std::endl; - } + } else { std::cout << "Calculate Compression: Off" << std::endl; } - if( createResourceGroupFromDirectoryParams.exportResources ) + if( createResourceGroupFromDirectoryParams.exportResources ) { std::cout << "Export Resources: On" << std::endl; - std::cout << "Export Resources Type: " << DestinationTypeToString( createResourceGroupFromDirectoryParams.exportResourcesDestinationSettings.destinationType ) << std::endl; + std::cout << "Export Resources Type: " << DestinationTypeToString( createResourceGroupFromDirectoryParams.exportResourcesDestinationSettings.destinationType ) << std::endl; - std::cout << "Export Resources Base Path: " << createResourceGroupFromDirectoryParams.exportResourcesDestinationSettings.basePath << std::endl; + std::cout << "Export Resources Base Path: " << createResourceGroupFromDirectoryParams.exportResourcesDestinationSettings.basePath << std::endl; } else { @@ -134,11 +156,13 @@ void CreateResourceGroupCliOperation::PrintStartBanner( CarbonResources::CreateR << std::endl; } -bool CreateResourceGroupCliOperation::CreateResourceGroup( CarbonResources::CreateResourceGroupFromDirectoryParams& createResourceGroupFromDirectoryParams, CarbonResources::ResourceGroupExportToFileParams& ResourceGroupExportToFileParams ) const +bool CreateResourceGroupCliOperation::CreateResourceGroup( + CarbonResources::CreateResourceGroupFromDirectoryParams& createResourceGroupFromDirectoryParams, + CarbonResources::ResourceGroupExportToFileParams& ResourceGroupExportToFileParams ) const { CarbonResources::ResourceGroup resourceGroup; - createResourceGroupFromDirectoryParams.statusCallback = GetStatusCallback(); + createResourceGroupFromDirectoryParams.statusCallback = GetStatusCallback(); if( createResourceGroupFromDirectoryParams.statusCallback ) { diff --git a/cli/src/CreateResourceGroupCliOperation.h b/cli/src/CreateResourceGroupCliOperation.h index ca9cee1..d40f982 100644 --- a/cli/src/CreateResourceGroupCliOperation.h +++ b/cli/src/CreateResourceGroupCliOperation.h @@ -18,9 +18,13 @@ class CreateResourceGroupCliOperation : public CliOperation virtual bool Execute( std::string& returnErrorMessage ) const final; private: - void PrintStartBanner( CarbonResources::CreateResourceGroupFromDirectoryParams& createResourceGroupFromDirectoryParams, CarbonResources::ResourceGroupExportToFileParams& ResourceGroupExportToFileParams ) const; + void PrintStartBanner( + CarbonResources::CreateResourceGroupFromDirectoryParams& createResourceGroupFromDirectoryParams, + CarbonResources::ResourceGroupExportToFileParams& ResourceGroupExportToFileParams ) const; - bool CreateResourceGroup( CarbonResources::CreateResourceGroupFromDirectoryParams& createResourceGroupFromDirectoryParams, CarbonResources::ResourceGroupExportToFileParams& ResourceGroupExportToFileParams ) const; + bool CreateResourceGroup( + CarbonResources::CreateResourceGroupFromDirectoryParams& createResourceGroupFromDirectoryParams, + CarbonResources::ResourceGroupExportToFileParams& ResourceGroupExportToFileParams ) const; private: std::string m_createResourceGroupPathArgumentId; @@ -30,14 +34,16 @@ class CreateResourceGroupCliOperation : public CliOperation std::string m_createResourceGroupDocumentVersionArgumentId; std::string m_createResourceGroupResourcePrefixArgumentId; - - std::string m_createResourceGroupSkipCompressionCalculationId; - std::string m_createResourceGroupExportResourcesId; + std::string m_createResourceGroupSkipCompressionCalculationId; - std::string m_createResourceGroupExportResourcesDestinationTypeId; - - std::string m_createResourceGroupExportResourcesDestinationPathId; + std::string m_createResourceGroupExportResourcesId; + + std::string m_createResourceGroupExportResourcesDestinationTypeId; + + std::string m_createResourceGroupExportResourcesDestinationPathId; + + std::string m_createResourceGroupIniFilterFilesArgumentId; }; #endif // CreateResourceGroupCliOperation_H \ No newline at end of file From 5158d92622fe4170b4744211a591c6fabac66c39 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 26 Jan 2026 10:27:33 +0000 Subject: [PATCH 040/124] Make the CurrentWorkingDirectoryChanger helper class available for all tests. --- tests/src/ResourceFilterTest.h | 48 -------------------------------- tests/src/ResourcesTestFixture.h | 48 ++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/tests/src/ResourceFilterTest.h b/tests/src/ResourceFilterTest.h index 70c9005..66694a9 100644 --- a/tests/src/ResourceFilterTest.h +++ b/tests/src/ResourceFilterTest.h @@ -12,52 +12,4 @@ class ResourceFilterTest : public ResourcesTestFixture { }; -// RAII helper class to change the current working directory temporarily (within a scope) -class CurrentWorkingDirectoryChanger { -public: - // Constructor acquires the current path and changes it - explicit CurrentWorkingDirectoryChanger(const std::filesystem::path& new_path) : - original_path_(std::filesystem::current_path()) - { - // Acquire original path - try - { - std::cout << "CurrentWorkingDirectoryChanger - Original directory: " << original_path_.generic_string() << std::endl; - std::filesystem::current_path(new_path); // Change to new path - std::cout << "CurrentWorkingDirectoryChanger - Changed directory to: " << std::filesystem::current_path().generic_string() << std::endl; - } - catch (const std::filesystem::filesystem_error& e) - { - std::cerr << "CurrentWorkingDirectoryChanger - Error changing directory: " << e.what() << std::endl; - // Handle error, maybe throw an exception or set a flag - } - } - - // Destructor restores the original path - ~CurrentWorkingDirectoryChanger() - { - try - { - std::filesystem::current_path(original_path_); // Restore original path - std::cout << "CurrentWorkingDirectoryChanger - Restored directory to: " << std::filesystem::current_path().generic_string() << std::endl; - } - catch (const std::filesystem::filesystem_error& e) - { - std::cerr << "CurrentWorkingDirectoryChanger - Error restoring directory: " << e.what() << std::endl; - } - } - - // Disable copy and move operations to ensure single ownership and prevent issues - CurrentWorkingDirectoryChanger(const CurrentWorkingDirectoryChanger&) = delete; - - CurrentWorkingDirectoryChanger& operator=(const CurrentWorkingDirectoryChanger&) = delete; - - CurrentWorkingDirectoryChanger(CurrentWorkingDirectoryChanger&&) = delete; - - CurrentWorkingDirectoryChanger& operator=(CurrentWorkingDirectoryChanger&&) = delete; - -private: - std::filesystem::path original_path_; -}; - #endif // ResourceFilterTest_H \ No newline at end of file diff --git a/tests/src/ResourcesTestFixture.h b/tests/src/ResourcesTestFixture.h index 94fbf0e..e3d701f 100644 --- a/tests/src/ResourcesTestFixture.h +++ b/tests/src/ResourcesTestFixture.h @@ -25,4 +25,52 @@ struct ResourcesTestFixture : public ::testing::Test bool DirectoryIsSubset( const std::filesystem::path& dir1, const std::filesystem::path& dir2 ); // Test that all files in dir1 exist in dir2, and the contents of the files in both directories are the same. }; +// RAII helper class to change the current working directory temporarily (within a scope) +class CurrentWorkingDirectoryChanger { +public: + // Constructor acquires the current path and changes it + explicit CurrentWorkingDirectoryChanger(const std::filesystem::path& new_path) : + original_path_(std::filesystem::current_path()) + { + // Acquire original path + try + { + std::cout << "CurrentWorkingDirectoryChanger - Original directory: " << original_path_.generic_string() << std::endl; + std::filesystem::current_path(new_path); // Change to new path + std::cout << "CurrentWorkingDirectoryChanger - Changed directory to: " << std::filesystem::current_path().generic_string() << std::endl; + } + catch (const std::filesystem::filesystem_error& e) + { + std::cerr << "CurrentWorkingDirectoryChanger - Error changing directory: " << e.what() << std::endl; + // Handle error, maybe throw an exception or set a flag + } + } + + // Destructor restores the original path + ~CurrentWorkingDirectoryChanger() + { + try + { + std::filesystem::current_path(original_path_); // Restore original path + std::cout << "CurrentWorkingDirectoryChanger - Restored directory to: " << std::filesystem::current_path().generic_string() << std::endl; + } + catch (const std::filesystem::filesystem_error& e) + { + std::cerr << "CurrentWorkingDirectoryChanger - Error restoring directory: " << e.what() << std::endl; + } + } + + // Disable copy and move operations to ensure single ownership and prevent issues + CurrentWorkingDirectoryChanger(const CurrentWorkingDirectoryChanger&) = delete; + + CurrentWorkingDirectoryChanger& operator=(const CurrentWorkingDirectoryChanger&) = delete; + + CurrentWorkingDirectoryChanger(CurrentWorkingDirectoryChanger&&) = delete; + + CurrentWorkingDirectoryChanger& operator=(CurrentWorkingDirectoryChanger&&) = delete; + +private: + std::filesystem::path original_path_; +}; + #endif // CarbonResourcesTestFixture_H \ No newline at end of file From 6d3c1fb67657df1f7111b4e94983368b05626c31 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 26 Jan 2026 10:38:11 +0000 Subject: [PATCH 041/124] Add resourceFilterIniFiles info to PrintStartBanner() --- cli/src/CreateResourceGroupCliOperation.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cli/src/CreateResourceGroupCliOperation.cpp b/cli/src/CreateResourceGroupCliOperation.cpp index 3239c30..5f560d0 100644 --- a/cli/src/CreateResourceGroupCliOperation.cpp +++ b/cli/src/CreateResourceGroupCliOperation.cpp @@ -152,6 +152,20 @@ void CreateResourceGroupCliOperation::PrintStartBanner( std::cout << "Export Resources: Off" << std::endl; } + if( createResourceGroupFromDirectoryParams.resourceFilterIniFiles.size() > 0 ) + { + std::cout << "Resource Filter INI File(s) used: " << std::endl; + + for( const auto& iniPath : createResourceGroupFromDirectoryParams.resourceFilterIniFiles ) + { + std::cout << " - " << iniPath.generic_string() << std::endl; + } + } + else + { + std::cout << "Resource Filter INI File(s) used: None" << std::endl; + } + std::cout << "----------------------------\n" << std::endl; } From cec67ec64d5ca51cd06ed0b52714de9153491ff6 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 26 Jan 2026 13:10:29 +0000 Subject: [PATCH 042/124] Make CLI parameter --filter-files singular --- cli/src/CreateResourceGroupCliOperation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/CreateResourceGroupCliOperation.cpp b/cli/src/CreateResourceGroupCliOperation.cpp index 5f560d0..0afd6b6 100644 --- a/cli/src/CreateResourceGroupCliOperation.cpp +++ b/cli/src/CreateResourceGroupCliOperation.cpp @@ -16,7 +16,7 @@ CreateResourceGroupCliOperation::CreateResourceGroupCliOperation() : m_createResourceGroupExportResourcesId( "--export-resources" ), m_createResourceGroupExportResourcesDestinationTypeId( "--export-resources-destination-type" ), m_createResourceGroupExportResourcesDestinationPathId( "--export-resources-destination-path" ), - m_createResourceGroupIniFilterFilesArgumentId( "--filter-files" ) + m_createResourceGroupIniFilterFilesArgumentId( "--filter-file" ) { AddRequiredPositionalArgument( m_createResourceGroupPathArgumentId, "Base directory to create resource group from." ); From 43bdd0fa6c2f6b8a1a57c968c79d74770becfec0 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 26 Jan 2026 13:21:12 +0000 Subject: [PATCH 043/124] Update helpString for --filter-file --- cli/src/CreateResourceGroupCliOperation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/CreateResourceGroupCliOperation.cpp b/cli/src/CreateResourceGroupCliOperation.cpp index 0afd6b6..6bc907f 100644 --- a/cli/src/CreateResourceGroupCliOperation.cpp +++ b/cli/src/CreateResourceGroupCliOperation.cpp @@ -42,7 +42,7 @@ CreateResourceGroupCliOperation::CreateResourceGroupCliOperation() : AddArgument( m_createResourceGroupExportResourcesDestinationPathId, "Represents the base path where the exported resources will be saved. Requires --export-resources", false, false, defaultImportParams.exportResourcesDestinationSettings.basePath.string() ); - AddArgument( m_createResourceGroupIniFilterFilesArgumentId, "Path to INI file(s) for resource filtering. Can be specified multiple times.", false, true, "" ); + AddArgument( m_createResourceGroupIniFilterFilesArgumentId, "Path to INI file(s) for resource filtering.", false, true, "" ); } bool CreateResourceGroupCliOperation::Execute( std::string& returnErrorMessage ) const From bef0e535f3cbda277cf4bc4cf64c2dc998866729 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 26 Jan 2026 15:49:42 +0000 Subject: [PATCH 044/124] Create CLI test for filtering using validSimpleExample1.ini --- .gitignore | 3 + tests/src/CliTestFixture.cpp | 13 +++- tests/src/CliTestFixture.h | 2 + tests/src/ResourcesCliTest.cpp | 59 +++++++++++++++++++ ...ingFilter_validSimpleExample1_Windows.yaml | 20 +++++++ 5 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_Windows.yaml diff --git a/.gitignore b/.gitignore index 25185dc..ebe1990 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,6 @@ CMakeUserPresets.json ### VCPKG ### # Temporary measure which fixes build agent issue vcpkg_registry_cache/ + +### Ignored Test Run Files ### +/tests/testData/IgnoredTestOutputFiles/ diff --git a/tests/src/CliTestFixture.cpp b/tests/src/CliTestFixture.cpp index 95e6ec2..9034863 100644 --- a/tests/src/CliTestFixture.cpp +++ b/tests/src/CliTestFixture.cpp @@ -21,4 +21,15 @@ int CliTestFixture::RunCli( std::vector& arguments, std::string& ou output = processOutput; return exit_status; -} \ No newline at end of file +} + +void CliTestFixture::CleanupTestOutputFiles( const std::vector& filesToRemove ) +{ + for( const auto& filePath : filesToRemove ) + { + if( std::filesystem::exists( filePath ) ) + { + std::filesystem::remove( filePath ); + } + } +} diff --git a/tests/src/CliTestFixture.h b/tests/src/CliTestFixture.h index be936e7..541ea3f 100644 --- a/tests/src/CliTestFixture.h +++ b/tests/src/CliTestFixture.h @@ -13,6 +13,8 @@ struct CliTestFixture : public ResourcesTestFixture { int RunCli( std::vector& arguments, std::string& output ); + + void CleanupTestOutputFiles( const std::vector& filesToRemove ); }; #endif // CliTestFixture_H \ No newline at end of file diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index bd8d133..a67820a 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -308,6 +308,65 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryOldDocumentFormatWithP EXPECT_TRUE( FilesMatch( goldFile, outputFile ) ); } +//--------------------------------------- + +TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) +{ + // Alter the current working directory for the duration of this test + CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); + + // Setup test parameters + std::string output; + std::vector arguments; + std::filesystem::path inputDirectoryPath = "."; // The base testData directory + std::filesystem::path outputFilePath = "./IgnoredTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1.yaml"; + std::vector filterIniFilePaths = { + "./ExampleIniFiles/validSimpleExample1.ini" + }; + + // Ensure any previous test output files are removed + CleanupTestOutputFiles({ outputFilePath } ); + + arguments.push_back( "create-group" ); + + arguments.push_back( std::filesystem::absolute(inputDirectoryPath).generic_string() ); + + arguments.push_back( "--verbosity-level" ); + arguments.push_back( "3" ); + + arguments.push_back( "--filter-file" ); + for(auto filterFilePath : filterIniFilePaths ) + { + arguments.push_back( std::filesystem::absolute(filterFilePath).generic_string() ); + } + + arguments.push_back( "--output-file" ); + arguments.push_back( outputFilePath.generic_string() ); + + int res = RunCli( arguments, output ); + std::cout << "Test RunCli output: " << std::endl; + std::cout << "----------------------------------" << std::endl; + std::cout << output << std::endl; + std::cout << "----------------------------------" << std::endl; + + ASSERT_EQ( res, 0 ) << "CLI operation failed, output: " << output; + + // Check expected outcome +#if _WIN64 + std::filesystem::path goldFile = std::filesystem::absolute("./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_Windows.yaml"); +#elif __APPLE__ + std::filesystem::path goldFile = std::filesystem::absolute("./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_macOS.yaml"); +#else +#error Unsupported platform +#endif + EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ); + + // Cleanup test output files + CleanupTestOutputFiles({ outputFilePath } ); +} + +//--------------------------------------- + TEST_F( ResourcesCliTest, CreateBundle ) { std::string output; diff --git a/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_Windows.yaml b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_Windows.yaml new file mode 100644 index 0000000..75c0d30 --- /dev/null +++ b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_Windows.yaml @@ -0,0 +1,20 @@ +Version: 0.1.0 +Type: ResourceGroup +NumberOfResources: 2 +TotalResourcesSizeCompressed: 8235 +TotalResourcesSizeUnCompressed: 41906 +Resources: + - RelativePath: resourcesOnBranch/introMovie.txt + Type: Resource + Location: 1c/1c6368ffb440041a_e9fadf6f2d386a0a0786bc863f20fa34 + Checksum: e9fadf6f2d386a0a0786bc863f20fa34 + UncompressedSize: 9091 + CompressedSize: 3232 + BinaryOperation: 33206 + - RelativePath: resourcesOnBranch/videoCardCategories.yaml + Type: Resource + Location: ff/ffe4c396c9c935d4_90d25dac9f4ed3233c5fda72bee3dfe6 + Checksum: 90d25dac9f4ed3233c5fda72bee3dfe6 + UncompressedSize: 32815 + CompressedSize: 5003 + BinaryOperation: 33206 \ No newline at end of file From 201dcff641674871b0f0e9383279357fe4c56730 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 26 Jan 2026 17:00:13 +0000 Subject: [PATCH 045/124] Create more CLI filter tests Added test for: - validComplexExample1 - combined validSimpleExample1 + validComplexExample1 --- tests/src/ResourcesCliTest.cpp | 111 +++++++++++++++++- ...ngFilter_validComplexExample1_Windows.yaml | 76 ++++++++++++ ...validSimpleAndComplexExample1_Windows.yaml | 90 ++++++++++++++ 3 files changed, 272 insertions(+), 5 deletions(-) create mode 100644 tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_Windows.yaml create mode 100644 tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_Windows.yaml diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index a67820a..9cdcd60 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -320,8 +320,109 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) std::vector arguments; std::filesystem::path inputDirectoryPath = "."; // The base testData directory std::filesystem::path outputFilePath = "./IgnoredTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1.yaml"; + std::filesystem::path filterIniFilePath = "./ExampleIniFiles/validSimpleExample1.ini"; + + // Ensure any previous test output files are removed + CleanupTestOutputFiles({ outputFilePath } ); + + arguments.push_back( "create-group" ); + + arguments.push_back( std::filesystem::absolute(inputDirectoryPath).generic_string() ); + + arguments.push_back( "--verbosity-level" ); + arguments.push_back( "3" ); + + arguments.push_back( "--filter-file" ); + arguments.push_back( std::filesystem::absolute(filterIniFilePath).generic_string() ); + + arguments.push_back( "--output-file" ); + arguments.push_back( outputFilePath.generic_string() ); + + int res = RunCli( arguments, output ); + std::cout << "Test RunCli output: " << std::endl; + std::cout << "----------------------------------" << std::endl; + std::cout << output << std::endl; + std::cout << "----------------------------------" << std::endl; + + ASSERT_EQ( res, 0 ) << "CLI operation failed, output: " << output; + + // Check expected outcome +#if _WIN64 + std::filesystem::path goldFile = std::filesystem::absolute("./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_Windows.yaml"); +#elif __APPLE__ + std::filesystem::path goldFile = std::filesystem::absolute("./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_macOS.yaml"); +#else +#error Unsupported platform +#endif + EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ); + + // Cleanup test output files + CleanupTestOutputFiles({ outputFilePath } ); +} + +TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) +{ + // Alter the current working directory for the duration of this test + CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); + + // Setup test parameters + std::string output; + std::vector arguments; + std::filesystem::path inputDirectoryPath = "."; // The base testData directory + std::filesystem::path outputFilePath = "./IgnoredTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1.yaml"; + std::filesystem::path filterIniFilePath = "./ExampleIniFiles/validComplexExample1.ini"; + + // Ensure any previous test output files are removed + CleanupTestOutputFiles({ outputFilePath } ); + + arguments.push_back( "create-group" ); + + arguments.push_back( std::filesystem::absolute(inputDirectoryPath).generic_string() ); + + arguments.push_back( "--verbosity-level" ); + arguments.push_back( "3" ); + + arguments.push_back( "--filter-file" ); + arguments.push_back( std::filesystem::absolute(filterIniFilePath).generic_string() ); + + arguments.push_back( "--output-file" ); + arguments.push_back( outputFilePath.generic_string() ); + + int res = RunCli( arguments, output ); + std::cout << "Test RunCli output: " << std::endl; + std::cout << "----------------------------------" << std::endl; + std::cout << output << std::endl; + std::cout << "----------------------------------" << std::endl; + + ASSERT_EQ( res, 0 ) << "CLI operation failed, output: " << output; + + // Check expected outcome +#if _WIN64 + std::filesystem::path goldFile = std::filesystem::absolute("./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_Windows.yaml"); +#elif __APPLE__ + std::filesystem::path goldFile = std::filesystem::absolute("./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_macOS.yaml"); +#else +#error Unsupported platform +#endif + EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ); + + // Cleanup test output files + CleanupTestOutputFiles({ outputFilePath } ); +} + +TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 ) +{ + // Alter the current working directory for the duration of this test + CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); + + // Setup test parameters + std::string output; + std::vector arguments; + std::filesystem::path inputDirectoryPath = "."; // The base testData directory + std::filesystem::path outputFilePath = "./IgnoredTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1.yaml"; std::vector filterIniFilePaths = { - "./ExampleIniFiles/validSimpleExample1.ini" + "./ExampleIniFiles/validSimpleExample1.ini", + "./ExampleIniFiles/validComplexExample1.ini" }; // Ensure any previous test output files are removed @@ -334,9 +435,9 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) arguments.push_back( "--verbosity-level" ); arguments.push_back( "3" ); - arguments.push_back( "--filter-file" ); for(auto filterFilePath : filterIniFilePaths ) { + arguments.push_back( "--filter-file" ); arguments.push_back( std::filesystem::absolute(filterFilePath).generic_string() ); } @@ -353,15 +454,15 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) // Check expected outcome #if _WIN64 - std::filesystem::path goldFile = std::filesystem::absolute("./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_Windows.yaml"); + std::filesystem::path goldFile = std::filesystem::absolute("./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_Windows.yaml"); #elif __APPLE__ - std::filesystem::path goldFile = std::filesystem::absolute("./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_macOS.yaml"); + std::filesystem::path goldFile = std::filesystem::absolute("./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_macOS.yaml"); #else #error Unsupported platform #endif EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ); - // Cleanup test output files + // Cleanup test output files CleanupTestOutputFiles({ outputFilePath } ); } diff --git a/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_Windows.yaml b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_Windows.yaml new file mode 100644 index 0000000..6e049fe --- /dev/null +++ b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_Windows.yaml @@ -0,0 +1,76 @@ +Version: 0.1.0 +Type: ResourceGroup +NumberOfResources: 10 +TotalResourcesSizeCompressed: 24573 +TotalResourcesSizeUnCompressed: 107508 +Resources: + - RelativePath: PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt + Type: Resource + Location: dc/dca8056adced9237_0fb2bed9d164ad014bcb060b95df7ba1 + Checksum: 0fb2bed9d164ad014bcb060b95df7ba1 + UncompressedSize: 9425 + CompressedSize: 3409 + BinaryOperation: 33206 + - RelativePath: PatchWithInputChunk/NextBuildResources/testResource2.txt + Type: Resource + Location: 82/8264e4640cf94b94_271c8036e4eae5515053e924a0a39e0f + Checksum: 271c8036e4eae5515053e924a0a39e0f + UncompressedSize: 29 + CompressedSize: 47 + BinaryOperation: 33206 + - RelativePath: PatchWithInputChunk/NextBuildResources/videoCardCategories.yaml + Type: Resource + Location: 02/02b2b2627ca28b62_90d25dac9f4ed3233c5fda72bee3dfe6 + Checksum: 90d25dac9f4ed3233c5fda72bee3dfe6 + UncompressedSize: 32815 + CompressedSize: 5003 + BinaryOperation: 33206 + - RelativePath: PatchWithInputChunk/PatchResourceGroup_previousBuild_latestBuild.yaml + Type: Resource + Location: 44/446d5f683db0d467_b7431ad6d859bfd67a4c3fae337b0b6e + Checksum: b7431ad6d859bfd67a4c3fae337b0b6e + UncompressedSize: 3902 + CompressedSize: 765 + BinaryOperation: 33206 + - RelativePath: PatchWithInputChunk/PreviousBuildResources/introMovie.txt + Type: Resource + Location: fd/fd2a47b3d5faa494_e9fadf6f2d386a0a0786bc863f20fa34 + Checksum: e9fadf6f2d386a0a0786bc863f20fa34 + UncompressedSize: 9091 + CompressedSize: 3232 + BinaryOperation: 33206 + - RelativePath: PatchWithInputChunk/PreviousBuildResources/introMoviePrefixed.txt + Type: Resource + Location: 5d/5d2aadfa1344ac63_5e631fd37d3350e30095d1251b178f2c + Checksum: 5e631fd37d3350e30095d1251b178f2c + UncompressedSize: 9117 + CompressedSize: 3259 + BinaryOperation: 33206 + - RelativePath: PatchWithInputChunk/PreviousBuildResources/introMovieSomewhatChanged.txt + Type: Resource + Location: 60/6054e5e0cf24763e_e9fadf6f2d386a0a0786bc863f20fa34 + Checksum: e9fadf6f2d386a0a0786bc863f20fa34 + UncompressedSize: 9091 + CompressedSize: 3232 + BinaryOperation: 33206 + - RelativePath: PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml + Type: Resource + Location: fb/fbc1cb8895f6c396_90d25dac9f4ed3233c5fda72bee3dfe6 + Checksum: 90d25dac9f4ed3233c5fda72bee3dfe6 + UncompressedSize: 32815 + CompressedSize: 5003 + BinaryOperation: 33206 + - RelativePath: PatchWithInputChunk/resFileIndexShort_build_next.txt + Type: Resource + Location: 1f/1f23be4b8bf6d2ae_4d398902b75611f7ae19903e6b461514 + Checksum: 4d398902b75611f7ae19903e6b461514 + UncompressedSize: 612 + CompressedSize: 324 + BinaryOperation: 33206 + - RelativePath: PatchWithInputChunk/resFileIndexShort_build_previous.txt + Type: Resource + Location: 32/326b0449af10b716_849821e0c98e37de4edc5f7156cf5887 + Checksum: 849821e0c98e37de4edc5f7156cf5887 + UncompressedSize: 611 + CompressedSize: 299 + BinaryOperation: 33206 \ No newline at end of file diff --git a/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_Windows.yaml b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_Windows.yaml new file mode 100644 index 0000000..a6632d2 --- /dev/null +++ b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_Windows.yaml @@ -0,0 +1,90 @@ +Version: 0.1.0 +Type: ResourceGroup +NumberOfResources: 12 +TotalResourcesSizeCompressed: 32808 +TotalResourcesSizeUnCompressed: 149414 +Resources: + - RelativePath: PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt + Type: Resource + Location: dc/dca8056adced9237_0fb2bed9d164ad014bcb060b95df7ba1 + Checksum: 0fb2bed9d164ad014bcb060b95df7ba1 + UncompressedSize: 9425 + CompressedSize: 3409 + BinaryOperation: 33206 + - RelativePath: PatchWithInputChunk/NextBuildResources/testResource2.txt + Type: Resource + Location: 82/8264e4640cf94b94_271c8036e4eae5515053e924a0a39e0f + Checksum: 271c8036e4eae5515053e924a0a39e0f + UncompressedSize: 29 + CompressedSize: 47 + BinaryOperation: 33206 + - RelativePath: PatchWithInputChunk/NextBuildResources/videoCardCategories.yaml + Type: Resource + Location: 02/02b2b2627ca28b62_90d25dac9f4ed3233c5fda72bee3dfe6 + Checksum: 90d25dac9f4ed3233c5fda72bee3dfe6 + UncompressedSize: 32815 + CompressedSize: 5003 + BinaryOperation: 33206 + - RelativePath: PatchWithInputChunk/PatchResourceGroup_previousBuild_latestBuild.yaml + Type: Resource + Location: 44/446d5f683db0d467_b7431ad6d859bfd67a4c3fae337b0b6e + Checksum: b7431ad6d859bfd67a4c3fae337b0b6e + UncompressedSize: 3902 + CompressedSize: 765 + BinaryOperation: 33206 + - RelativePath: PatchWithInputChunk/PreviousBuildResources/introMovie.txt + Type: Resource + Location: fd/fd2a47b3d5faa494_e9fadf6f2d386a0a0786bc863f20fa34 + Checksum: e9fadf6f2d386a0a0786bc863f20fa34 + UncompressedSize: 9091 + CompressedSize: 3232 + BinaryOperation: 33206 + - RelativePath: PatchWithInputChunk/PreviousBuildResources/introMoviePrefixed.txt + Type: Resource + Location: 5d/5d2aadfa1344ac63_5e631fd37d3350e30095d1251b178f2c + Checksum: 5e631fd37d3350e30095d1251b178f2c + UncompressedSize: 9117 + CompressedSize: 3259 + BinaryOperation: 33206 + - RelativePath: PatchWithInputChunk/PreviousBuildResources/introMovieSomewhatChanged.txt + Type: Resource + Location: 60/6054e5e0cf24763e_e9fadf6f2d386a0a0786bc863f20fa34 + Checksum: e9fadf6f2d386a0a0786bc863f20fa34 + UncompressedSize: 9091 + CompressedSize: 3232 + BinaryOperation: 33206 + - RelativePath: PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml + Type: Resource + Location: fb/fbc1cb8895f6c396_90d25dac9f4ed3233c5fda72bee3dfe6 + Checksum: 90d25dac9f4ed3233c5fda72bee3dfe6 + UncompressedSize: 32815 + CompressedSize: 5003 + BinaryOperation: 33206 + - RelativePath: PatchWithInputChunk/resFileIndexShort_build_next.txt + Type: Resource + Location: 1f/1f23be4b8bf6d2ae_4d398902b75611f7ae19903e6b461514 + Checksum: 4d398902b75611f7ae19903e6b461514 + UncompressedSize: 612 + CompressedSize: 324 + BinaryOperation: 33206 + - RelativePath: PatchWithInputChunk/resFileIndexShort_build_previous.txt + Type: Resource + Location: 32/326b0449af10b716_849821e0c98e37de4edc5f7156cf5887 + Checksum: 849821e0c98e37de4edc5f7156cf5887 + UncompressedSize: 611 + CompressedSize: 299 + BinaryOperation: 33206 + - RelativePath: resourcesOnBranch/introMovie.txt + Type: Resource + Location: 1c/1c6368ffb440041a_e9fadf6f2d386a0a0786bc863f20fa34 + Checksum: e9fadf6f2d386a0a0786bc863f20fa34 + UncompressedSize: 9091 + CompressedSize: 3232 + BinaryOperation: 33206 + - RelativePath: resourcesOnBranch/videoCardCategories.yaml + Type: Resource + Location: ff/ffe4c396c9c935d4_90d25dac9f4ed3233c5fda72bee3dfe6 + Checksum: 90d25dac9f4ed3233c5fda72bee3dfe6 + UncompressedSize: 32815 + CompressedSize: 5003 + BinaryOperation: 33206 \ No newline at end of file From d80b9e60587bbe1d63efab6a22db296a4c4608c9 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 26 Jan 2026 17:17:02 +0000 Subject: [PATCH 046/124] Fix formatting of FilterResources CLI tests --- tests/src/ResourcesCliTest.cpp | 44 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index 9cdcd60..3946658 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -318,22 +318,22 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) // Setup test parameters std::string output; std::vector arguments; - std::filesystem::path inputDirectoryPath = "."; // The base testData directory + std::filesystem::path inputDirectoryPath = "."; // The base testData directory std::filesystem::path outputFilePath = "./IgnoredTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1.yaml"; std::filesystem::path filterIniFilePath = "./ExampleIniFiles/validSimpleExample1.ini"; // Ensure any previous test output files are removed - CleanupTestOutputFiles({ outputFilePath } ); + CleanupTestOutputFiles( { outputFilePath } ); arguments.push_back( "create-group" ); - arguments.push_back( std::filesystem::absolute(inputDirectoryPath).generic_string() ); + arguments.push_back( std::filesystem::absolute( inputDirectoryPath ).generic_string() ); arguments.push_back( "--verbosity-level" ); arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); - arguments.push_back( std::filesystem::absolute(filterIniFilePath).generic_string() ); + arguments.push_back( std::filesystem::absolute( filterIniFilePath ).generic_string() ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.generic_string() ); @@ -348,16 +348,16 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) // Check expected outcome #if _WIN64 - std::filesystem::path goldFile = std::filesystem::absolute("./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_Windows.yaml"); + std::filesystem::path goldFile = std::filesystem::absolute( "./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_Windows.yaml" ); #elif __APPLE__ - std::filesystem::path goldFile = std::filesystem::absolute("./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_macOS.yaml"); + std::filesystem::path goldFile = std::filesystem::absolute( "./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_macOS.yaml" ); #else #error Unsupported platform #endif EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ); // Cleanup test output files - CleanupTestOutputFiles({ outputFilePath } ); + CleanupTestOutputFiles( { outputFilePath } ); } TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) @@ -368,22 +368,22 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) // Setup test parameters std::string output; std::vector arguments; - std::filesystem::path inputDirectoryPath = "."; // The base testData directory + std::filesystem::path inputDirectoryPath = "."; // The base testData directory std::filesystem::path outputFilePath = "./IgnoredTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1.yaml"; std::filesystem::path filterIniFilePath = "./ExampleIniFiles/validComplexExample1.ini"; // Ensure any previous test output files are removed - CleanupTestOutputFiles({ outputFilePath } ); + CleanupTestOutputFiles( { outputFilePath } ); arguments.push_back( "create-group" ); - arguments.push_back( std::filesystem::absolute(inputDirectoryPath).generic_string() ); + arguments.push_back( std::filesystem::absolute( inputDirectoryPath ).generic_string() ); arguments.push_back( "--verbosity-level" ); arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); - arguments.push_back( std::filesystem::absolute(filterIniFilePath).generic_string() ); + arguments.push_back( std::filesystem::absolute( filterIniFilePath ).generic_string() ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.generic_string() ); @@ -398,16 +398,16 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) // Check expected outcome #if _WIN64 - std::filesystem::path goldFile = std::filesystem::absolute("./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_Windows.yaml"); + std::filesystem::path goldFile = std::filesystem::absolute( "./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_Windows.yaml" ); #elif __APPLE__ - std::filesystem::path goldFile = std::filesystem::absolute("./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_macOS.yaml"); + std::filesystem::path goldFile = std::filesystem::absolute( "./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_macOS.yaml" ); #else #error Unsupported platform #endif EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ); // Cleanup test output files - CleanupTestOutputFiles({ outputFilePath } ); + CleanupTestOutputFiles( { outputFilePath } ); } TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 ) @@ -418,7 +418,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 // Setup test parameters std::string output; std::vector arguments; - std::filesystem::path inputDirectoryPath = "."; // The base testData directory + std::filesystem::path inputDirectoryPath = "."; // The base testData directory std::filesystem::path outputFilePath = "./IgnoredTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1.yaml"; std::vector filterIniFilePaths = { "./ExampleIniFiles/validSimpleExample1.ini", @@ -426,19 +426,19 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 }; // Ensure any previous test output files are removed - CleanupTestOutputFiles({ outputFilePath } ); + CleanupTestOutputFiles( { outputFilePath } ); arguments.push_back( "create-group" ); - arguments.push_back( std::filesystem::absolute(inputDirectoryPath).generic_string() ); + arguments.push_back( std::filesystem::absolute( inputDirectoryPath ).generic_string() ); arguments.push_back( "--verbosity-level" ); arguments.push_back( "3" ); - for(auto filterFilePath : filterIniFilePaths ) + for( auto filterFilePath : filterIniFilePaths ) { arguments.push_back( "--filter-file" ); - arguments.push_back( std::filesystem::absolute(filterFilePath).generic_string() ); + arguments.push_back( std::filesystem::absolute( filterFilePath ).generic_string() ); } arguments.push_back( "--output-file" ); @@ -454,16 +454,16 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 // Check expected outcome #if _WIN64 - std::filesystem::path goldFile = std::filesystem::absolute("./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_Windows.yaml"); + std::filesystem::path goldFile = std::filesystem::absolute( "./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_Windows.yaml" ); #elif __APPLE__ - std::filesystem::path goldFile = std::filesystem::absolute("./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_macOS.yaml"); + std::filesystem::path goldFile = std::filesystem::absolute( "./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_macOS.yaml" ); #else #error Unsupported platform #endif EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ); // Cleanup test output files - CleanupTestOutputFiles({ outputFilePath } ); + CleanupTestOutputFiles( { outputFilePath } ); } //--------------------------------------- From 56dbfb04e4ccfa683efac4e6de635cce69b14b0e Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 26 Jan 2026 17:26:08 +0000 Subject: [PATCH 047/124] Remove unnecessary comments in class CurrentWorkingDirectoryChanger --- tests/src/ResourcesTestFixture.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/src/ResourcesTestFixture.h b/tests/src/ResourcesTestFixture.h index e3d701f..f8b5987 100644 --- a/tests/src/ResourcesTestFixture.h +++ b/tests/src/ResourcesTestFixture.h @@ -32,7 +32,6 @@ class CurrentWorkingDirectoryChanger { explicit CurrentWorkingDirectoryChanger(const std::filesystem::path& new_path) : original_path_(std::filesystem::current_path()) { - // Acquire original path try { std::cout << "CurrentWorkingDirectoryChanger - Original directory: " << original_path_.generic_string() << std::endl; @@ -42,7 +41,6 @@ class CurrentWorkingDirectoryChanger { catch (const std::filesystem::filesystem_error& e) { std::cerr << "CurrentWorkingDirectoryChanger - Error changing directory: " << e.what() << std::endl; - // Handle error, maybe throw an exception or set a flag } } @@ -60,7 +58,7 @@ class CurrentWorkingDirectoryChanger { } } - // Disable copy and move operations to ensure single ownership and prevent issues + // Disable copy and move operations CurrentWorkingDirectoryChanger(const CurrentWorkingDirectoryChanger&) = delete; CurrentWorkingDirectoryChanger& operator=(const CurrentWorkingDirectoryChanger&) = delete; From ae101d96bf69915c48ad71b6ec9b6f80b4740bfb Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 30 Jan 2026 17:07:08 +0000 Subject: [PATCH 048/124] Update include statements - mostly --- tools/src/FilterPrefixMapEntry.cpp | 1 + tools/src/FilterPrefixmap.cpp | 4 ++-- tools/src/FilterResourceFilter.cpp | 1 + tools/src/FilterResourcePathFileEntry.cpp | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/src/FilterPrefixMapEntry.cpp b/tools/src/FilterPrefixMapEntry.cpp index 2334283..351f8a2 100644 --- a/tools/src/FilterPrefixMapEntry.cpp +++ b/tools/src/FilterPrefixMapEntry.cpp @@ -1,5 +1,6 @@ // Copyright © 2025 CCP ehf. +#include #include namespace ResourceTools diff --git a/tools/src/FilterPrefixmap.cpp b/tools/src/FilterPrefixmap.cpp index 5b5aa68..90b2735 100644 --- a/tools/src/FilterPrefixmap.cpp +++ b/tools/src/FilterPrefixmap.cpp @@ -1,9 +1,9 @@ // Copyright © 2025 CCP ehf. -#include -#include #include #include +#include +#include namespace ResourceTools { diff --git a/tools/src/FilterResourceFilter.cpp b/tools/src/FilterResourceFilter.cpp index d41747d..0afe944 100644 --- a/tools/src/FilterResourceFilter.cpp +++ b/tools/src/FilterResourceFilter.cpp @@ -2,6 +2,7 @@ #include #include +#include #include namespace ResourceTools diff --git a/tools/src/FilterResourcePathFileEntry.cpp b/tools/src/FilterResourcePathFileEntry.cpp index d4d0ecf..d82c4c4 100644 --- a/tools/src/FilterResourcePathFileEntry.cpp +++ b/tools/src/FilterResourcePathFileEntry.cpp @@ -1,6 +1,7 @@ // Copyright © 2025 CCP ehf. #include +#include #include namespace ResourceTools From b19dba2ba7e3090363322363d3383f9774b39a75 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Sat, 31 Jan 2026 15:42:53 +0000 Subject: [PATCH 049/124] Fix wrong case of #include - It worked (wrong) on Windows - Failed on macOS --- tools/include/FilterNamedSection.h | 2 +- tools/src/FilterPrefixmap.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/include/FilterNamedSection.h b/tools/include/FilterNamedSection.h index 9702634..74b984b 100644 --- a/tools/include/FilterNamedSection.h +++ b/tools/include/FilterNamedSection.h @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include diff --git a/tools/src/FilterPrefixmap.cpp b/tools/src/FilterPrefixmap.cpp index 90b2735..7a70b92 100644 --- a/tools/src/FilterPrefixmap.cpp +++ b/tools/src/FilterPrefixmap.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include namespace ResourceTools From 8a4b7122fdb2e3d29a99497724f17c753e6e7c70 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Sat, 31 Jan 2026 16:02:48 +0000 Subject: [PATCH 050/124] Remove unused m_parentPrefixMap member in NamedSection - Caused a warning as error on macOS - Wasn't being used so safe to remove. --- tools/include/FilterNamedSection.h | 2 -- tools/src/FilterNamedSection.cpp | 1 - 2 files changed, 3 deletions(-) diff --git a/tools/include/FilterNamedSection.h b/tools/include/FilterNamedSection.h index 74b984b..41f51e1 100644 --- a/tools/include/FilterNamedSection.h +++ b/tools/include/FilterNamedSection.h @@ -35,8 +35,6 @@ class FilterNamedSection private: std::string m_sectionName; - const FilterPrefixMap& m_parentPrefixMap; // The "parent" prefix map from the [DEFAULT] section - FilterResourceFilter m_filter; FilterResourcePathFile m_respaths; diff --git a/tools/src/FilterNamedSection.cpp b/tools/src/FilterNamedSection.cpp index aa20076..0d7ec67 100644 --- a/tools/src/FilterNamedSection.cpp +++ b/tools/src/FilterNamedSection.cpp @@ -12,7 +12,6 @@ FilterNamedSection::FilterNamedSection( std::string sectionName, const std::string& resfile, const FilterPrefixMap& parentPrefixMap ) : m_sectionName( std::move( sectionName ) ), - m_parentPrefixMap( parentPrefixMap ), m_filter( filter, true ), // isToplevelFilter = true m_respaths( respaths, parentPrefixMap, m_filter ), m_resfile( resfile.empty() ? std::nullopt : std::make_optional( resfile, parentPrefixMap, m_filter ) ) From 2cddd56038fc45783204521689f2436817084782 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 2 Feb 2026 11:24:11 +0000 Subject: [PATCH 051/124] Change Kotlin Github access token --- .teamcity/MacOS/Project.kt | 4 ++-- .teamcity/Windows/Project.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.teamcity/MacOS/Project.kt b/.teamcity/MacOS/Project.kt index 712e116..db04686 100644 --- a/.teamcity/MacOS/Project.kt +++ b/.teamcity/MacOS/Project.kt @@ -150,7 +150,7 @@ class CarbonBuildMacOS(buildName: String, configType: String, preset: String, ag vcsRootExtId = "${DslContext.settingsRootId.id}" provider = github { authType = token { - token = "%GITHUB_TEAMCITY_TOKEN%" + token = "%GITHUB_CARBON_PAT%" } filterAuthorRole = PullRequests.GitHubRoleFilter.MEMBER } @@ -159,7 +159,7 @@ class CarbonBuildMacOS(buildName: String, configType: String, preset: String, ag publisher = github { githubUrl = "https://api.github.com" authType = personalToken { - token = "%GITHUB_TEAMCITY_TOKEN%" + token = "%GITHUB_CARBON_PAT%" } } } diff --git a/.teamcity/Windows/Project.kt b/.teamcity/Windows/Project.kt index 5795f06..6433401 100644 --- a/.teamcity/Windows/Project.kt +++ b/.teamcity/Windows/Project.kt @@ -208,7 +208,7 @@ class CarbonBuildWindows(buildName: String, configType: String, preset: String) vcsRootExtId = "${DslContext.settingsRootId.id}" provider = github { authType = token { - token = "%GITHUB_TEAMCITY_TOKEN%" + token = "%GITHUB_CARBON_PAT%" } filterAuthorRole = PullRequests.GitHubRoleFilter.MEMBER } @@ -217,7 +217,7 @@ class CarbonBuildWindows(buildName: String, configType: String, preset: String) publisher = github { githubUrl = "https://api.github.com" authType = personalToken { - token = "%GITHUB_TEAMCITY_TOKEN%" + token = "%GITHUB_CARBON_PAT%" } } } From 8459427bc21cf6392357fb3cc93c4e439bac97de Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 2 Feb 2026 11:35:35 +0000 Subject: [PATCH 052/124] Move include statement to trigger TC build --- tools/src/ResourceFilter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/src/ResourceFilter.cpp b/tools/src/ResourceFilter.cpp index 691f503..a58d9ae 100644 --- a/tools/src/ResourceFilter.cpp +++ b/tools/src/ResourceFilter.cpp @@ -1,8 +1,8 @@ // Copyright © 2026 CCP ehf. -#include #include #include +#include namespace ResourceTools { From e26015306c02e023507d89880bd492a4b4f9e523 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 2 Feb 2026 11:47:50 +0000 Subject: [PATCH 053/124] Fix casing of #include (missed one from earlier) - Fails to build on macOS (incorrect casing, warning as errors) --- tests/src/ResourceFilterTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 1a2e64a..733667f 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include #include From a943d6a2473d398940f6ccef744de35406e2ca52 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 2 Feb 2026 13:24:18 +0000 Subject: [PATCH 054/124] Use generic_string() for all paths involving Filter operations - Change includes both tools and tests --- tests/src/ResourceFilterTest.cpp | 24 ++++++++++++------------ tools/src/FilterResourceFile.cpp | 8 ++++---- tools/src/ResourceFilter.cpp | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 733667f..0d8b82b 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -16,7 +16,7 @@ TEST_F( ResourceFilterTest, Example1IniParsing ) { // Use the test fixture's helper to get the absolute path const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/example1.ini" ); - INIReader reader( iniPath.string() ); + INIReader reader( iniPath.generic_string() ); ASSERT_EQ( reader.ParseError(), 0 ) << "Failed to parse example1.ini"; // There should only be 2 sections @@ -1582,7 +1582,7 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_example1_ini ) try { - ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + ResourceTools::FilterResourceFile resourceFile( iniPath.generic_string() ); const auto& iniFilePathMap = resourceFile.GetIniFileResolvedPathMap(); // Validate the paths: @@ -1619,12 +1619,12 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidMissingDefaultSection try { - ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + ResourceTools::FilterResourceFile resourceFile( iniPath.generic_string() ); FAIL() << "Expected std::invalid_argument when loading ini file missing [DEFAULT] section"; } catch( const std::invalid_argument& e ) { - std::string expectedError = "Missing [DEFAULT] section in INI file: " + iniPath.string(); + std::string expectedError = "Missing [DEFAULT] section in INI file: " + iniPath.generic_string(); EXPECT_STREQ( e.what(), expectedError.c_str() ); } catch( ... ) @@ -1639,12 +1639,12 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidMissingNamedSection_i try { - ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + ResourceTools::FilterResourceFile resourceFile( iniPath.generic_string() ); FAIL() << "Expected std::invalid_argument when loading ini file missing [NamedSection] section"; } catch( const std::invalid_argument& e ) { - std::string expectedError = "No namedSections defined in INI file: " + iniPath.string(); + std::string expectedError = "No namedSections defined in INI file: " + iniPath.generic_string(); EXPECT_STREQ( e.what(), expectedError.c_str() ); } catch( ... ) @@ -1659,12 +1659,12 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_iniFileNotFound ) try { - ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + ResourceTools::FilterResourceFile resourceFile( iniPath.generic_string() ); FAIL() << "Expected std::runtime_error when loading non-existent ini file"; } catch( const std::runtime_error& e ) { - std::string expectedError = "Failed to parse INI file: " + iniPath.string() + " - unable to open file"; + std::string expectedError = "Failed to parse INI file: " + iniPath.generic_string() + " - unable to open file"; EXPECT_STREQ( e.what(), expectedError.c_str() ); } catch( ... ) @@ -1678,7 +1678,7 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidPrefixmap_ini ) const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidPrefixmap.ini" ); try { - ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + ResourceTools::FilterResourceFile resourceFile( iniPath.generic_string() ); FAIL() << "Expected std::invalid_argument when loading ini file with invalid prefixmap"; } catch( const std::invalid_argument& e ) @@ -1697,7 +1697,7 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidSectionFilter_ini ) const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidSectionFilter.ini" ); try { - ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + ResourceTools::FilterResourceFile resourceFile( iniPath.generic_string() ); FAIL() << "Expected std::invalid_argument when loading ini file with invalid section filter"; } catch( const std::invalid_argument& e ) @@ -1716,7 +1716,7 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidInlineFilter_ini ) const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidInlineFilter.ini" ); try { - ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + ResourceTools::FilterResourceFile resourceFile( iniPath.generic_string() ); FAIL() << "Expected std::invalid_argument when loading ini file with invalid inline filter"; } catch( const std::invalid_argument& e ) @@ -1735,7 +1735,7 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidPrefixMismatch_ini ) const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidPrefixMismatch.ini" ); try { - ResourceTools::FilterResourceFile resourceFile( iniPath.string() ); + ResourceTools::FilterResourceFile resourceFile( iniPath.generic_string() ); FAIL() << "Expected std::invalid_argument when loading ini file with prefix mismatch"; } catch( const std::invalid_argument& e ) diff --git a/tools/src/FilterResourceFile.cpp b/tools/src/FilterResourceFile.cpp index d657e78..f82bb56 100644 --- a/tools/src/FilterResourceFile.cpp +++ b/tools/src/FilterResourceFile.cpp @@ -48,16 +48,16 @@ const std::map& FilterResourceFile::GetIniFil void FilterResourceFile::ParseIniFile() { // Open, read and parse the resource INI file. - INIReader reader( m_iniFilePath.string() ); + INIReader reader( m_iniFilePath.generic_string() ); if( reader.ParseError() != 0 ) { - throw std::runtime_error( "Failed to parse INI file: " + m_iniFilePath.string() + " - " + reader.ParseErrorMessage() ); + throw std::runtime_error( "Failed to parse INI file: " + m_iniFilePath.generic_string() + " - " + reader.ParseErrorMessage() ); } // Parse the [DEFAULT] section if( !reader.HasSection( "DEFAULT" ) ) { - throw std::invalid_argument( "Missing [DEFAULT] section in INI file: " + m_iniFilePath.string() ); + throw std::invalid_argument( "Missing [DEFAULT] section in INI file: " + m_iniFilePath.generic_string() ); } m_defaultSection = FilterDefaultSection( reader.Get( "DEFAULT", "prefixmap", "" ) ); @@ -67,7 +67,7 @@ void FilterResourceFile::ParseIniFile() if( allSections.size() <= 1 ) { // No namedSections defined - throw std::invalid_argument( "No namedSections defined in INI file: " + m_iniFilePath.string() ); + throw std::invalid_argument( "No namedSections defined in INI file: " + m_iniFilePath.generic_string() ); } // Parse all other named sections diff --git a/tools/src/ResourceFilter.cpp b/tools/src/ResourceFilter.cpp index a58d9ae..97f8621 100644 --- a/tools/src/ResourceFilter.cpp +++ b/tools/src/ResourceFilter.cpp @@ -31,7 +31,7 @@ void ResourceFilter::Initialize( const std::vector& iniFi catch( const std::exception& e ) { // Optionally log or handle error - std::string errorMsg = "Unable to create ResourceFilter for: " + path.string() + " - because of: " + e.what(); + std::string errorMsg = "Unable to create ResourceFilter for: " + path.generic_string() + " - because of: " + e.what(); throw std::runtime_error( errorMsg ); } } From d8ee50616825723d0bbac4cad5d33b12ac2fa3ad Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 2 Feb 2026 14:19:48 +0000 Subject: [PATCH 055/124] Debug info - ResourceFilter_Load_validSimpleExample1_ini_usingRelativePaths - Temporarily adding debug information to test in order to see why it is failing for macOS in TeamCity --- tests/src/ResourceFilterTest.cpp | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 0d8b82b..e85f09c 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -1872,8 +1872,43 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load_validSimpleExample1_ini_usingRel }; ASSERT_EQ( resourceFilter.HasFilters(), true ); + // TODO: Remove debug output once confirmed working on macOS (TeamCity) + //-- BEGIN - Debug resourceFilter contents vs expected values + std::cout << "------ Debug info (begin) -----" << std::endl; + std::cout << "ResourceFilter FullResolvedPathMap contents:" << std::endl; + for ( const auto& [fullPath, filter] : resourceFilter.GetFullResolvedPathMap() ) + { + std::cout << "ResourceFilter Path: " << std::endl; + std::cout << " - path (relative string): " << fullPath << std::endl; + std::cout << " - path (relative generic_string): " << std::filesystem::path(fullPath).generic_string() << std::endl; + std::cout << " - path (absolute generic_string): " << std::filesystem::absolute(std::filesystem::path(fullPath)).generic_string() << std::endl; + std::cout << " Includes: "; + for( const auto& include : filter.GetIncludeFilter() ) + { + std::cout << include << " "; + } + std::cout << std::endl; + std::cout << " Excludes: "; + for( const auto& exclude : filter.GetExcludeFilter() ) + { + std::cout << exclude << " "; + } + std::cout << std::endl; + } + std::cout << "- - - - - - - - -" << std::endl; + std::cout << "Expected valid resolved relative paths:" << std::endl; + for( const auto& resolvedRelativePath : validResolvedRelativePaths ) + { + std::cout << " - path (relative string): " << resolvedRelativePath.string() << std::endl; + std::cout << " - path (relative generic_string): " << resolvedRelativePath.generic_string() << std::endl; + std::cout << " - path (absolute generic_string): " << std::filesystem::absolute(resolvedRelativePath).generic_string() << std::endl << std::endl; + } + std::cout << "------ Debug info (end) -----" << std::endl; + //-- END - Debug resourceFilter contents vs expected values for( const auto& resolvedRelativePath : validResolvedRelativePaths ) { + // TODO: Remove debug output once confirmed working on macOS (TeamCity) + std::cout << "About to call resourceFilter.ShouldInclude() for (generic_string): " << resolvedRelativePath.generic_string() << std::endl; ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativePath ), true ) << "Should have included relative path: " << resolvedRelativePath.generic_string(); } From 25d2083437a914fe8bbb9d4f39638fd18413142c Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 2 Feb 2026 16:15:07 +0000 Subject: [PATCH 056/124] Normalize path strings before WildcardMatching --- tools/include/ResourceFilter.h | 3 +++ tools/src/ResourceFilter.cpp | 29 +++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/tools/include/ResourceFilter.h b/tools/include/ResourceFilter.h index c4a9129..0790902 100644 --- a/tools/include/ResourceFilter.h +++ b/tools/include/ResourceFilter.h @@ -42,6 +42,9 @@ class ResourceFilter // Helper function for wildcard matching paths (supports "*" and "...") static bool WildcardMatch( const std::string& pattern, const std::string& checkStr ); + + // Helper function to normalize paths (i.e. deal with \ / .. . etc) + static std::string NormalizePath( const std::string& path ); }; } diff --git a/tools/src/ResourceFilter.cpp b/tools/src/ResourceFilter.cpp index 97f8621..7586fa1 100644 --- a/tools/src/ResourceFilter.cpp +++ b/tools/src/ResourceFilter.cpp @@ -73,7 +73,7 @@ bool ResourceFilter::ShouldInclude( const std::filesystem::path& inFilePath ) { // Make sure we work with the absolute path representation of the input file std::filesystem::path inFilePathAbs = std::filesystem::absolute( inFilePath ); - std::string inFilePathAbsStr = inFilePathAbs.generic_string(); + std::string inFileNormalAbsPathStr = NormalizePath( inFilePathAbs.generic_string() ); // Priority: lower is higher priority: // -1 = exact match on filename (or folder) @@ -90,9 +90,9 @@ bool ResourceFilter::ShouldInclude( const std::filesystem::path& inFilePath ) // Make sure to work with absolute paths for comparison std::filesystem::path resolvedRelativePath( resolvedRelativePathStr ); std::filesystem::path resolvedPathAbs = std::filesystem::absolute( resolvedRelativePath ); - std::string resolvedPathAbsStr = resolvedPathAbs.generic_string(); + std::string resolvedNormalAbsPathStr = NormalizePath( resolvedPathAbs.generic_string() ); - if( resolvedPathAbsStr == inFilePathAbsStr ) + if( resolvedNormalAbsPathStr == inFileNormalAbsPathStr ) { // If there is an exact match on the full filename path, this means highest priority and // SHOULD BE considered an "INCLUDE" even though resolvedPath has filters that might say otherwise. @@ -104,19 +104,23 @@ bool ResourceFilter::ShouldInclude( const std::filesystem::path& inFilePath ) // We need to append it to the absolute path (if specified) before WildcardMatching if( resolvedRelativePathStr.find( "..." ) != std::string::npos ) { - if( resolvedPathAbsStr.back() != '/' ) + if( resolvedNormalAbsPathStr.back() != '/' ) { - resolvedPathAbsStr += '/'; + resolvedNormalAbsPathStr += '/'; } - resolvedPathAbsStr += "..."; + resolvedNormalAbsPathStr += "..."; } - if( !WildcardMatch( resolvedPathAbsStr, inFilePathAbsStr ) ) + if( !WildcardMatch( resolvedNormalAbsPathStr, inFileNormalAbsPathStr ) ) { // There was NO wildcard match on paths, ignore this resolvedRelativePath entry continue; } // There is a Wildcard match - determine the folder depth difference + // Reset the path variables to their Normalized absolute path components (in case they differ from non-Normalized version) + inFilePathAbs = std::filesystem::absolute( inFileNormalAbsPathStr ); + resolvedPathAbs = std::filesystem::absolute( resolvedNormalAbsPathStr ); + auto inFileIt = inFilePathAbs.begin(); auto resolvedIt = resolvedPathAbs.begin(); while( inFileIt != inFilePathAbs.end() && resolvedIt != resolvedPathAbs.end() && *inFileIt == *resolvedIt ) @@ -181,7 +185,7 @@ bool ResourceFilter::ShouldInclude( const std::filesystem::path& inFilePath ) // checkStr = The input file path to check against the pattern bool ResourceFilter::WildcardMatch( const std::string& pattern, const std::string& checkStr ) { - // Replace ... with a unique token, then process * + // Replace ... with a unique token std::string pat = pattern; std::string token = "\x01"; size_t pos; @@ -189,6 +193,8 @@ bool ResourceFilter::WildcardMatch( const std::string& pattern, const std::strin { pat.replace( pos, 3, token ); } + + // Escape special characters and deal with wildcards std::string regexPat; for( size_t i = 0; i < pat.size(); ++i ) { @@ -211,6 +217,7 @@ bool ResourceFilter::WildcardMatch( const std::string& pattern, const std::strin regexPat += pat[i]; } } + try { std::regex re( regexPat, std::regex::ECMAScript | std::regex::icase ); @@ -223,4 +230,10 @@ bool ResourceFilter::WildcardMatch( const std::string& pattern, const std::strin } } +std::string ResourceFilter::NormalizePath( const std::string& path ) +{ + std::filesystem::path p( path ); + return p.lexically_normal().generic_string(); +} + } // namespace ResourceTools From e237775486a695cd4acfccc30ad8143331590426 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 2 Feb 2026 16:38:41 +0000 Subject: [PATCH 057/124] Change WildcardMatch regex exception handling --- tools/src/ResourceFilter.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/src/ResourceFilter.cpp b/tools/src/ResourceFilter.cpp index 7586fa1..9f214d8 100644 --- a/tools/src/ResourceFilter.cpp +++ b/tools/src/ResourceFilter.cpp @@ -224,9 +224,15 @@ bool ResourceFilter::WildcardMatch( const std::string& pattern, const std::strin bool regexResult = std::regex_match( checkStr, re ); return regexResult; } - catch( ... ) + catch( const std::regex_error& e ) { - return false; + std::string errorMsg = "Regex Exception during WildcardMatching - regexPattern: " + regexPat + " checkString: " + checkStr + " - error details: " + e.what(); + throw std::runtime_error( errorMsg ); + } + catch( const std::exception& e ) + { + std::string errorMsg = "Standard Exception during WildcardMatching - regexPattern: " + regexPat + " checkString: " + checkStr + " - error details: " + e.what(); + throw std::runtime_error( errorMsg ); } } From f91de560d9aaafdd058852d57ab3ccb57eae72b8 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 2 Feb 2026 17:26:50 +0000 Subject: [PATCH 058/124] Add debugging code to Path tests Changed tests: - ResourceFilter_Load_validSimpleExample1_ini_usingRelativePaths - ResourceFilter_Load_validSimpleExample1_ini_usingAbsolutePaths - ResourceFilter_Load_validComplexExample1_ini_usingRelativePaths - ResourceFilter_Load2iniFiles_validComplexExample1_and_validSimpleExample1 New tests: - ResourceFilter_Load_validComplexExample1_ini_usingAbsolutePaths --- tests/src/ResourceFilterTest.cpp | 275 ++++++++++++++++++++++++++++++- 1 file changed, 271 insertions(+), 4 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index e85f09c..eb293ab 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -1872,16 +1872,18 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load_validSimpleExample1_ini_usingRel }; ASSERT_EQ( resourceFilter.HasFilters(), true ); + // TODO: Remove debug output once confirmed working on macOS (TeamCity) //-- BEGIN - Debug resourceFilter contents vs expected values std::cout << "------ Debug info (begin) -----" << std::endl; std::cout << "ResourceFilter FullResolvedPathMap contents:" << std::endl; - for ( const auto& [fullPath, filter] : resourceFilter.GetFullResolvedPathMap() ) + for( const auto& [fullPath, filter] : resourceFilter.GetFullResolvedPathMap() ) { std::cout << "ResourceFilter Path: " << std::endl; std::cout << " - path (relative string): " << fullPath << std::endl; - std::cout << " - path (relative generic_string): " << std::filesystem::path(fullPath).generic_string() << std::endl; - std::cout << " - path (absolute generic_string): " << std::filesystem::absolute(std::filesystem::path(fullPath)).generic_string() << std::endl; + std::cout << " - path (relative generic_string): " << std::filesystem::path( fullPath ).generic_string() << std::endl; + std::cout << " - path (absolute generic_string): " << std::filesystem::absolute( std::filesystem::path( fullPath ) ).generic_string() << std::endl; + std::cout << " - path (absolute normal string): " << std::filesystem::absolute( std::filesystem::path( fullPath ).lexically_normal() ).generic_string() << std::endl; std::cout << " Includes: "; for( const auto& include : filter.GetIncludeFilter() ) { @@ -1901,10 +1903,12 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load_validSimpleExample1_ini_usingRel { std::cout << " - path (relative string): " << resolvedRelativePath.string() << std::endl; std::cout << " - path (relative generic_string): " << resolvedRelativePath.generic_string() << std::endl; - std::cout << " - path (absolute generic_string): " << std::filesystem::absolute(resolvedRelativePath).generic_string() << std::endl << std::endl; + std::cout << " - path (absolute generic_string): " << std::filesystem::absolute( resolvedRelativePath ).generic_string() << std::endl; + std::cout << " - path (absolute normal string): " << std::filesystem::absolute( resolvedRelativePath.lexically_normal() ).generic_string() << std::endl; } std::cout << "------ Debug info (end) -----" << std::endl; //-- END - Debug resourceFilter contents vs expected values + for( const auto& resolvedRelativePath : validResolvedRelativePaths ) { // TODO: Remove debug output once confirmed working on macOS (TeamCity) @@ -1951,8 +1955,46 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load_validSimpleExample1_ini_usingAbs }; ASSERT_EQ( resourceFilter.HasFilters(), true ); + + // TODO: Remove debug output once confirmed working on macOS (TeamCity) + //-- BEGIN - Debug resourceFilter contents vs expected values + std::cout << "------ Debug info (begin) -----" << std::endl; + std::cout << "ResourceFilter FullResolvedPathMap contents:" << std::endl; + for( const auto& [fullPath, filter] : resourceFilter.GetFullResolvedPathMap() ) + { + std::cout << "ResourceFilter Path: " << std::endl; + std::cout << " - path (relative string): " << fullPath << std::endl; + std::cout << " - path (relative generic_string): " << std::filesystem::path( fullPath ).generic_string() << std::endl; + std::cout << " - path (absolute generic_string): " << std::filesystem::absolute( std::filesystem::path( fullPath ) ).generic_string() << std::endl; + std::cout << " - path (absolute normal string): " << std::filesystem::absolute( std::filesystem::path( fullPath ).lexically_normal() ).generic_string() << std::endl; + std::cout << " Includes: "; + for( const auto& include : filter.GetIncludeFilter() ) + { + std::cout << include << " "; + } + std::cout << std::endl; + std::cout << " Excludes: "; + for( const auto& exclude : filter.GetExcludeFilter() ) + { + std::cout << exclude << " "; + } + std::cout << std::endl; + } + std::cout << "- - - - - - - - -" << std::endl; + std::cout << "Expected valid resolved absolute paths:" << std::endl; + for( const auto& resolvedAbsolutePath : validResolvedAbsolutePaths ) + { + std::cout << " - path (absolute string): " << resolvedAbsolutePath.string() << std::endl; + std::cout << " - path (absolute generic_string): " << resolvedAbsolutePath.generic_string() << std::endl; + std::cout << " - path (absolute normal string): " << resolvedAbsolutePath.lexically_normal().generic_string() << std::endl; + } + std::cout << "------ Debug info (end) -----" << std::endl; + //-- END - Debug resourceFilter contents vs expected values + for( const auto& resolvedAbsPath : validResolvedAbsolutePaths ) { + // TODO: Remove debug output once confirmed working on macOS (TeamCity) + std::cout << "About to call resourceFilter.ShouldInclude() for (generic_string): " << resolvedAbsPath.generic_string() << std::endl; ASSERT_EQ( resourceFilter.ShouldInclude( resolvedAbsPath ), true ) << "Should have included absolute path: " << resolvedAbsPath.generic_string(); } @@ -2005,8 +2047,47 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load_validComplexExample1_ini_usingRe }; ASSERT_EQ( resourceFilter.HasFilters(), true ); + + // TODO: Remove debug output once confirmed working on macOS (TeamCity) + //-- BEGIN - Debug resourceFilter contents vs expected values + std::cout << "------ Debug info (begin) -----" << std::endl; + std::cout << "ResourceFilter FullResolvedPathMap contents:" << std::endl; + for( const auto& [fullPath, filter] : resourceFilter.GetFullResolvedPathMap() ) + { + std::cout << "ResourceFilter Path: " << std::endl; + std::cout << " - path (relative string): " << fullPath << std::endl; + std::cout << " - path (relative generic_string): " << std::filesystem::path( fullPath ).generic_string() << std::endl; + std::cout << " - path (absolute generic_string): " << std::filesystem::absolute( std::filesystem::path( fullPath ) ).generic_string() << std::endl; + std::cout << " - path (absolute normal string): " << std::filesystem::absolute( std::filesystem::path( fullPath ).lexically_normal() ).generic_string() << std::endl; + std::cout << " Includes: "; + for( const auto& include : filter.GetIncludeFilter() ) + { + std::cout << include << " "; + } + std::cout << std::endl; + std::cout << " Excludes: "; + for( const auto& exclude : filter.GetExcludeFilter() ) + { + std::cout << exclude << " "; + } + std::cout << std::endl; + } + std::cout << "- - - - - - - - -" << std::endl; + std::cout << "Expected valid resolved relative paths:" << std::endl; for( const auto& resolvedRelativePath : validResolvedRelativePaths ) { + std::cout << " - path (relative string): " << resolvedRelativePath.string() << std::endl; + std::cout << " - path (relative generic_string): " << resolvedRelativePath.generic_string() << std::endl; + std::cout << " - path (absolute generic_string): " << std::filesystem::absolute( resolvedRelativePath ).generic_string() << std::endl; + std::cout << " - path (absolute normal string): " << std::filesystem::absolute( resolvedRelativePath.lexically_normal() ).generic_string() << std::endl; + } + std::cout << "------ Debug info (end) -----" << std::endl; + //-- END - Debug resourceFilter contents vs expected values + + for( const auto& resolvedRelativePath : validResolvedRelativePaths ) + { + // TODO: Remove debug output once confirmed working on macOS (TeamCity) + std::cout << "About to call resourceFilter.ShouldInclude() for (generic_string): " << resolvedRelativePath.generic_string() << std::endl; ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativePath ), true ) << "Should have included relative path: " << resolvedRelativePath.generic_string(); } @@ -2084,6 +2165,153 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load_validComplexExample1_ini_usingRe } } +TEST_F( ResourceFilterTest, ResourceFilter_Load_validComplexExample1_ini_usingAbsolutePaths ) +{ + // Alter the current working directory for the duration of this test + CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); + + try + { + const std::filesystem::path iniPath1 = "ExampleIniFiles/validComplexExample1.ini"; + std::vector pathsAbs = { std::filesystem::absolute( iniPath1 ) }; + ResourceTools::ResourceFilter resourceFilter; + resourceFilter.Initialize( pathsAbs ); + + // Validate correct included paths via the resourceFilter: + std::set validResolvedRelativePaths = { + //"PatchWithInputChunk/NextBuildResources/introMovie.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + std::filesystem::absolute( "PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resLocalCDN:/../NextBuildResources/introMoviePrefixed.txt + //"PatchWithInputChunk/NextBuildResources/introMovieSomewhatChanged.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + std::filesystem::absolute( "PatchWithInputChunk/NextBuildResources/testResource2.txt" ), + std::filesystem::absolute( "PatchWithInputChunk/NextBuildResources/videoCardCategories.yaml" ), + std::filesystem::absolute( "PatchWithInputChunk/PreviousBuildResources/introMovie.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] + std::filesystem::absolute( "PatchWithInputChunk/PreviousBuildResources/introMoviePrefixed.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] + std::filesystem::absolute( "PatchWithInputChunk/PreviousBuildResources/introMovieSomewhatChanged.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] + //"PatchWithInputChunk/PreviousBuildResources/testResource.txt", // resRoot:/PatchWithInputChunk/PreviousBuildResources/* ![ testResource.txt ] + std::filesystem::absolute( "PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml" ), + std::filesystem::absolute( "PatchWithInputChunk/PatchResourceGroup_previousBuild_latestBuild.yaml" ), + std::filesystem::absolute( "PatchWithInputChunk/resFileIndexShort_build_next.txt" ), + std::filesystem::absolute( "PatchWithInputChunk/resFileIndexShort_build_previous.txt" ), + }; + + ASSERT_EQ( resourceFilter.HasFilters(), true ); + + // TODO: Remove debug output once confirmed working on macOS (TeamCity) + //-- BEGIN - Debug resourceFilter contents vs expected values + std::cout << "------ Debug info (begin) -----" << std::endl; + std::cout << "ResourceFilter FullResolvedPathMap contents:" << std::endl; + for( const auto& [fullPath, filter] : resourceFilter.GetFullResolvedPathMap() ) + { + std::cout << "ResourceFilter Path: " << std::endl; + std::cout << " - path (relative string): " << fullPath << std::endl; + std::cout << " - path (relative generic_string): " << std::filesystem::path( fullPath ).generic_string() << std::endl; + std::cout << " - path (absolute generic_string): " << std::filesystem::absolute( std::filesystem::path( fullPath ) ).generic_string() << std::endl; + std::cout << " - path (absolute normal string): " << std::filesystem::absolute( std::filesystem::path( fullPath ).lexically_normal() ).generic_string() << std::endl; + std::cout << " Includes: "; + for( const auto& include : filter.GetIncludeFilter() ) + { + std::cout << include << " "; + } + std::cout << std::endl; + std::cout << " Excludes: "; + for( const auto& exclude : filter.GetExcludeFilter() ) + { + std::cout << exclude << " "; + } + std::cout << std::endl; + } + std::cout << "- - - - - - - - -" << std::endl; + std::cout << "Expected valid resolved absolute paths:" << std::endl; + for( const auto& resolvedAbsolutePath : validResolvedRelativePaths ) + { + std::cout << " - path (absolute string): " << resolvedAbsolutePath.string() << std::endl; + std::cout << " - path (absolute generic_string): " << resolvedAbsolutePath.generic_string() << std::endl; + std::cout << " - path (absolute normal string): " << resolvedAbsolutePath.lexically_normal().generic_string() << std::endl; + } + std::cout << "------ Debug info (end) -----" << std::endl; + //-- END - Debug resourceFilter contents vs expected values + + for( const auto& resolvedAbsPath : validResolvedRelativePaths ) + { + // TODO: Remove debug output once confirmed working on macOS (TeamCity) + std::cout << "About to call resourceFilter.ShouldInclude() for (generic_string): " << resolvedAbsPath.generic_string() << std::endl; + ASSERT_EQ( resourceFilter.ShouldInclude( resolvedAbsPath ), true ) << "Should have included relative path: " << resolvedAbsPath.generic_string(); + } + + // Additional check to make sure the FullResolvedPathMap contains correct data (either include or exclude): + std::set expectedPaths = { + "PatchWithInputChunk/...", + "./PatchWithInputChunk/...", + "PatchWithInputChunk/PreviousBuildResources/*", + "PatchWithInputChunk/LocalCDNPatches/../NextBuildResources/introMoviePrefixed.txt", + "./PatchWithInputChunk/PreviousBuildResources/*" + }; + const auto& fullPathMap = resourceFilter.GetFullResolvedPathMap(); + ASSERT_EQ( fullPathMap.size(), expectedPaths.size() ); + MapContainsPaths( expectedPaths, fullPathMap, "FullResolvedPathMap from validSimpleExample1.ini" ); + + // Manually validate the fullPathMap, as it has several different prefixPathCombos + some inline filter overrides + for( const auto& kv : fullPathMap ) + { + if( kv.first == "PatchWithInputChunk/..." ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".yaml" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".txt" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 0 ); + } + else if( kv.first == "./PatchWithInputChunk/..." ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".yaml" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".txt" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 1 ); + EXPECT_EQ( kv.second.GetExcludeFilter()[0], "Movie" ); + } + else if( kv.first == "PatchWithInputChunk/PreviousBuildResources/*" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 3 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".yaml" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".txt" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), "Movie" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 0 ); + } + else if( kv.first == "PatchWithInputChunk/LocalCDNPatches/../NextBuildResources/introMoviePrefixed.txt" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".yaml" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".txt" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 0 ); + } + else if( kv.first == "./PatchWithInputChunk/PreviousBuildResources/*" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".yaml" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".txt" ) != kv.second.GetIncludeFilter().end() ); + + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 1 ); + EXPECT_EQ( kv.second.GetExcludeFilter()[0], "testResource.txt" ); + } + else + { + FAIL() << "Unexpected path found in FullResolvedPathMap: " << kv.first; + } + } + } + catch( const std::exception& e ) + { + FAIL() << "Test [ResourceFilter_Load_validComplexExample1_ini_usingRelativePaths] failed with: " << e.what(); + } + catch( ... ) + { + FAIL() << "Test [ResourceFilter_Load_validComplexExample1_ini_usingRelativePaths] failed when it should have passed."; + } +} + TEST_F( ResourceFilterTest, ResourceFilter_Load2iniFiles_validComplexExample1_and_validSimpleExample1 ) { // Alter the current working directory for the duration of this test @@ -2120,8 +2348,47 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load2iniFiles_validComplexExample1_an }; ASSERT_EQ( resourceFilter.HasFilters(), true ); + + // TODO: Remove debug output once confirmed working on macOS (TeamCity) + //-- BEGIN - Debug resourceFilter contents vs expected values + std::cout << "------ Debug info (begin) -----" << std::endl; + std::cout << "ResourceFilter FullResolvedPathMap contents:" << std::endl; + for( const auto& [fullPath, filter] : resourceFilter.GetFullResolvedPathMap() ) + { + std::cout << "ResourceFilter Path: " << std::endl; + std::cout << " - path (relative string): " << fullPath << std::endl; + std::cout << " - path (relative generic_string): " << std::filesystem::path( fullPath ).generic_string() << std::endl; + std::cout << " - path (absolute generic_string): " << std::filesystem::absolute( std::filesystem::path( fullPath ) ).generic_string() << std::endl; + std::cout << " - path (absolute normal string): " << std::filesystem::absolute( std::filesystem::path( fullPath ).lexically_normal() ).generic_string() << std::endl; + std::cout << " Includes: "; + for( const auto& include : filter.GetIncludeFilter() ) + { + std::cout << include << " "; + } + std::cout << std::endl; + std::cout << " Excludes: "; + for( const auto& exclude : filter.GetExcludeFilter() ) + { + std::cout << exclude << " "; + } + std::cout << std::endl; + } + std::cout << "- - - - - - - - -" << std::endl; + std::cout << "Expected valid resolved relative paths:" << std::endl; for( const auto& resolvedRelativePath : validResolvedRelativePaths ) { + std::cout << " - path (relative string): " << resolvedRelativePath.string() << std::endl; + std::cout << " - path (relative generic_string): " << resolvedRelativePath.generic_string() << std::endl; + std::cout << " - path (absolute generic_string): " << std::filesystem::absolute( resolvedRelativePath ).generic_string() << std::endl; + std::cout << " - path (absolute normal string): " << std::filesystem::absolute( resolvedRelativePath.lexically_normal() ).generic_string() << std::endl; + } + std::cout << "------ Debug info (end) -----" << std::endl; + //-- END - Debug resourceFilter contents vs expected values + + for( const auto& resolvedRelativePath : validResolvedRelativePaths ) + { + // TODO: Remove debug output once confirmed working on macOS (TeamCity) + std::cout << "About to call resourceFilter.ShouldInclude() for (generic_string): " << resolvedRelativePath.generic_string() << std::endl; ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativePath ), true ) << "Should have included relative path: " << resolvedRelativePath.generic_string(); } From b983c2af52dddd0df10fb79c3c836c9a86575093 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:53:44 +0000 Subject: [PATCH 059/124] Fix formatting of FilterResourcePathFile.cpp --- tools/src/FilterResourcePathFile.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/src/FilterResourcePathFile.cpp b/tools/src/FilterResourcePathFile.cpp index 08a71a1..6fc709d 100644 --- a/tools/src/FilterResourcePathFile.cpp +++ b/tools/src/FilterResourcePathFile.cpp @@ -35,14 +35,18 @@ void FilterResourcePathFile::ParseRawPathFileAttribute() // Trim whitespace from both ends size_t first = line.find_first_not_of( " \t\r" ); if( first == std::string::npos ) + { continue; // skip if empty line + } size_t last = line.find_last_not_of( " \t\r" ); std::string rawPathLine = line.substr( first, last - first + 1 ); // Skip commented out lines (in case there is "inline" comment within the .ini file attribute value) if( rawPathLine.empty() || rawPathLine[0] == '#' || rawPathLine[0] == ';' ) + { continue; + } // Add entries to the resolved path map auto lineEntry = FilterResourcePathFileEntry( rawPathLine, m_parentPrefixMap, m_parentSectionFilter ); From 8841d937f641ab404590fc10566c7dadafc42a39 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 3 Feb 2026 11:03:40 +0000 Subject: [PATCH 060/124] Add debug info in ResourceFilter.cpp - To be removed later --- tools/src/ResourceFilter.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tools/src/ResourceFilter.cpp b/tools/src/ResourceFilter.cpp index 9f214d8..b2f57b1 100644 --- a/tools/src/ResourceFilter.cpp +++ b/tools/src/ResourceFilter.cpp @@ -3,6 +3,7 @@ #include #include #include +#include // TODO: Added for debugging purposes, remove this namespace ResourceTools { @@ -82,6 +83,11 @@ bool ResourceFilter::ShouldInclude( const std::filesystem::path& inFilePath ) int bestIncludePriority = std::numeric_limits::max(); int bestExcludePriority = std::numeric_limits::max(); + // TODO: Add debugging info + std::cout << " - inFilePath: " << inFilePath << std::endl; + std::cout << " - inFilePathAbs: " << inFilePathAbs.generic_string() << std::endl; + std::cout << " - inFileNormalAbsPathStr: " << inFileNormalAbsPathStr << std::endl; + // Get the full resolved path map and iterate through it (contains relative paths) const auto& resolvedPathMap = GetFullResolvedPathMap(); @@ -92,6 +98,13 @@ bool ResourceFilter::ShouldInclude( const std::filesystem::path& inFilePath ) std::filesystem::path resolvedPathAbs = std::filesystem::absolute( resolvedRelativePath ); std::string resolvedNormalAbsPathStr = NormalizePath( resolvedPathAbs.generic_string() ); + // TODO: Add debugging info + std::cout << " Checking resolvedRelativePathStr:" << std::endl; + std::cout << " - resolvedRelativePathStr: " << resolvedRelativePathStr << std::endl; + std::cout << " - resolvedRelativePath: " << resolvedRelativePath.generic_string() << std::endl; + std::cout << " - resolvedPathAbs: " << resolvedPathAbs.generic_string() << std::endl; + std::cout << " - resolvedNormalAbsPathStr: " << resolvedNormalAbsPathStr << std::endl; + if( resolvedNormalAbsPathStr == inFileNormalAbsPathStr ) { // If there is an exact match on the full filename path, this means highest priority and @@ -109,9 +122,15 @@ bool ResourceFilter::ShouldInclude( const std::filesystem::path& inFilePath ) resolvedNormalAbsPathStr += '/'; } resolvedNormalAbsPathStr += "..."; + + // TODO: Add debugging info + std::cout << " - Adjusted resolvedNormalAbsPathStr for ... : " << resolvedNormalAbsPathStr << std::endl; } if( !WildcardMatch( resolvedNormalAbsPathStr, inFileNormalAbsPathStr ) ) { + // TODO: Add debugging info + std::cout << " No WildcardMatch for pattern: " << resolvedNormalAbsPathStr << " checkStr: " << inFileNormalAbsPathStr << std::endl; + // There was NO wildcard match on paths, ignore this resolvedRelativePath entry continue; } @@ -121,6 +140,11 @@ bool ResourceFilter::ShouldInclude( const std::filesystem::path& inFilePath ) inFilePathAbs = std::filesystem::absolute( inFileNormalAbsPathStr ); resolvedPathAbs = std::filesystem::absolute( resolvedNormalAbsPathStr ); + // TODO: Add debugging info + std::cout << " WildcardMatch succeeded!" << std::endl; + std::cout << " - inFilePathAbs (normalized): " << inFilePathAbs.generic_string() << std::endl; + std::cout << " - resolvedPathAbs (normalized): " << resolvedPathAbs.generic_string() << std::endl; + auto inFileIt = inFilePathAbs.begin(); auto resolvedIt = resolvedPathAbs.begin(); while( inFileIt != inFilePathAbs.end() && resolvedIt != resolvedPathAbs.end() && *inFileIt == *resolvedIt ) @@ -159,6 +183,10 @@ bool ResourceFilter::ShouldInclude( const std::filesystem::path& inFilePath ) } } + // TODO: Add debugging info + std::cout << " - bestIncludePriority: " << bestIncludePriority << std::endl; + std::cout << " - bestExcludePriority: " << bestExcludePriority << std::endl; + // Apply priority rules: if( bestIncludePriority == std::numeric_limits::max() ) { From d7c7bdae46f69abf072bcd35c23b58fb5396caeb Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 3 Feb 2026 13:31:08 +0000 Subject: [PATCH 061/124] Fix handling on recursive folder wildcard "..." --- tools/src/ResourceFilter.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tools/src/ResourceFilter.cpp b/tools/src/ResourceFilter.cpp index b2f57b1..336492a 100644 --- a/tools/src/ResourceFilter.cpp +++ b/tools/src/ResourceFilter.cpp @@ -113,19 +113,25 @@ bool ResourceFilter::ShouldInclude( const std::filesystem::path& inFilePath ) continue; } - // std::filesystem::path does not support "..." (recursive wildcard). - // We need to append it to the absolute path (if specified) before WildcardMatching - if( resolvedRelativePathStr.find( "..." ) != std::string::npos ) + // Make sure the resolvedNormalAbsPathStr contains the "..." (recursive folder wildcard) if the + // original resolvedRelativePathStr had it. + // Only check the end of the string for any combination of ["/...", "...", ".../"]. + if( resolvedRelativePathStr.find( "...", resolvedRelativePathStr.size() - 4 ) != std::string::npos ) { - if( resolvedNormalAbsPathStr.back() != '/' ) + if( resolvedNormalAbsPathStr.find( "...", resolvedNormalAbsPathStr.size() - 4 ) == std::string::npos ) { - resolvedNormalAbsPathStr += '/'; + if( resolvedNormalAbsPathStr.back() != '/' ) + { + resolvedNormalAbsPathStr += '/'; + } + resolvedNormalAbsPathStr += "..."; } - resolvedNormalAbsPathStr += "..."; // TODO: Add debugging info std::cout << " - Adjusted resolvedNormalAbsPathStr for ... : " << resolvedNormalAbsPathStr << std::endl; } + + // Perform Wildcard matching on the normalized absolute paths if( !WildcardMatch( resolvedNormalAbsPathStr, inFileNormalAbsPathStr ) ) { // TODO: Add debugging info From b0d0b537c105a276ba350de4f2e8c36eadcddaa4 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 3 Feb 2026 14:33:57 +0000 Subject: [PATCH 062/124] Temp disable expected checks on Filter CLI tests - Done to generate the missing "gold" compare files on macOS --- tests/src/ResourcesCliTest.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index 3946658..903f8df 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -354,10 +354,11 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) #else #error Unsupported platform #endif - EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ); + // TODO: Temporarily disable so test passes and doesn't cleanup the output file + //EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ); // Cleanup test output files - CleanupTestOutputFiles( { outputFilePath } ); + //CleanupTestOutputFiles( { outputFilePath } ); } TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) @@ -404,10 +405,11 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) #else #error Unsupported platform #endif - EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ); + // TODO: Temporarily disable so test passes and doesn't cleanup the output file + //EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ); // Cleanup test output files - CleanupTestOutputFiles( { outputFilePath } ); + //CleanupTestOutputFiles( { outputFilePath } ); } TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 ) @@ -460,10 +462,11 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 #else #error Unsupported platform #endif - EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ); + // TODO: Temporarily disable so test passes and doesn't cleanup the output file + //EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ); // Cleanup test output files - CleanupTestOutputFiles( { outputFilePath } ); + //CleanupTestOutputFiles( { outputFilePath } ); } //--------------------------------------- From 85db31b1549cc938e46eb0a7176a3bd024cc006a Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 3 Feb 2026 14:58:27 +0000 Subject: [PATCH 063/124] Add debug statements for CLI Filter tests --- tests/src/ResourcesCliTest.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index 903f8df..294002f 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -338,6 +338,13 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.generic_string() ); + // TODO: Add debug information on parameters + std::cout << "CLI arguments: " << std::endl; + for( const auto& arg : arguments ) + { + std::cout << " " << arg << std::endl; + } + int res = RunCli( arguments, output ); std::cout << "Test RunCli output: " << std::endl; std::cout << "----------------------------------" << std::endl; @@ -389,6 +396,13 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.generic_string() ); + // TODO: Add debug information on parameters + std::cout << "CLI arguments: " << std::endl; + for( const auto& arg : arguments ) + { + std::cout << " " << arg << std::endl; + } + int res = RunCli( arguments, output ); std::cout << "Test RunCli output: " << std::endl; std::cout << "----------------------------------" << std::endl; @@ -446,6 +460,13 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.generic_string() ); + // TODO: Add debug information on parameters + std::cout << "CLI arguments: " << std::endl; + for( const auto& arg : arguments ) + { + std::cout << " " << arg << std::endl; + } + int res = RunCli( arguments, output ); std::cout << "Test RunCli output: " << std::endl; std::cout << "----------------------------------" << std::endl; From 02c5c0f487f913bbe4d9f136251f58245f0ac026 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 3 Feb 2026 17:01:56 +0000 Subject: [PATCH 064/124] Normalize paths in CLI Filter test arguments --- tests/src/ResourcesCliTest.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index 294002f..0870e3a 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -310,6 +310,12 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryOldDocumentFormatWithP //--------------------------------------- +// Helper function to normalize paths +std::string NormalizePath( const std::filesystem::path& path ) +{ + return path.lexically_normal().generic_string(); +} + TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) { // Alter the current working directory for the duration of this test @@ -333,7 +339,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); - arguments.push_back( std::filesystem::absolute( filterIniFilePath ).generic_string() ); + arguments.push_back( NormalizePath( std::filesystem::absolute( filterIniFilePath ) ) ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.generic_string() ); @@ -391,7 +397,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); - arguments.push_back( std::filesystem::absolute( filterIniFilePath ).generic_string() ); + arguments.push_back( NormalizePath( std::filesystem::absolute( filterIniFilePath ) ) ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.generic_string() ); @@ -454,7 +460,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 for( auto filterFilePath : filterIniFilePaths ) { arguments.push_back( "--filter-file" ); - arguments.push_back( std::filesystem::absolute( filterFilePath ).generic_string() ); + arguments.push_back( NormalizePath( std::filesystem::absolute( filterFilePath ) ) ); } arguments.push_back( "--output-file" ); From 4b0fa839f9bb195ab995a61504505a8e7fefa5fe Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 3 Feb 2026 21:24:39 +0000 Subject: [PATCH 065/124] Update microsoft vcpkg baseline - Done to try to fix C++20 arm64 osx triplet cmake error for inih --- vcpkg-configuration.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcpkg-configuration.json b/vcpkg-configuration.json index 9c0fca4..7348714 100644 --- a/vcpkg-configuration.json +++ b/vcpkg-configuration.json @@ -1,7 +1,7 @@ { "default-registry": { "kind": "git", - "baseline": "bea476a80218a581bcb5318595849520217c825e", + "baseline": "4334d8b4c8916018600212ab4dd4bbdc343065d1", "repository": "https://github.com/microsoft/vcpkg.git" }, "registries": [ From 5f64f32a67dc52fcca9e86c9b12728027993b06a Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 3 Feb 2026 21:45:51 +0000 Subject: [PATCH 066/124] Normalize paths in CLI Filter test - take 2 --- tests/src/ResourcesCliTest.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index 0870e3a..0eda78b 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -333,7 +333,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) arguments.push_back( "create-group" ); - arguments.push_back( std::filesystem::absolute( inputDirectoryPath ).generic_string() ); + arguments.push_back( NormalizePath( std::filesystem::absolute( inputDirectoryPath ) ) ); arguments.push_back( "--verbosity-level" ); arguments.push_back( "3" ); @@ -342,7 +342,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) arguments.push_back( NormalizePath( std::filesystem::absolute( filterIniFilePath ) ) ); arguments.push_back( "--output-file" ); - arguments.push_back( outputFilePath.generic_string() ); + arguments.push_back( NormalizePath( outputFilePath ) ); // TODO: Add debug information on parameters std::cout << "CLI arguments: " << std::endl; @@ -391,7 +391,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) arguments.push_back( "create-group" ); - arguments.push_back( std::filesystem::absolute( inputDirectoryPath ).generic_string() ); + arguments.push_back( NormalizePath( std::filesystem::absolute( inputDirectoryPath ) ) ); arguments.push_back( "--verbosity-level" ); arguments.push_back( "3" ); @@ -400,7 +400,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) arguments.push_back( NormalizePath( std::filesystem::absolute( filterIniFilePath ) ) ); arguments.push_back( "--output-file" ); - arguments.push_back( outputFilePath.generic_string() ); + arguments.push_back( NormalizePath( outputFilePath ) ); // TODO: Add debug information on parameters std::cout << "CLI arguments: " << std::endl; @@ -452,7 +452,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 arguments.push_back( "create-group" ); - arguments.push_back( std::filesystem::absolute( inputDirectoryPath ).generic_string() ); + arguments.push_back( NormalizePath( std::filesystem::absolute( inputDirectoryPath ) ) ); arguments.push_back( "--verbosity-level" ); arguments.push_back( "3" ); @@ -464,7 +464,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 } arguments.push_back( "--output-file" ); - arguments.push_back( outputFilePath.generic_string() ); + arguments.push_back( NormalizePath( outputFilePath ) ); // TODO: Add debug information on parameters std::cout << "CLI arguments: " << std::endl; From 9873d32aa63f3b3dd982a2f23f18e7fac0a84648 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 3 Feb 2026 22:07:46 +0000 Subject: [PATCH 067/124] Normalize outputFilePath in CLI Filter tests --- tests/src/ResourcesCliTest.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index 0eda78b..092b949 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -342,7 +342,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) arguments.push_back( NormalizePath( std::filesystem::absolute( filterIniFilePath ) ) ); arguments.push_back( "--output-file" ); - arguments.push_back( NormalizePath( outputFilePath ) ); + arguments.push_back( NormalizePath( std::filesystem::absolute( outputFilePath ) ) ); // TODO: Add debug information on parameters std::cout << "CLI arguments: " << std::endl; @@ -367,11 +367,10 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) #else #error Unsupported platform #endif - // TODO: Temporarily disable so test passes and doesn't cleanup the output file - //EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ); + EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ) << " Output file does not match expected gold file."; // Cleanup test output files - //CleanupTestOutputFiles( { outputFilePath } ); + CleanupTestOutputFiles( { outputFilePath } ); } TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) @@ -400,7 +399,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) arguments.push_back( NormalizePath( std::filesystem::absolute( filterIniFilePath ) ) ); arguments.push_back( "--output-file" ); - arguments.push_back( NormalizePath( outputFilePath ) ); + arguments.push_back( NormalizePath( std::filesystem::absolute( outputFilePath ) ) ); // TODO: Add debug information on parameters std::cout << "CLI arguments: " << std::endl; @@ -425,11 +424,10 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) #else #error Unsupported platform #endif - // TODO: Temporarily disable so test passes and doesn't cleanup the output file - //EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ); + EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ) << " Output file does not match expected gold file."; // Cleanup test output files - //CleanupTestOutputFiles( { outputFilePath } ); + CleanupTestOutputFiles( { outputFilePath } ); } TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 ) @@ -464,7 +462,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 } arguments.push_back( "--output-file" ); - arguments.push_back( NormalizePath( outputFilePath ) ); + arguments.push_back( NormalizePath( std::filesystem::absolute( outputFilePath ) ) ); // TODO: Add debug information on parameters std::cout << "CLI arguments: " << std::endl; @@ -489,11 +487,10 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 #else #error Unsupported platform #endif - // TODO: Temporarily disable so test passes and doesn't cleanup the output file - //EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ); + EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ) << " Output file does not match expected gold file."; // Cleanup test output files - //CleanupTestOutputFiles( { outputFilePath } ); + CleanupTestOutputFiles( { outputFilePath } ); } //--------------------------------------- From bf0f295bd07f8cc0eb3dce870f71a49e0bd84afe Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 3 Feb 2026 23:17:27 +0000 Subject: [PATCH 068/124] Use system specific filePaths for CLI Filter tests - Use string() instead of generic_string() --- tests/src/ResourcesCliTest.cpp | 60 +++++++++++++++++----------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index 092b949..cea506e 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -310,10 +310,10 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryOldDocumentFormatWithP //--------------------------------------- -// Helper function to normalize paths +// Helper function to normalize paths, using regular string() function for system specific path separators std::string NormalizePath( const std::filesystem::path& path ) { - return path.lexically_normal().generic_string(); + return path.lexically_normal().string(); } TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) @@ -324,25 +324,25 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) // Setup test parameters std::string output; std::vector arguments; - std::filesystem::path inputDirectoryPath = "."; // The base testData directory - std::filesystem::path outputFilePath = "./IgnoredTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1.yaml"; - std::filesystem::path filterIniFilePath = "./ExampleIniFiles/validSimpleExample1.ini"; + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "." ); // The base testData directory + std::filesystem::path outputFilePath = GetTestFileFileAbsolutePath( "IgnoredTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1.yaml" ); + std::filesystem::path filterIniFilePath = GetTestFileFileAbsolutePath( "ExampleIniFiles/validSimpleExample1.ini" ); // Ensure any previous test output files are removed CleanupTestOutputFiles( { outputFilePath } ); arguments.push_back( "create-group" ); - arguments.push_back( NormalizePath( std::filesystem::absolute( inputDirectoryPath ) ) ); + arguments.push_back( NormalizePath( inputDirectoryPath ) ); arguments.push_back( "--verbosity-level" ); arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); - arguments.push_back( NormalizePath( std::filesystem::absolute( filterIniFilePath ) ) ); + arguments.push_back( NormalizePath( filterIniFilePath ) ); arguments.push_back( "--output-file" ); - arguments.push_back( NormalizePath( std::filesystem::absolute( outputFilePath ) ) ); + arguments.push_back( NormalizePath( outputFilePath ) ); // TODO: Add debug information on parameters std::cout << "CLI arguments: " << std::endl; @@ -361,16 +361,16 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) // Check expected outcome #if _WIN64 - std::filesystem::path goldFile = std::filesystem::absolute( "./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_Windows.yaml" ); + std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_Windows.yaml" ); #elif __APPLE__ - std::filesystem::path goldFile = std::filesystem::absolute( "./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_macOS.yaml" ); + std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_macOS.yaml" ); #else #error Unsupported platform #endif EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ) << " Output file does not match expected gold file."; // Cleanup test output files - CleanupTestOutputFiles( { outputFilePath } ); + //CleanupTestOutputFiles( { outputFilePath } ); } TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) @@ -381,25 +381,25 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) // Setup test parameters std::string output; std::vector arguments; - std::filesystem::path inputDirectoryPath = "."; // The base testData directory - std::filesystem::path outputFilePath = "./IgnoredTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1.yaml"; - std::filesystem::path filterIniFilePath = "./ExampleIniFiles/validComplexExample1.ini"; + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "." ); // The base testData directory + std::filesystem::path outputFilePath = GetTestFileFileAbsolutePath( "IgnoredTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1.yaml" ); + std::filesystem::path filterIniFilePath = GetTestFileFileAbsolutePath( "ExampleIniFiles/validComplexExample1.ini" ); // Ensure any previous test output files are removed CleanupTestOutputFiles( { outputFilePath } ); arguments.push_back( "create-group" ); - arguments.push_back( NormalizePath( std::filesystem::absolute( inputDirectoryPath ) ) ); + arguments.push_back( inputDirectoryPath.string() ); arguments.push_back( "--verbosity-level" ); arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); - arguments.push_back( NormalizePath( std::filesystem::absolute( filterIniFilePath ) ) ); + arguments.push_back( filterIniFilePath.string() ); arguments.push_back( "--output-file" ); - arguments.push_back( NormalizePath( std::filesystem::absolute( outputFilePath ) ) ); + arguments.push_back( outputFilePath.string() ); // TODO: Add debug information on parameters std::cout << "CLI arguments: " << std::endl; @@ -418,16 +418,16 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) // Check expected outcome #if _WIN64 - std::filesystem::path goldFile = std::filesystem::absolute( "./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_Windows.yaml" ); + std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_Windows.yaml" ); #elif __APPLE__ - std::filesystem::path goldFile = std::filesystem::absolute( "./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_macOS.yaml" ); + std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_macOS.yaml" ); #else #error Unsupported platform #endif EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ) << " Output file does not match expected gold file."; // Cleanup test output files - CleanupTestOutputFiles( { outputFilePath } ); + //CleanupTestOutputFiles( { outputFilePath } ); } TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 ) @@ -438,11 +438,11 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 // Setup test parameters std::string output; std::vector arguments; - std::filesystem::path inputDirectoryPath = "."; // The base testData directory - std::filesystem::path outputFilePath = "./IgnoredTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1.yaml"; + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "." ) ; // The base testData directory + std::filesystem::path outputFilePath = GetTestFileFileAbsolutePath( "IgnoredTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1.yaml" ); std::vector filterIniFilePaths = { - "./ExampleIniFiles/validSimpleExample1.ini", - "./ExampleIniFiles/validComplexExample1.ini" + GetTestFileFileAbsolutePath( "ExampleIniFiles/validSimpleExample1.ini" ), + GetTestFileFileAbsolutePath( "ExampleIniFiles/validComplexExample1.ini" ) }; // Ensure any previous test output files are removed @@ -450,7 +450,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 arguments.push_back( "create-group" ); - arguments.push_back( NormalizePath( std::filesystem::absolute( inputDirectoryPath ) ) ); + arguments.push_back( inputDirectoryPath.string() ); arguments.push_back( "--verbosity-level" ); arguments.push_back( "3" ); @@ -458,11 +458,11 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 for( auto filterFilePath : filterIniFilePaths ) { arguments.push_back( "--filter-file" ); - arguments.push_back( NormalizePath( std::filesystem::absolute( filterFilePath ) ) ); + arguments.push_back( filterFilePath.string() ); } arguments.push_back( "--output-file" ); - arguments.push_back( NormalizePath( std::filesystem::absolute( outputFilePath ) ) ); + arguments.push_back( outputFilePath.string() ); // TODO: Add debug information on parameters std::cout << "CLI arguments: " << std::endl; @@ -481,16 +481,16 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 // Check expected outcome #if _WIN64 - std::filesystem::path goldFile = std::filesystem::absolute( "./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_Windows.yaml" ); + std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_Windows.yaml" ); #elif __APPLE__ - std::filesystem::path goldFile = std::filesystem::absolute( "./ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_macOS.yaml" ); + std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_macOS.yaml" ); #else #error Unsupported platform #endif EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ) << " Output file does not match expected gold file."; // Cleanup test output files - CleanupTestOutputFiles( { outputFilePath } ); + //CleanupTestOutputFiles( { outputFilePath } ); } //--------------------------------------- From 9d302ec142281f1d7de1d547a89f3d9ea0b84cdf Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 4 Feb 2026 00:26:28 +0000 Subject: [PATCH 069/124] Change RunCli() to populate stderr as well --- tests/src/CliTestFixture.cpp | 12 ++-- tests/src/CliTestFixture.h | 2 +- tests/src/ResourcesCliTest.cpp | 126 ++++++++++++++++++++------------- 3 files changed, 86 insertions(+), 54 deletions(-) diff --git a/tests/src/CliTestFixture.cpp b/tests/src/CliTestFixture.cpp index 9034863..b2755a3 100644 --- a/tests/src/CliTestFixture.cpp +++ b/tests/src/CliTestFixture.cpp @@ -6,19 +6,23 @@ #include -int CliTestFixture::RunCli( std::vector& arguments, std::string& output ) +int CliTestFixture::RunCli( std::vector& arguments, std::string& output, std::string& errorOutput ) { std::string processOutput; + std::string processError; arguments.insert( arguments.begin(), CARBON_RESOURCES_CLI_EXE_NAME ); - TinyProcessLib::Process process1a( arguments, "", [&processOutput]( const char* bytes, size_t n ) { - processOutput += std::string( bytes, n ); - } ); + TinyProcessLib::Process process1a( + arguments, "", + [&processOutput]( const char* bytes, size_t n ) { processOutput += std::string( bytes, n ); }, + [&processError]( const char* bytes, size_t n ) { processError += std::string( bytes, n ); } + ); auto exit_status = process1a.get_exit_status(); output = processOutput; + errorOutput = processError; return exit_status; } diff --git a/tests/src/CliTestFixture.h b/tests/src/CliTestFixture.h index 541ea3f..5835cb6 100644 --- a/tests/src/CliTestFixture.h +++ b/tests/src/CliTestFixture.h @@ -12,7 +12,7 @@ struct CliTestFixture : public ResourcesTestFixture { - int RunCli( std::vector& arguments, std::string& output ); + int RunCli( std::vector& arguments, std::string& output, std::string& errorOutput ); void CleanupTestOutputFiles( const std::vector& filesToRemove ); }; diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index cea506e..e5bbf4a 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -9,10 +9,11 @@ struct ResourcesCliTest : public CliTestFixture TEST_F( ResourcesCliTest, RunWithoutArguments ) { std::string output; + std::string errorOutput; std::vector arguments; - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); // Expect 4 which indicates failed with no command specified ASSERT_EQ( res, 4 ); @@ -21,12 +22,13 @@ TEST_F( ResourcesCliTest, RunWithoutArguments ) TEST_F( ResourcesCliTest, RunWithNonesenseArguments ) { std::string output; + std::string errorOutput; std::vector arguments; arguments.push_back( "Nonesense" ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); // Expect 3 which indicates failed due to invalid operation ASSERT_EQ( res, 3 ); @@ -35,12 +37,13 @@ TEST_F( ResourcesCliTest, RunWithNonesenseArguments ) TEST_F( ResourcesCliTest, RunCreateGroupWithNoArguments ) { std::string output; + std::string errorOutput; std::vector arguments; arguments.push_back( "create-group" ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); // Expect 2 which failed due to invalid operation arguments ASSERT_EQ( res, 2 ); @@ -49,12 +52,13 @@ TEST_F( ResourcesCliTest, RunCreateGroupWithNoArguments ) TEST_F( ResourcesCliTest, RunCreatePatchWithNoArguments ) { std::string output; + std::string errorOutput; std::vector arguments; arguments.push_back( "create-patch" ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); // Expect 2 which failed due to invalid operation arguments ASSERT_EQ( res, 2 ); @@ -63,12 +67,13 @@ TEST_F( ResourcesCliTest, RunCreatePatchWithNoArguments ) TEST_F( ResourcesCliTest, RunCreateBundleWithNoArguments ) { std::string output; + std::string errorOutput; std::vector arguments; arguments.push_back( "create-bundle" ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); // Expect 2 which failed due to invalid operation arguments ASSERT_EQ( res, 2 ); @@ -79,12 +84,13 @@ TEST_F( ResourcesCliTest, RunCreateBundleWithNoArguments ) TEST_F( ResourcesCliTest, RunApplyPatchWithNoArguments ) { std::string output; + std::string errorOutput; std::vector arguments; arguments.push_back( "apply-patch" ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); // Expect 2 which failed due to invalid operation arguments ASSERT_EQ( res, 2 ); @@ -93,12 +99,13 @@ TEST_F( ResourcesCliTest, RunApplyPatchWithNoArguments ) TEST_F( ResourcesCliTest, RunUnpackBundleWithNoArguments ) { std::string output; + std::string errorOutput; std::vector arguments; arguments.push_back( "unpack-bundle" ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); // Expect 2 which failed due to invalid operation arguments ASSERT_EQ( res, 2 ); @@ -106,8 +113,8 @@ TEST_F( ResourcesCliTest, RunUnpackBundleWithNoArguments ) TEST_F( ResourcesCliTest, CreateOperationWithInvalidInput ) { - std::string output; + std::string errorOutput; std::vector arguments; @@ -119,7 +126,7 @@ TEST_F( ResourcesCliTest, CreateOperationWithInvalidInput ) std::filesystem::path inputDirectory = "INVALID_PATH"; arguments.push_back( inputDirectory.string() ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); // Expect return 1 indicating failed during valid operation ASSERT_EQ( res, 1 ); @@ -129,6 +136,7 @@ TEST_F( ResourcesCliTest, CreateOperationWithInvalidInput ) TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectory ) { std::string output; + std::string errorOutput; std::vector arguments; @@ -144,9 +152,9 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectory ) std::filesystem::path outputFile = "GroupOut/ResourceGroup.yaml"; arguments.push_back( outputFile.string() ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); - ASSERT_EQ( res, 0 ); + ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; #if _WIN64 std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "CreateResourceFiles/ResourceGroupWindows.yaml" ); @@ -161,6 +169,7 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectory ) TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryExportResources ) { std::string output; + std::string errorOutput; std::vector arguments; @@ -185,9 +194,9 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryExportResources ) std::filesystem::path outputFile = "GroupOut/ResourceGroup.yaml"; arguments.push_back( outputFile.string() ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); - ASSERT_EQ( res, 0 ); + ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; #if _WIN64 std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "CreateResourceFiles/ResourceGroupWindows.yaml" ); @@ -204,6 +213,7 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryExportResources ) TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryWithSkipCompression ) { std::string output; + std::string errorOutput; std::vector arguments; @@ -221,9 +231,9 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryWithSkipCompression ) std::filesystem::path outputFile = "GroupOut/ResourceGroup.yaml"; arguments.push_back( outputFile.string() ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); - ASSERT_EQ( res, 0 ); + ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; #if _WIN64 std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "CreateResourceFiles/ResourceGroupSkipCompressionWindows.yaml" ); @@ -238,6 +248,7 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryWithSkipCompression ) TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryOldDocumentFormat ) { std::string output; + std::string errorOutput; std::vector arguments; @@ -256,9 +267,9 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryOldDocumentFormat ) arguments.push_back( "--document-version" ); arguments.push_back( "0.0.0" ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); - ASSERT_EQ( res, 0 ); + ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; #if _WIN64 std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "CreateResourceFiles/ResourceGroupWindows.csv" ); @@ -273,6 +284,7 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryOldDocumentFormat ) TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryOldDocumentFormatWithPrefix ) { std::string output; + std::string errorOutput; std::vector arguments; @@ -294,9 +306,9 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryOldDocumentFormatWithP arguments.push_back( "--resource-prefix" ); arguments.push_back( "test" ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); - ASSERT_EQ( res, 0 ); + ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; #if _WIN64 std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "CreateResourceFiles/ResourceGroupWindowsPrefixed.csv" ); @@ -323,6 +335,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) // Setup test parameters std::string output; + std::string errorOutput; std::vector arguments; std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "." ); // The base testData directory std::filesystem::path outputFilePath = GetTestFileFileAbsolutePath( "IgnoredTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1.yaml" ); @@ -351,13 +364,13 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) std::cout << " " << arg << std::endl; } - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); std::cout << "Test RunCli output: " << std::endl; std::cout << "----------------------------------" << std::endl; std::cout << output << std::endl; std::cout << "----------------------------------" << std::endl; - ASSERT_EQ( res, 0 ) << "CLI operation failed, output: " << output; + ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; // Check expected outcome #if _WIN64 @@ -380,6 +393,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) // Setup test parameters std::string output; + std::string errorOutput; std::vector arguments; std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "." ); // The base testData directory std::filesystem::path outputFilePath = GetTestFileFileAbsolutePath( "IgnoredTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1.yaml" ); @@ -408,13 +422,13 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) std::cout << " " << arg << std::endl; } - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); std::cout << "Test RunCli output: " << std::endl; std::cout << "----------------------------------" << std::endl; std::cout << output << std::endl; std::cout << "----------------------------------" << std::endl; - ASSERT_EQ( res, 0 ) << "CLI operation failed, output: " << output; + ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; // Check expected outcome #if _WIN64 @@ -437,6 +451,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 // Setup test parameters std::string output; + std::string errorOutput; std::vector arguments; std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "." ) ; // The base testData directory std::filesystem::path outputFilePath = GetTestFileFileAbsolutePath( "IgnoredTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1.yaml" ); @@ -471,13 +486,13 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 std::cout << " " << arg << std::endl; } - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); std::cout << "Test RunCli output: " << std::endl; std::cout << "----------------------------------" << std::endl; std::cout << output << std::endl; std::cout << "----------------------------------" << std::endl; - ASSERT_EQ( res, 0 ) << "CLI operation failed, output: " << output; + ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; // Check expected outcome #if _WIN64 @@ -498,6 +513,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 TEST_F( ResourcesCliTest, CreateBundle ) { std::string output; + std::string errorOutput; std::vector arguments; @@ -530,9 +546,9 @@ TEST_F( ResourcesCliTest, CreateBundle ) arguments.push_back( "1000" ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); - EXPECT_EQ( res, 0 ); + EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; // Check expected outcome std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "CreateBundle/BundleResourceGroup.yaml" ); @@ -545,6 +561,7 @@ TEST_F( ResourcesCliTest, CreateBundle ) TEST_F( ResourcesCliTest, RemoveResourcesWithUnknownResourceIgnoreOnResourceNotFound ) { std::string output; + std::string errorOutput; std::vector arguments; @@ -569,14 +586,15 @@ TEST_F( ResourcesCliTest, RemoveResourcesWithUnknownResourceIgnoreOnResourceNotF arguments.push_back( "--ignore-missing-resources" ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); - EXPECT_EQ( res, 0 ); + EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; } TEST_F( ResourcesCliTest, RemoveResourcesWithUnknownResourceWithInvalidPathToResourcesFile ) { std::string output; + std::string errorOutput; std::vector arguments; @@ -599,7 +617,7 @@ TEST_F( ResourcesCliTest, RemoveResourcesWithUnknownResourceWithInvalidPathToRes arguments.push_back( resourceGroupAfterRemovePath.string() ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); EXPECT_EQ( res, 1 ); } @@ -607,6 +625,7 @@ TEST_F( ResourcesCliTest, RemoveResourcesWithUnknownResourceWithInvalidPathToRes TEST_F( ResourcesCliTest, RemoveResourcesWithUnknownResource ) { std::string output; + std::string errorOutput; std::vector arguments; @@ -629,7 +648,7 @@ TEST_F( ResourcesCliTest, RemoveResourcesWithUnknownResource ) arguments.push_back( resourceGroupAfterRemovePath.string() ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); EXPECT_EQ( res, 1 ); } @@ -637,6 +656,7 @@ TEST_F( ResourcesCliTest, RemoveResourcesWithUnknownResource ) TEST_F( ResourcesCliTest, RemoveResources ) { std::string output; + std::string errorOutput; std::vector arguments; @@ -659,9 +679,9 @@ TEST_F( ResourcesCliTest, RemoveResources ) arguments.push_back( resourceGroupAfterRemovePath.string() ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); - EXPECT_EQ( res, 0 ); + EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; // Check output matches expected std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "RemoveResource/ResourceGroupAfterRemove.yaml" ); @@ -672,6 +692,7 @@ TEST_F( ResourcesCliTest, RemoveResources ) TEST_F( ResourcesCliTest, DiffResourceGroupsWithTwoAdditions ) { std::string output; + std::string errorOutput; std::vector arguments; @@ -694,9 +715,9 @@ TEST_F( ResourcesCliTest, DiffResourceGroupsWithTwoAdditions ) arguments.push_back( outputPath.string() ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); - EXPECT_EQ( res, 0 ); + EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; // Check output matches expected std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "DiffGroups/ExpectedDiffWithAdditions.txt" ); @@ -711,6 +732,7 @@ TEST_F( ResourcesCliTest, DiffResourceGroupsWithTwoAdditions ) TEST_F( ResourcesCliTest, DiffResourceGroupsWithTwoChanges ) { std::string output; + std::string errorOutput; std::vector arguments; @@ -733,9 +755,9 @@ TEST_F( ResourcesCliTest, DiffResourceGroupsWithTwoChanges ) arguments.push_back( outputPath.string() ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); - EXPECT_EQ( res, 0 ); + EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; // Check output matches expected std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "DiffGroups/ExpectedDiffWithChanges.txt" ); @@ -750,6 +772,7 @@ TEST_F( ResourcesCliTest, DiffResourceGroupsWithTwoChanges ) TEST_F( ResourcesCliTest, DiffResourceGroupsWithTwoSubtractions ) { std::string output; + std::string errorOutput; std::vector arguments; @@ -772,9 +795,9 @@ TEST_F( ResourcesCliTest, DiffResourceGroupsWithTwoSubtractions ) arguments.push_back( outputPath.string() ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); - EXPECT_EQ( res, 0 ); + EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; // Check output matches expected std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "DiffGroups/ExpectedDiffWithSubtractions.txt" ); @@ -789,6 +812,7 @@ TEST_F( ResourcesCliTest, DiffResourceGroupsWithTwoSubtractions ) TEST_F( ResourcesCliTest, MergeGroup ) { std::string output; + std::string errorOutput; std::vector arguments; @@ -811,9 +835,9 @@ TEST_F( ResourcesCliTest, MergeGroup ) arguments.push_back( mergedOutputPath.string() ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); - EXPECT_EQ( res, 0 ); + EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; // Check output matches expected std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "MergeGroups/YamlAdditive/ExpectedMergedResourceGroup.yaml" ); @@ -824,6 +848,7 @@ TEST_F( ResourcesCliTest, MergeGroup ) TEST_F( ResourcesCliTest, CreatePatch ) { std::string output; + std::string errorOutput; std::vector arguments; @@ -865,9 +890,9 @@ TEST_F( ResourcesCliTest, CreatePatch ) arguments.push_back( "--chunk-size" ); arguments.push_back( "50000000" ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); - EXPECT_EQ( res, 0 ); + EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; // Check expected outcome std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "Patch/PatchResourceGroup.yaml" ); @@ -880,6 +905,7 @@ TEST_F( ResourcesCliTest, CreatePatch ) TEST_F( ResourcesCliTest, CreateGroup ) { std::string output; + std::string errorOutput; std::vector arguments; @@ -898,9 +924,9 @@ TEST_F( ResourcesCliTest, CreateGroup ) arguments.push_back( outputFilename ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); - EXPECT_EQ( res, 0 ); + EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; // Check expected outcome #if _WIN64 @@ -918,6 +944,7 @@ TEST_F( ResourcesCliTest, CreateGroup ) TEST_F( ResourcesCliTest, ApplyPatch ) { std::string output; + std::string errorOutput; std::vector arguments; @@ -961,9 +988,9 @@ TEST_F( ResourcesCliTest, ApplyPatch ) std::filesystem::copy( resourcesToPatchBasePath, outputBasePath ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); - EXPECT_EQ( res, 0 ); + EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; // Check expected outcome std::filesystem::path goldDirectory = GetTestFileFileAbsolutePath( "Patch/NextBuildResources" ); @@ -973,6 +1000,7 @@ TEST_F( ResourcesCliTest, ApplyPatch ) TEST_F( ResourcesCliTest, UnpackBundle ) { std::string output; + std::string errorOutput; std::vector arguments; @@ -995,9 +1023,9 @@ TEST_F( ResourcesCliTest, UnpackBundle ) arguments.push_back( "LOCAL_RELATIVE" ); - int res = RunCli( arguments, output ); + int res = RunCli( arguments, output, errorOutput ); - EXPECT_EQ( res, 0 ); + EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; // Check expected outcome EXPECT_TRUE( DirectoryIsSubset( GetTestFileFileAbsolutePath( "Bundle/Res" ), "UnpackBundleOut" ) ); From a658470c62f2c49769f361a89c3a6d830b75a325 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 4 Feb 2026 15:10:19 +0000 Subject: [PATCH 070/124] Populate std::cerr on Filter errors --- src/ResourceGroupImpl.cpp | 3 +++ tests/src/CliTestFixture.cpp | 7 +++++++ tests/src/ResourcesCliTest.cpp | 21 --------------------- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/ResourceGroupImpl.cpp b/src/ResourceGroupImpl.cpp index 5ff17f8..80930e1 100644 --- a/src/ResourceGroupImpl.cpp +++ b/src/ResourceGroupImpl.cpp @@ -20,6 +20,7 @@ #include "ChunkIndex.h" #include "ResourceGroupFactory.h" #include +#include namespace CarbonResources { @@ -78,6 +79,7 @@ Result ResourceGroup::ResourceGroupImpl::CreateFromDirectory( const CreateResour catch( const std::exception& e ) { std::string errorMsg = "Unable to create ResourceFilter - because of: " + std::string( e.what() ); + std::cerr << errorMsg << std::endl; return Result{ ResultType::FAILED_TO_INITIALIZE_RESOURCE_FILTER, errorMsg }; } } @@ -104,6 +106,7 @@ Result ResourceGroup::ResourceGroupImpl::CreateFromDirectory( const CreateResour catch( const std::exception& e ) { std::string errorMsg = "Unable to decide on include/exclude filtering for: " + entry.path().generic_string() + " - because of: " + std::string( e.what() ); + std::cerr << errorMsg << std::endl; return Result{ ResultType::FAILED_TO_APPLY_RESOURCE_FILTER_RULES, errorMsg }; } } diff --git a/tests/src/CliTestFixture.cpp b/tests/src/CliTestFixture.cpp index b2755a3..fd8b9af 100644 --- a/tests/src/CliTestFixture.cpp +++ b/tests/src/CliTestFixture.cpp @@ -13,6 +13,13 @@ int CliTestFixture::RunCli( std::vector& arguments, std::string& ou arguments.insert( arguments.begin(), CARBON_RESOURCES_CLI_EXE_NAME ); + // TODO: Add debug information on parameters + std::cout << "RunCli arguments: " << std::endl; + for( const auto& arg : arguments ) + { + std::cout << " " << arg << std::endl; + } + TinyProcessLib::Process process1a( arguments, "", [&processOutput]( const char* bytes, size_t n ) { processOutput += std::string( bytes, n ); }, diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index e5bbf4a..5b6d4f8 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -357,13 +357,6 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) arguments.push_back( "--output-file" ); arguments.push_back( NormalizePath( outputFilePath ) ); - // TODO: Add debug information on parameters - std::cout << "CLI arguments: " << std::endl; - for( const auto& arg : arguments ) - { - std::cout << " " << arg << std::endl; - } - int res = RunCli( arguments, output, errorOutput ); std::cout << "Test RunCli output: " << std::endl; std::cout << "----------------------------------" << std::endl; @@ -415,13 +408,6 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.string() ); - // TODO: Add debug information on parameters - std::cout << "CLI arguments: " << std::endl; - for( const auto& arg : arguments ) - { - std::cout << " " << arg << std::endl; - } - int res = RunCli( arguments, output, errorOutput ); std::cout << "Test RunCli output: " << std::endl; std::cout << "----------------------------------" << std::endl; @@ -479,13 +465,6 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.string() ); - // TODO: Add debug information on parameters - std::cout << "CLI arguments: " << std::endl; - for( const auto& arg : arguments ) - { - std::cout << " " << arg << std::endl; - } - int res = RunCli( arguments, output, errorOutput ); std::cout << "Test RunCli output: " << std::endl; std::cout << "----------------------------------" << std::endl; From 0cc50643b18031986084e896a264a7134ffeceef Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 4 Feb 2026 16:12:00 +0000 Subject: [PATCH 071/124] Add override of working directory in CliTestFixture::RunCli() - Also change Filter cli tests to set an override --- tests/src/CliTestFixture.cpp | 4 ++-- tests/src/CliTestFixture.h | 2 +- tests/src/ResourcesCliTest.cpp | 38 ++++++++++++++-------------------- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/tests/src/CliTestFixture.cpp b/tests/src/CliTestFixture.cpp index fd8b9af..11b960a 100644 --- a/tests/src/CliTestFixture.cpp +++ b/tests/src/CliTestFixture.cpp @@ -6,7 +6,7 @@ #include -int CliTestFixture::RunCli( std::vector& arguments, std::string& output, std::string& errorOutput ) +int CliTestFixture::RunCli( std::vector& arguments, std::string& output, std::string& errorOutput, const std::string& workingDirectory /* = "" (empty = do not alter it) */ ) { std::string processOutput; std::string processError; @@ -21,7 +21,7 @@ int CliTestFixture::RunCli( std::vector& arguments, std::string& ou } TinyProcessLib::Process process1a( - arguments, "", + arguments, workingDirectory, [&processOutput]( const char* bytes, size_t n ) { processOutput += std::string( bytes, n ); }, [&processError]( const char* bytes, size_t n ) { processError += std::string( bytes, n ); } ); diff --git a/tests/src/CliTestFixture.h b/tests/src/CliTestFixture.h index 5835cb6..6477b10 100644 --- a/tests/src/CliTestFixture.h +++ b/tests/src/CliTestFixture.h @@ -12,7 +12,7 @@ struct CliTestFixture : public ResourcesTestFixture { - int RunCli( std::vector& arguments, std::string& output, std::string& errorOutput ); + int RunCli( std::vector& arguments, std::string& output, std::string& errorOutput, const std::string& workingDirectory = "" ); void CleanupTestOutputFiles( const std::vector& filesToRemove ); }; diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index 5b6d4f8..9942445 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -322,12 +322,6 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryOldDocumentFormatWithP //--------------------------------------- -// Helper function to normalize paths, using regular string() function for system specific path separators -std::string NormalizePath( const std::filesystem::path& path ) -{ - return path.lexically_normal().string(); -} - TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) { // Alter the current working directory for the duration of this test @@ -337,27 +331,27 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) std::string output; std::string errorOutput; std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "." ); // The base testData directory - std::filesystem::path outputFilePath = GetTestFileFileAbsolutePath( "IgnoredTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1.yaml" ); - std::filesystem::path filterIniFilePath = GetTestFileFileAbsolutePath( "ExampleIniFiles/validSimpleExample1.ini" ); + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path outputFilePath = "IgnoredTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1.yaml"; + std::filesystem::path filterIniFilePath = "ExampleIniFiles/validSimpleExample1.ini"; // Ensure any previous test output files are removed CleanupTestOutputFiles( { outputFilePath } ); arguments.push_back( "create-group" ); - arguments.push_back( NormalizePath( inputDirectoryPath ) ); + arguments.push_back( inputDirectoryPath.string() ); arguments.push_back( "--verbosity-level" ); arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); - arguments.push_back( NormalizePath( filterIniFilePath ) ); + arguments.push_back( filterIniFilePath.string() ); arguments.push_back( "--output-file" ); - arguments.push_back( NormalizePath( outputFilePath ) ); + arguments.push_back( outputFilePath.string() ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments, output, errorOutput, TEST_DATA_BASE_PATH ); std::cout << "Test RunCli output: " << std::endl; std::cout << "----------------------------------" << std::endl; std::cout << output << std::endl; @@ -388,9 +382,9 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) std::string output; std::string errorOutput; std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "." ); // The base testData directory - std::filesystem::path outputFilePath = GetTestFileFileAbsolutePath( "IgnoredTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1.yaml" ); - std::filesystem::path filterIniFilePath = GetTestFileFileAbsolutePath( "ExampleIniFiles/validComplexExample1.ini" ); + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path outputFilePath = "IgnoredTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1.yaml"; + std::filesystem::path filterIniFilePath = "ExampleIniFiles/validComplexExample1.ini"; // Ensure any previous test output files are removed CleanupTestOutputFiles( { outputFilePath } ); @@ -408,7 +402,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.string() ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments, output, errorOutput, TEST_DATA_BASE_PATH ); std::cout << "Test RunCli output: " << std::endl; std::cout << "----------------------------------" << std::endl; std::cout << output << std::endl; @@ -439,11 +433,11 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 std::string output; std::string errorOutput; std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "." ) ; // The base testData directory - std::filesystem::path outputFilePath = GetTestFileFileAbsolutePath( "IgnoredTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1.yaml" ); + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ) ; // The base testData directory + std::filesystem::path outputFilePath = "IgnoredTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1.yaml"; std::vector filterIniFilePaths = { - GetTestFileFileAbsolutePath( "ExampleIniFiles/validSimpleExample1.ini" ), - GetTestFileFileAbsolutePath( "ExampleIniFiles/validComplexExample1.ini" ) + "ExampleIniFiles/validSimpleExample1.ini", + "ExampleIniFiles/validComplexExample1.ini" }; // Ensure any previous test output files are removed @@ -465,7 +459,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.string() ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments, output, errorOutput, TEST_DATA_BASE_PATH ); std::cout << "Test RunCli output: " << std::endl; std::cout << "----------------------------------" << std::endl; std::cout << output << std::endl; From 80a8dec10fb3fdad3b5c6f07da08f225856c6d97 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 4 Feb 2026 21:22:58 +0000 Subject: [PATCH 072/124] Call the CLI with full path in tests --- tests/CMakeLists.txt | 3 ++- tests/src/CliTestFixture.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c0f8453..e49e30e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -23,7 +23,8 @@ add_executable(resources-test ${SRC_FILES}) target_compile_definitions(resources-test PRIVATE TEST_DATA_BASE_PATH="${CMAKE_SOURCE_DIR}/tests/testData" - CARBON_RESOURCES_CLI_EXE_NAME=\"$\") + CARBON_RESOURCES_CLI_EXE_NAME=\"$\" + CARBON_RESOURCES_CLI_EXE_FULLPATH=\"$\") target_include_directories(resources-test PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) diff --git a/tests/src/CliTestFixture.cpp b/tests/src/CliTestFixture.cpp index 11b960a..15a1b23 100644 --- a/tests/src/CliTestFixture.cpp +++ b/tests/src/CliTestFixture.cpp @@ -11,7 +11,7 @@ int CliTestFixture::RunCli( std::vector& arguments, std::string& ou std::string processOutput; std::string processError; - arguments.insert( arguments.begin(), CARBON_RESOURCES_CLI_EXE_NAME ); + arguments.insert( arguments.begin(), CARBON_RESOURCES_CLI_EXE_FULLPATH ); // TODO: Add debug information on parameters std::cout << "RunCli arguments: " << std::endl; From 89bcc80547e32bf4bc4cd7bec47bcfb0f1a1123f Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 4 Feb 2026 22:04:17 +0000 Subject: [PATCH 073/124] Remove debug print statements from tests --- tests/src/CliTestFixture.cpp | 4 +- tests/src/ResourceFilterTest.cpp | 194 ------------------------------- tests/src/ResourcesCliTest.cpp | 15 +-- tools/src/ResourceFilter.cpp | 30 +---- 4 files changed, 9 insertions(+), 234 deletions(-) diff --git a/tests/src/CliTestFixture.cpp b/tests/src/CliTestFixture.cpp index 15a1b23..6f1b0e1 100644 --- a/tests/src/CliTestFixture.cpp +++ b/tests/src/CliTestFixture.cpp @@ -13,12 +13,12 @@ int CliTestFixture::RunCli( std::vector& arguments, std::string& ou arguments.insert( arguments.begin(), CARBON_RESOURCES_CLI_EXE_FULLPATH ); - // TODO: Add debug information on parameters - std::cout << "RunCli arguments: " << std::endl; + std::cout << "--- RunCli() arguments: ---" << std::endl; for( const auto& arg : arguments ) { std::cout << " " << arg << std::endl; } + std::cout << "---------------------------" << std::endl; TinyProcessLib::Process process1a( arguments, workingDirectory, diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index eb293ab..c06a2ff 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -960,7 +960,6 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Invalid_RespathMissing ) ResourceTools::FilterPrefixMap defaultPrefixMap( defaultParentPrefixMapStr ); - // TODO: Should change code to throw defined error code/type try { ResourceTools::FilterNamedSection namedSection( sectionName, filter, "", resfile, defaultPrefixMap ); @@ -1872,47 +1871,8 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load_validSimpleExample1_ini_usingRel }; ASSERT_EQ( resourceFilter.HasFilters(), true ); - - // TODO: Remove debug output once confirmed working on macOS (TeamCity) - //-- BEGIN - Debug resourceFilter contents vs expected values - std::cout << "------ Debug info (begin) -----" << std::endl; - std::cout << "ResourceFilter FullResolvedPathMap contents:" << std::endl; - for( const auto& [fullPath, filter] : resourceFilter.GetFullResolvedPathMap() ) - { - std::cout << "ResourceFilter Path: " << std::endl; - std::cout << " - path (relative string): " << fullPath << std::endl; - std::cout << " - path (relative generic_string): " << std::filesystem::path( fullPath ).generic_string() << std::endl; - std::cout << " - path (absolute generic_string): " << std::filesystem::absolute( std::filesystem::path( fullPath ) ).generic_string() << std::endl; - std::cout << " - path (absolute normal string): " << std::filesystem::absolute( std::filesystem::path( fullPath ).lexically_normal() ).generic_string() << std::endl; - std::cout << " Includes: "; - for( const auto& include : filter.GetIncludeFilter() ) - { - std::cout << include << " "; - } - std::cout << std::endl; - std::cout << " Excludes: "; - for( const auto& exclude : filter.GetExcludeFilter() ) - { - std::cout << exclude << " "; - } - std::cout << std::endl; - } - std::cout << "- - - - - - - - -" << std::endl; - std::cout << "Expected valid resolved relative paths:" << std::endl; for( const auto& resolvedRelativePath : validResolvedRelativePaths ) { - std::cout << " - path (relative string): " << resolvedRelativePath.string() << std::endl; - std::cout << " - path (relative generic_string): " << resolvedRelativePath.generic_string() << std::endl; - std::cout << " - path (absolute generic_string): " << std::filesystem::absolute( resolvedRelativePath ).generic_string() << std::endl; - std::cout << " - path (absolute normal string): " << std::filesystem::absolute( resolvedRelativePath.lexically_normal() ).generic_string() << std::endl; - } - std::cout << "------ Debug info (end) -----" << std::endl; - //-- END - Debug resourceFilter contents vs expected values - - for( const auto& resolvedRelativePath : validResolvedRelativePaths ) - { - // TODO: Remove debug output once confirmed working on macOS (TeamCity) - std::cout << "About to call resourceFilter.ShouldInclude() for (generic_string): " << resolvedRelativePath.generic_string() << std::endl; ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativePath ), true ) << "Should have included relative path: " << resolvedRelativePath.generic_string(); } @@ -1955,46 +1915,8 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load_validSimpleExample1_ini_usingAbs }; ASSERT_EQ( resourceFilter.HasFilters(), true ); - - // TODO: Remove debug output once confirmed working on macOS (TeamCity) - //-- BEGIN - Debug resourceFilter contents vs expected values - std::cout << "------ Debug info (begin) -----" << std::endl; - std::cout << "ResourceFilter FullResolvedPathMap contents:" << std::endl; - for( const auto& [fullPath, filter] : resourceFilter.GetFullResolvedPathMap() ) - { - std::cout << "ResourceFilter Path: " << std::endl; - std::cout << " - path (relative string): " << fullPath << std::endl; - std::cout << " - path (relative generic_string): " << std::filesystem::path( fullPath ).generic_string() << std::endl; - std::cout << " - path (absolute generic_string): " << std::filesystem::absolute( std::filesystem::path( fullPath ) ).generic_string() << std::endl; - std::cout << " - path (absolute normal string): " << std::filesystem::absolute( std::filesystem::path( fullPath ).lexically_normal() ).generic_string() << std::endl; - std::cout << " Includes: "; - for( const auto& include : filter.GetIncludeFilter() ) - { - std::cout << include << " "; - } - std::cout << std::endl; - std::cout << " Excludes: "; - for( const auto& exclude : filter.GetExcludeFilter() ) - { - std::cout << exclude << " "; - } - std::cout << std::endl; - } - std::cout << "- - - - - - - - -" << std::endl; - std::cout << "Expected valid resolved absolute paths:" << std::endl; - for( const auto& resolvedAbsolutePath : validResolvedAbsolutePaths ) - { - std::cout << " - path (absolute string): " << resolvedAbsolutePath.string() << std::endl; - std::cout << " - path (absolute generic_string): " << resolvedAbsolutePath.generic_string() << std::endl; - std::cout << " - path (absolute normal string): " << resolvedAbsolutePath.lexically_normal().generic_string() << std::endl; - } - std::cout << "------ Debug info (end) -----" << std::endl; - //-- END - Debug resourceFilter contents vs expected values - for( const auto& resolvedAbsPath : validResolvedAbsolutePaths ) { - // TODO: Remove debug output once confirmed working on macOS (TeamCity) - std::cout << "About to call resourceFilter.ShouldInclude() for (generic_string): " << resolvedAbsPath.generic_string() << std::endl; ASSERT_EQ( resourceFilter.ShouldInclude( resolvedAbsPath ), true ) << "Should have included absolute path: " << resolvedAbsPath.generic_string(); } @@ -2047,47 +1969,8 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load_validComplexExample1_ini_usingRe }; ASSERT_EQ( resourceFilter.HasFilters(), true ); - - // TODO: Remove debug output once confirmed working on macOS (TeamCity) - //-- BEGIN - Debug resourceFilter contents vs expected values - std::cout << "------ Debug info (begin) -----" << std::endl; - std::cout << "ResourceFilter FullResolvedPathMap contents:" << std::endl; - for( const auto& [fullPath, filter] : resourceFilter.GetFullResolvedPathMap() ) - { - std::cout << "ResourceFilter Path: " << std::endl; - std::cout << " - path (relative string): " << fullPath << std::endl; - std::cout << " - path (relative generic_string): " << std::filesystem::path( fullPath ).generic_string() << std::endl; - std::cout << " - path (absolute generic_string): " << std::filesystem::absolute( std::filesystem::path( fullPath ) ).generic_string() << std::endl; - std::cout << " - path (absolute normal string): " << std::filesystem::absolute( std::filesystem::path( fullPath ).lexically_normal() ).generic_string() << std::endl; - std::cout << " Includes: "; - for( const auto& include : filter.GetIncludeFilter() ) - { - std::cout << include << " "; - } - std::cout << std::endl; - std::cout << " Excludes: "; - for( const auto& exclude : filter.GetExcludeFilter() ) - { - std::cout << exclude << " "; - } - std::cout << std::endl; - } - std::cout << "- - - - - - - - -" << std::endl; - std::cout << "Expected valid resolved relative paths:" << std::endl; for( const auto& resolvedRelativePath : validResolvedRelativePaths ) { - std::cout << " - path (relative string): " << resolvedRelativePath.string() << std::endl; - std::cout << " - path (relative generic_string): " << resolvedRelativePath.generic_string() << std::endl; - std::cout << " - path (absolute generic_string): " << std::filesystem::absolute( resolvedRelativePath ).generic_string() << std::endl; - std::cout << " - path (absolute normal string): " << std::filesystem::absolute( resolvedRelativePath.lexically_normal() ).generic_string() << std::endl; - } - std::cout << "------ Debug info (end) -----" << std::endl; - //-- END - Debug resourceFilter contents vs expected values - - for( const auto& resolvedRelativePath : validResolvedRelativePaths ) - { - // TODO: Remove debug output once confirmed working on macOS (TeamCity) - std::cout << "About to call resourceFilter.ShouldInclude() for (generic_string): " << resolvedRelativePath.generic_string() << std::endl; ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativePath ), true ) << "Should have included relative path: " << resolvedRelativePath.generic_string(); } @@ -2195,46 +2078,8 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load_validComplexExample1_ini_usingAb }; ASSERT_EQ( resourceFilter.HasFilters(), true ); - - // TODO: Remove debug output once confirmed working on macOS (TeamCity) - //-- BEGIN - Debug resourceFilter contents vs expected values - std::cout << "------ Debug info (begin) -----" << std::endl; - std::cout << "ResourceFilter FullResolvedPathMap contents:" << std::endl; - for( const auto& [fullPath, filter] : resourceFilter.GetFullResolvedPathMap() ) - { - std::cout << "ResourceFilter Path: " << std::endl; - std::cout << " - path (relative string): " << fullPath << std::endl; - std::cout << " - path (relative generic_string): " << std::filesystem::path( fullPath ).generic_string() << std::endl; - std::cout << " - path (absolute generic_string): " << std::filesystem::absolute( std::filesystem::path( fullPath ) ).generic_string() << std::endl; - std::cout << " - path (absolute normal string): " << std::filesystem::absolute( std::filesystem::path( fullPath ).lexically_normal() ).generic_string() << std::endl; - std::cout << " Includes: "; - for( const auto& include : filter.GetIncludeFilter() ) - { - std::cout << include << " "; - } - std::cout << std::endl; - std::cout << " Excludes: "; - for( const auto& exclude : filter.GetExcludeFilter() ) - { - std::cout << exclude << " "; - } - std::cout << std::endl; - } - std::cout << "- - - - - - - - -" << std::endl; - std::cout << "Expected valid resolved absolute paths:" << std::endl; - for( const auto& resolvedAbsolutePath : validResolvedRelativePaths ) - { - std::cout << " - path (absolute string): " << resolvedAbsolutePath.string() << std::endl; - std::cout << " - path (absolute generic_string): " << resolvedAbsolutePath.generic_string() << std::endl; - std::cout << " - path (absolute normal string): " << resolvedAbsolutePath.lexically_normal().generic_string() << std::endl; - } - std::cout << "------ Debug info (end) -----" << std::endl; - //-- END - Debug resourceFilter contents vs expected values - for( const auto& resolvedAbsPath : validResolvedRelativePaths ) { - // TODO: Remove debug output once confirmed working on macOS (TeamCity) - std::cout << "About to call resourceFilter.ShouldInclude() for (generic_string): " << resolvedAbsPath.generic_string() << std::endl; ASSERT_EQ( resourceFilter.ShouldInclude( resolvedAbsPath ), true ) << "Should have included relative path: " << resolvedAbsPath.generic_string(); } @@ -2348,47 +2193,8 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load2iniFiles_validComplexExample1_an }; ASSERT_EQ( resourceFilter.HasFilters(), true ); - - // TODO: Remove debug output once confirmed working on macOS (TeamCity) - //-- BEGIN - Debug resourceFilter contents vs expected values - std::cout << "------ Debug info (begin) -----" << std::endl; - std::cout << "ResourceFilter FullResolvedPathMap contents:" << std::endl; - for( const auto& [fullPath, filter] : resourceFilter.GetFullResolvedPathMap() ) - { - std::cout << "ResourceFilter Path: " << std::endl; - std::cout << " - path (relative string): " << fullPath << std::endl; - std::cout << " - path (relative generic_string): " << std::filesystem::path( fullPath ).generic_string() << std::endl; - std::cout << " - path (absolute generic_string): " << std::filesystem::absolute( std::filesystem::path( fullPath ) ).generic_string() << std::endl; - std::cout << " - path (absolute normal string): " << std::filesystem::absolute( std::filesystem::path( fullPath ).lexically_normal() ).generic_string() << std::endl; - std::cout << " Includes: "; - for( const auto& include : filter.GetIncludeFilter() ) - { - std::cout << include << " "; - } - std::cout << std::endl; - std::cout << " Excludes: "; - for( const auto& exclude : filter.GetExcludeFilter() ) - { - std::cout << exclude << " "; - } - std::cout << std::endl; - } - std::cout << "- - - - - - - - -" << std::endl; - std::cout << "Expected valid resolved relative paths:" << std::endl; - for( const auto& resolvedRelativePath : validResolvedRelativePaths ) - { - std::cout << " - path (relative string): " << resolvedRelativePath.string() << std::endl; - std::cout << " - path (relative generic_string): " << resolvedRelativePath.generic_string() << std::endl; - std::cout << " - path (absolute generic_string): " << std::filesystem::absolute( resolvedRelativePath ).generic_string() << std::endl; - std::cout << " - path (absolute normal string): " << std::filesystem::absolute( resolvedRelativePath.lexically_normal() ).generic_string() << std::endl; - } - std::cout << "------ Debug info (end) -----" << std::endl; - //-- END - Debug resourceFilter contents vs expected values - for( const auto& resolvedRelativePath : validResolvedRelativePaths ) { - // TODO: Remove debug output once confirmed working on macOS (TeamCity) - std::cout << "About to call resourceFilter.ShouldInclude() for (generic_string): " << resolvedRelativePath.generic_string() << std::endl; ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativePath ), true ) << "Should have included relative path: " << resolvedRelativePath.generic_string(); } diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index 9942445..577fc41 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -352,10 +352,9 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) arguments.push_back( outputFilePath.string() ); int res = RunCli( arguments, output, errorOutput, TEST_DATA_BASE_PATH ); - std::cout << "Test RunCli output: " << std::endl; - std::cout << "----------------------------------" << std::endl; + std::cout << "--- RunCli() output: ---" << std::endl; std::cout << output << std::endl; - std::cout << "----------------------------------" << std::endl; + std::cout << "------------------------" << std::endl; ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; @@ -403,10 +402,9 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) arguments.push_back( outputFilePath.string() ); int res = RunCli( arguments, output, errorOutput, TEST_DATA_BASE_PATH ); - std::cout << "Test RunCli output: " << std::endl; - std::cout << "----------------------------------" << std::endl; + std::cout << "--- RunCli() output: ---" << std::endl; std::cout << output << std::endl; - std::cout << "----------------------------------" << std::endl; + std::cout << "------------------------" << std::endl; ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; @@ -460,10 +458,9 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 arguments.push_back( outputFilePath.string() ); int res = RunCli( arguments, output, errorOutput, TEST_DATA_BASE_PATH ); - std::cout << "Test RunCli output: " << std::endl; - std::cout << "----------------------------------" << std::endl; + std::cout << "--- RunCli() output: ---" << std::endl; std::cout << output << std::endl; - std::cout << "----------------------------------" << std::endl; + std::cout << "------------------------" << std::endl; ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; diff --git a/tools/src/ResourceFilter.cpp b/tools/src/ResourceFilter.cpp index 336492a..4b26fcd 100644 --- a/tools/src/ResourceFilter.cpp +++ b/tools/src/ResourceFilter.cpp @@ -3,7 +3,6 @@ #include #include #include -#include // TODO: Added for debugging purposes, remove this namespace ResourceTools { @@ -83,28 +82,16 @@ bool ResourceFilter::ShouldInclude( const std::filesystem::path& inFilePath ) int bestIncludePriority = std::numeric_limits::max(); int bestExcludePriority = std::numeric_limits::max(); - // TODO: Add debugging info - std::cout << " - inFilePath: " << inFilePath << std::endl; - std::cout << " - inFilePathAbs: " << inFilePathAbs.generic_string() << std::endl; - std::cout << " - inFileNormalAbsPathStr: " << inFileNormalAbsPathStr << std::endl; - // Get the full resolved path map and iterate through it (contains relative paths) const auto& resolvedPathMap = GetFullResolvedPathMap(); for( const auto& [resolvedRelativePathStr, filter] : resolvedPathMap ) { - // Make sure to work with absolute paths for comparison + // Use normalized absolute paths for comparison std::filesystem::path resolvedRelativePath( resolvedRelativePathStr ); std::filesystem::path resolvedPathAbs = std::filesystem::absolute( resolvedRelativePath ); std::string resolvedNormalAbsPathStr = NormalizePath( resolvedPathAbs.generic_string() ); - // TODO: Add debugging info - std::cout << " Checking resolvedRelativePathStr:" << std::endl; - std::cout << " - resolvedRelativePathStr: " << resolvedRelativePathStr << std::endl; - std::cout << " - resolvedRelativePath: " << resolvedRelativePath.generic_string() << std::endl; - std::cout << " - resolvedPathAbs: " << resolvedPathAbs.generic_string() << std::endl; - std::cout << " - resolvedNormalAbsPathStr: " << resolvedNormalAbsPathStr << std::endl; - if( resolvedNormalAbsPathStr == inFileNormalAbsPathStr ) { // If there is an exact match on the full filename path, this means highest priority and @@ -126,17 +113,11 @@ bool ResourceFilter::ShouldInclude( const std::filesystem::path& inFilePath ) } resolvedNormalAbsPathStr += "..."; } - - // TODO: Add debugging info - std::cout << " - Adjusted resolvedNormalAbsPathStr for ... : " << resolvedNormalAbsPathStr << std::endl; } // Perform Wildcard matching on the normalized absolute paths if( !WildcardMatch( resolvedNormalAbsPathStr, inFileNormalAbsPathStr ) ) { - // TODO: Add debugging info - std::cout << " No WildcardMatch for pattern: " << resolvedNormalAbsPathStr << " checkStr: " << inFileNormalAbsPathStr << std::endl; - // There was NO wildcard match on paths, ignore this resolvedRelativePath entry continue; } @@ -146,11 +127,6 @@ bool ResourceFilter::ShouldInclude( const std::filesystem::path& inFilePath ) inFilePathAbs = std::filesystem::absolute( inFileNormalAbsPathStr ); resolvedPathAbs = std::filesystem::absolute( resolvedNormalAbsPathStr ); - // TODO: Add debugging info - std::cout << " WildcardMatch succeeded!" << std::endl; - std::cout << " - inFilePathAbs (normalized): " << inFilePathAbs.generic_string() << std::endl; - std::cout << " - resolvedPathAbs (normalized): " << resolvedPathAbs.generic_string() << std::endl; - auto inFileIt = inFilePathAbs.begin(); auto resolvedIt = resolvedPathAbs.begin(); while( inFileIt != inFilePathAbs.end() && resolvedIt != resolvedPathAbs.end() && *inFileIt == *resolvedIt ) @@ -189,10 +165,6 @@ bool ResourceFilter::ShouldInclude( const std::filesystem::path& inFilePath ) } } - // TODO: Add debugging info - std::cout << " - bestIncludePriority: " << bestIncludePriority << std::endl; - std::cout << " - bestExcludePriority: " << bestExcludePriority << std::endl; - // Apply priority rules: if( bestIncludePriority == std::numeric_limits::max() ) { From d203192b5a743ef3efd4a1bccd3a2277cee42804 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 4 Feb 2026 23:36:02 +0000 Subject: [PATCH 074/124] Add pragma once to Filter header files and CamelCase define macros --- tools/include/FilterDefaultSection.h | 7 ++++--- tools/include/FilterNamedSection.h | 7 ++++--- tools/include/FilterPrefixMapEntry.h | 7 ++++--- tools/include/FilterPrefixmap.h | 7 ++++--- tools/include/FilterResourceFile.h | 7 ++++--- tools/include/FilterResourceFilter.h | 7 ++++--- tools/include/FilterResourcePathFile.h | 7 ++++--- tools/include/FilterResourcePathFileEntry.h | 7 ++++--- tools/include/ResourceFilter.h | 7 ++++--- 9 files changed, 36 insertions(+), 27 deletions(-) diff --git a/tools/include/FilterDefaultSection.h b/tools/include/FilterDefaultSection.h index 3000b8d..2d8702b 100644 --- a/tools/include/FilterDefaultSection.h +++ b/tools/include/FilterDefaultSection.h @@ -1,7 +1,8 @@ // Copyright © 2025 CCP ehf. -#ifndef FILTERDEFAULTSECTION_H -#define FILTERDEFAULTSECTION_H +#pragma once +#ifndef FilterDefaultSection_H +#define FilterDefaultSection_H #include #include @@ -24,4 +25,4 @@ class FilterDefaultSection } -#endif // FILTERDEFAULTSECTION_H +#endif // FilterDefaultSection_H diff --git a/tools/include/FilterNamedSection.h b/tools/include/FilterNamedSection.h index 41f51e1..edf891d 100644 --- a/tools/include/FilterNamedSection.h +++ b/tools/include/FilterNamedSection.h @@ -1,7 +1,8 @@ // Copyright © 2025 CCP ehf. -#ifndef FILTERNAMEDSECTION_H -#define FILTERNAMEDSECTION_H +#pragma once +#ifndef FilterNamedSection_H +#define FilterNamedSection_H #include #include @@ -47,4 +48,4 @@ class FilterNamedSection } -#endif // FILTERNAMEDSECTION_H +#endif // FilterNamedSection_H diff --git a/tools/include/FilterPrefixMapEntry.h b/tools/include/FilterPrefixMapEntry.h index ee04cba..d579012 100644 --- a/tools/include/FilterPrefixMapEntry.h +++ b/tools/include/FilterPrefixMapEntry.h @@ -1,7 +1,8 @@ // Copyright © 2025 CCP ehf. -#ifndef FILTERPREFIXMAPENTRY_H -#define FILTERPREFIXMAPENTRY_H +#pragma once +#ifndef FilterPrefixMapEntry_H +#define FilterPrefixMapEntry_H #include #include @@ -31,4 +32,4 @@ class FilterPrefixMapEntry } -#endif // FILTERPREFIXMAPENTRY_H +#endif // FilterPrefixMapEntry_H diff --git a/tools/include/FilterPrefixmap.h b/tools/include/FilterPrefixmap.h index bd066c9..c22c613 100644 --- a/tools/include/FilterPrefixmap.h +++ b/tools/include/FilterPrefixmap.h @@ -1,7 +1,8 @@ // Copyright © 2025 CCP ehf. -#ifndef FILTERPREFIXMAP_H -#define FILTERPREFIXMAP_H +#pragma once +#ifndef FilterPrefixmap_H +#define FilterPrefixmap_H #include #include @@ -30,4 +31,4 @@ class FilterPrefixMap } -#endif // FILTERPREFIXMAP_H +#endif // FilterPrefixmap_H diff --git a/tools/include/FilterResourceFile.h b/tools/include/FilterResourceFile.h index 03ddbba..73e32df 100644 --- a/tools/include/FilterResourceFile.h +++ b/tools/include/FilterResourceFile.h @@ -1,7 +1,8 @@ // Copyright © 2025 CCP ehf. -#ifndef FILTERRESOURCEFILE_H -#define FILTERRESOURCEFILE_H +#pragma once +#ifndef FilterResourceFile_H +#define FilterResourceFile_H #include #include @@ -35,4 +36,4 @@ class FilterResourceFile } -#endif // FILTERRESOURCEFILE_H +#endif // FilterResourceFile_H diff --git a/tools/include/FilterResourceFilter.h b/tools/include/FilterResourceFilter.h index cf31f81..2f4760e 100644 --- a/tools/include/FilterResourceFilter.h +++ b/tools/include/FilterResourceFilter.h @@ -1,7 +1,8 @@ // Copyright © 2025 CCP ehf. -#ifndef FILTERRESOURCEFILTER_H -#define FILTERRESOURCEFILTER_H +#pragma once +#ifndef FilterResourceFilter_H +#define FilterResourceFilter_H #include #include @@ -42,4 +43,4 @@ class FilterResourceFilter } -#endif // FILTERRESOURCEFILTER_H +#endif // FilterResourceFilter_H diff --git a/tools/include/FilterResourcePathFile.h b/tools/include/FilterResourcePathFile.h index 1c00b02..fbcc547 100644 --- a/tools/include/FilterResourcePathFile.h +++ b/tools/include/FilterResourcePathFile.h @@ -1,7 +1,8 @@ // Copyright © 2025 CCP ehf. -#ifndef FILTERRESOURCEPATHFILE_H -#define FILTERRESOURCEPATHFILE_H +#pragma once +#ifndef FilterResourcePathFile_H +#define FilterResourcePathFile_H #include #include @@ -39,4 +40,4 @@ class FilterResourcePathFile } -#endif // FILTERRESOURCEPATHFILE_H +#endif // FilterResourcePathFile_H diff --git a/tools/include/FilterResourcePathFileEntry.h b/tools/include/FilterResourcePathFileEntry.h index 993fa0a..e480b22 100644 --- a/tools/include/FilterResourcePathFileEntry.h +++ b/tools/include/FilterResourcePathFileEntry.h @@ -1,7 +1,8 @@ // Copyright © 2025 CCP ehf. -#ifndef FILTERRESOURCEPATHFILEENTRY_H -#define FILTERRESOURCEPATHFILEENTRY_H +#pragma once +#ifndef FilterResourcePathFileEntry_H +#define FilterResourcePathFileEntry_H #include #include @@ -42,4 +43,4 @@ class FilterResourcePathFileEntry } -#endif //FILTERRESOURCEPATHFILEENTRY_H +#endif //FilterResourcePathFileEntry_H diff --git a/tools/include/ResourceFilter.h b/tools/include/ResourceFilter.h index 0790902..9b02a9f 100644 --- a/tools/include/ResourceFilter.h +++ b/tools/include/ResourceFilter.h @@ -1,7 +1,8 @@ // Copyright © 2026 CCP ehf. -#ifndef RESOURCEFILTER_H -#define RESOURCEFILTER_H +#pragma once +#ifndef ResourceFilter_H +#define ResourceFilter_H #include #include @@ -49,4 +50,4 @@ class ResourceFilter } -#endif // RESOURCEFILTER_H +#endif // ResourceFilter_H From fa65603bf5e5c90eb79345cf61ec5674fbfaed18 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 5 Feb 2026 00:48:49 +0000 Subject: [PATCH 075/124] Organize/order imports for Filter header and cpp files --- tests/src/ResourceFilterTest.cpp | 24 +++++++++++++-------- tests/src/ResourceFilterTest.h | 1 - tools/include/FilterDefaultSection.h | 3 +-- tools/include/FilterNamedSection.h | 10 ++++----- tools/include/FilterPrefixMapEntry.h | 2 +- tools/include/FilterPrefixmap.h | 6 +++--- tools/include/FilterResourceFile.h | 5 +++-- tools/include/FilterResourcePathFile.h | 5 +++-- tools/include/FilterResourcePathFileEntry.h | 6 ++++-- tools/include/ResourceFilter.h | 5 +++-- tools/src/FilterDefaultSection.cpp | 3 +-- tools/src/FilterNamedSection.cpp | 3 ++- tools/src/FilterPrefixMapEntry.cpp | 3 ++- tools/src/FilterPrefixmap.cpp | 6 +++--- tools/src/FilterResourceFile.cpp | 6 ++++-- tools/src/FilterResourceFilter.cpp | 5 +++-- tools/src/FilterResourcePathFile.cpp | 7 +++--- tools/src/FilterResourcePathFileEntry.cpp | 3 ++- tools/src/ResourceFilter.cpp | 4 ++-- 19 files changed, 61 insertions(+), 46 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index c06a2ff..9513224 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -2,15 +2,21 @@ #include "ResourceFilterTest.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include + +#include "INIReader.h" + +#include "FilterDefaultSection.h" +#include "FilterNamedSection.h" +#include "FilterResourceFilter.h" +#include "FilterPrefixmap.h" +#include "FilterPrefixMapEntry.h" +#include "FilterResourceFile.h" +#include "FilterResourcePathFile.h" +#include "ResourceFilter.h" TEST_F( ResourceFilterTest, Example1IniParsing ) { diff --git a/tests/src/ResourceFilterTest.h b/tests/src/ResourceFilterTest.h index 66694a9..12a635c 100644 --- a/tests/src/ResourceFilterTest.h +++ b/tests/src/ResourceFilterTest.h @@ -5,7 +5,6 @@ #define ResourceFilterTest_H #include "ResourcesTestFixture.h" -#include // Inherit from ResourcesTestFixture to gain access to file and directory helper functions class ResourceFilterTest : public ResourcesTestFixture diff --git a/tools/include/FilterDefaultSection.h b/tools/include/FilterDefaultSection.h index 2d8702b..eebe924 100644 --- a/tools/include/FilterDefaultSection.h +++ b/tools/include/FilterDefaultSection.h @@ -4,8 +4,7 @@ #ifndef FilterDefaultSection_H #define FilterDefaultSection_H -#include -#include +#include "FilterPrefixmap.h" namespace ResourceTools { diff --git a/tools/include/FilterNamedSection.h b/tools/include/FilterNamedSection.h index edf891d..560610c 100644 --- a/tools/include/FilterNamedSection.h +++ b/tools/include/FilterNamedSection.h @@ -4,13 +4,13 @@ #ifndef FilterNamedSection_H #define FilterNamedSection_H -#include -#include #include #include -#include -#include -#include +#include + +#include "FilterPrefixmap.h" +#include "FilterResourceFilter.h" +#include "FilterResourcePathFile.h" namespace ResourceTools { diff --git a/tools/include/FilterPrefixMapEntry.h b/tools/include/FilterPrefixMapEntry.h index d579012..80e53a0 100644 --- a/tools/include/FilterPrefixMapEntry.h +++ b/tools/include/FilterPrefixMapEntry.h @@ -4,8 +4,8 @@ #ifndef FilterPrefixMapEntry_H #define FilterPrefixMapEntry_H -#include #include +#include namespace ResourceTools { diff --git a/tools/include/FilterPrefixmap.h b/tools/include/FilterPrefixmap.h index c22c613..2213e78 100644 --- a/tools/include/FilterPrefixmap.h +++ b/tools/include/FilterPrefixmap.h @@ -4,10 +4,10 @@ #ifndef FilterPrefixmap_H #define FilterPrefixmap_H -#include #include -#include -#include +#include + +#include "FilterPrefixMapEntry.h" namespace ResourceTools { diff --git a/tools/include/FilterResourceFile.h b/tools/include/FilterResourceFile.h index 73e32df..21bc4bd 100644 --- a/tools/include/FilterResourceFile.h +++ b/tools/include/FilterResourceFile.h @@ -6,8 +6,9 @@ #include #include -#include -#include + +#include "FilterDefaultSection.h" +#include "FilterNamedSection.h" namespace ResourceTools { diff --git a/tools/include/FilterResourcePathFile.h b/tools/include/FilterResourcePathFile.h index fbcc547..bbd0c44 100644 --- a/tools/include/FilterResourcePathFile.h +++ b/tools/include/FilterResourcePathFile.h @@ -5,8 +5,9 @@ #define FilterResourcePathFile_H #include -#include -#include + +#include "FilterPrefixmap.h" +#include "FilterResourceFilter.h" namespace ResourceTools { diff --git a/tools/include/FilterResourcePathFileEntry.h b/tools/include/FilterResourcePathFileEntry.h index e480b22..5bb645c 100644 --- a/tools/include/FilterResourcePathFileEntry.h +++ b/tools/include/FilterResourcePathFileEntry.h @@ -5,8 +5,10 @@ #define FilterResourcePathFileEntry_H #include -#include -#include +#include + +#include "FilterPrefixmap.h" +#include "FilterResourceFilter.h" namespace ResourceTools { diff --git a/tools/include/ResourceFilter.h b/tools/include/ResourceFilter.h index 9b02a9f..6f2ae0f 100644 --- a/tools/include/ResourceFilter.h +++ b/tools/include/ResourceFilter.h @@ -6,8 +6,9 @@ #include #include -#include -#include + +#include "FilterResourceFile.h" +#include "FilterResourceFilter.h" namespace ResourceTools { diff --git a/tools/src/FilterDefaultSection.cpp b/tools/src/FilterDefaultSection.cpp index 9e10caa..e0cbf6a 100644 --- a/tools/src/FilterDefaultSection.cpp +++ b/tools/src/FilterDefaultSection.cpp @@ -1,7 +1,6 @@ // Copyright © 2025 CCP ehf. -#include -#include +#include "FilterDefaultSection.h" namespace ResourceTools { diff --git a/tools/src/FilterNamedSection.cpp b/tools/src/FilterNamedSection.cpp index 0d7ec67..d205e60 100644 --- a/tools/src/FilterNamedSection.cpp +++ b/tools/src/FilterNamedSection.cpp @@ -1,7 +1,8 @@ // Copyright © 2025 CCP ehf. +#include "FilterNamedSection.h" + #include -#include namespace ResourceTools { diff --git a/tools/src/FilterPrefixMapEntry.cpp b/tools/src/FilterPrefixMapEntry.cpp index 351f8a2..8a11588 100644 --- a/tools/src/FilterPrefixMapEntry.cpp +++ b/tools/src/FilterPrefixMapEntry.cpp @@ -1,7 +1,8 @@ // Copyright © 2025 CCP ehf. +#include "FilterPrefixMapEntry.h" + #include -#include namespace ResourceTools { diff --git a/tools/src/FilterPrefixmap.cpp b/tools/src/FilterPrefixmap.cpp index 7a70b92..f6bba17 100644 --- a/tools/src/FilterPrefixmap.cpp +++ b/tools/src/FilterPrefixmap.cpp @@ -1,9 +1,9 @@ // Copyright © 2025 CCP ehf. -#include +#include "FilterPrefixmap.h" + #include -#include -#include +#include namespace ResourceTools { diff --git a/tools/src/FilterResourceFile.cpp b/tools/src/FilterResourceFile.cpp index f82bb56..6d6cb13 100644 --- a/tools/src/FilterResourceFile.cpp +++ b/tools/src/FilterResourceFile.cpp @@ -1,8 +1,10 @@ // Copyright © 2025 CCP ehf. +#include "FilterResourceFile.h" + #include -#include -#include + +#include "INIReader.h" namespace ResourceTools { diff --git a/tools/src/FilterResourceFilter.cpp b/tools/src/FilterResourceFilter.cpp index 0afe944..1c603ad 100644 --- a/tools/src/FilterResourceFilter.cpp +++ b/tools/src/FilterResourceFilter.cpp @@ -1,9 +1,10 @@ // Copyright © 2025 CCP ehf. -#include +#include "FilterResourceFilter.h" + #include +#include #include -#include namespace ResourceTools { diff --git a/tools/src/FilterResourcePathFile.cpp b/tools/src/FilterResourcePathFile.cpp index 6fc709d..c78da88 100644 --- a/tools/src/FilterResourcePathFile.cpp +++ b/tools/src/FilterResourcePathFile.cpp @@ -1,9 +1,10 @@ // Copyright © 2025 CCP ehf. -#include +#include "FilterResourcePathFile.h" + #include -#include -#include + +#include "FilterResourcePathFileEntry.h" namespace ResourceTools { diff --git a/tools/src/FilterResourcePathFileEntry.cpp b/tools/src/FilterResourcePathFileEntry.cpp index d82c4c4..0d2e4e4 100644 --- a/tools/src/FilterResourcePathFileEntry.cpp +++ b/tools/src/FilterResourcePathFileEntry.cpp @@ -1,8 +1,9 @@ // Copyright © 2025 CCP ehf. +#include "FilterResourcePathFileEntry.h" + #include #include -#include namespace ResourceTools { diff --git a/tools/src/ResourceFilter.cpp b/tools/src/ResourceFilter.cpp index 4b26fcd..7a7f66e 100644 --- a/tools/src/ResourceFilter.cpp +++ b/tools/src/ResourceFilter.cpp @@ -1,7 +1,7 @@ // Copyright © 2026 CCP ehf. -#include -#include +#include "ResourceFilter.h" + #include namespace ResourceTools From 9951e5c21456ae5f601a769b17947cb6bd569e2f Mon Sep 17 00:00:00 2001 From: ccptoebeans <> Date: Thu, 5 Feb 2026 09:37:40 +0000 Subject: [PATCH 076/124] Add macOS gold files for comparison in Filter CLI tests --- ...singFilter_validComplexExample1_macOS.yaml | 76 ++++++++++++++++ ...r_validSimpleAndComplexExample1_macOS.yaml | 90 +++++++++++++++++++ ...UsingFilter_validSimpleExample1_macOS.yaml | 20 +++++ 3 files changed, 186 insertions(+) create mode 100644 tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_macOS.yaml create mode 100644 tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_macOS.yaml create mode 100644 tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_macOS.yaml diff --git a/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_macOS.yaml b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_macOS.yaml new file mode 100644 index 0000000..2a7e0a8 --- /dev/null +++ b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_macOS.yaml @@ -0,0 +1,76 @@ +Version: 0.1.0 +Type: ResourceGroup +NumberOfResources: 10 +TotalResourcesSizeCompressed: 24573 +TotalResourcesSizeUnCompressed: 107508 +Resources: + - RelativePath: PatchWithInputChunk/resFileIndexShort_build_next.txt + Type: Resource + Location: 1f/1f23be4b8bf6d2ae_4d398902b75611f7ae19903e6b461514 + Checksum: 4d398902b75611f7ae19903e6b461514 + UncompressedSize: 612 + CompressedSize: 324 + BinaryOperation: 33188 + - RelativePath: PatchWithInputChunk/NextBuildResources/testResource2.txt + Type: Resource + Location: 82/8264e4640cf94b94_271c8036e4eae5515053e924a0a39e0f + Checksum: 271c8036e4eae5515053e924a0a39e0f + UncompressedSize: 29 + CompressedSize: 47 + BinaryOperation: 33188 + - RelativePath: PatchWithInputChunk/NextBuildResources/videoCardCategories.yaml + Type: Resource + Location: 02/02b2b2627ca28b62_90d25dac9f4ed3233c5fda72bee3dfe6 + Checksum: 90d25dac9f4ed3233c5fda72bee3dfe6 + UncompressedSize: 32815 + CompressedSize: 5003 + BinaryOperation: 33188 + - RelativePath: PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt + Type: Resource + Location: dc/dca8056adced9237_0fb2bed9d164ad014bcb060b95df7ba1 + Checksum: 0fb2bed9d164ad014bcb060b95df7ba1 + UncompressedSize: 9425 + CompressedSize: 3409 + BinaryOperation: 33188 + - RelativePath: PatchWithInputChunk/resFileIndexShort_build_previous.txt + Type: Resource + Location: 32/326b0449af10b716_849821e0c98e37de4edc5f7156cf5887 + Checksum: 849821e0c98e37de4edc5f7156cf5887 + UncompressedSize: 611 + CompressedSize: 299 + BinaryOperation: 33188 + - RelativePath: PatchWithInputChunk/PatchResourceGroup_previousBuild_latestBuild.yaml + Type: Resource + Location: 44/446d5f683db0d467_b7431ad6d859bfd67a4c3fae337b0b6e + Checksum: b7431ad6d859bfd67a4c3fae337b0b6e + UncompressedSize: 3902 + CompressedSize: 765 + BinaryOperation: 33188 + - RelativePath: PatchWithInputChunk/PreviousBuildResources/introMovie.txt + Type: Resource + Location: fd/fd2a47b3d5faa494_e9fadf6f2d386a0a0786bc863f20fa34 + Checksum: e9fadf6f2d386a0a0786bc863f20fa34 + UncompressedSize: 9091 + CompressedSize: 3232 + BinaryOperation: 33188 + - RelativePath: PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml + Type: Resource + Location: fb/fbc1cb8895f6c396_90d25dac9f4ed3233c5fda72bee3dfe6 + Checksum: 90d25dac9f4ed3233c5fda72bee3dfe6 + UncompressedSize: 32815 + CompressedSize: 5003 + BinaryOperation: 33188 + - RelativePath: PatchWithInputChunk/PreviousBuildResources/introMoviePrefixed.txt + Type: Resource + Location: 5d/5d2aadfa1344ac63_5e631fd37d3350e30095d1251b178f2c + Checksum: 5e631fd37d3350e30095d1251b178f2c + UncompressedSize: 9117 + CompressedSize: 3259 + BinaryOperation: 33188 + - RelativePath: PatchWithInputChunk/PreviousBuildResources/introMovieSomewhatChanged.txt + Type: Resource + Location: 60/6054e5e0cf24763e_e9fadf6f2d386a0a0786bc863f20fa34 + Checksum: e9fadf6f2d386a0a0786bc863f20fa34 + UncompressedSize: 9091 + CompressedSize: 3232 + BinaryOperation: 33188 \ No newline at end of file diff --git a/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_macOS.yaml b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_macOS.yaml new file mode 100644 index 0000000..c25018f --- /dev/null +++ b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_macOS.yaml @@ -0,0 +1,90 @@ +Version: 0.1.0 +Type: ResourceGroup +NumberOfResources: 12 +TotalResourcesSizeCompressed: 32808 +TotalResourcesSizeUnCompressed: 149414 +Resources: + - RelativePath: resourcesOnBranch/introMovie.txt + Type: Resource + Location: 1c/1c6368ffb440041a_e9fadf6f2d386a0a0786bc863f20fa34 + Checksum: e9fadf6f2d386a0a0786bc863f20fa34 + UncompressedSize: 9091 + CompressedSize: 3232 + BinaryOperation: 33188 + - RelativePath: resourcesOnBranch/videoCardCategories.yaml + Type: Resource + Location: ff/ffe4c396c9c935d4_90d25dac9f4ed3233c5fda72bee3dfe6 + Checksum: 90d25dac9f4ed3233c5fda72bee3dfe6 + UncompressedSize: 32815 + CompressedSize: 5003 + BinaryOperation: 33188 + - RelativePath: PatchWithInputChunk/resFileIndexShort_build_next.txt + Type: Resource + Location: 1f/1f23be4b8bf6d2ae_4d398902b75611f7ae19903e6b461514 + Checksum: 4d398902b75611f7ae19903e6b461514 + UncompressedSize: 612 + CompressedSize: 324 + BinaryOperation: 33188 + - RelativePath: PatchWithInputChunk/NextBuildResources/testResource2.txt + Type: Resource + Location: 82/8264e4640cf94b94_271c8036e4eae5515053e924a0a39e0f + Checksum: 271c8036e4eae5515053e924a0a39e0f + UncompressedSize: 29 + CompressedSize: 47 + BinaryOperation: 33188 + - RelativePath: PatchWithInputChunk/NextBuildResources/videoCardCategories.yaml + Type: Resource + Location: 02/02b2b2627ca28b62_90d25dac9f4ed3233c5fda72bee3dfe6 + Checksum: 90d25dac9f4ed3233c5fda72bee3dfe6 + UncompressedSize: 32815 + CompressedSize: 5003 + BinaryOperation: 33188 + - RelativePath: PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt + Type: Resource + Location: dc/dca8056adced9237_0fb2bed9d164ad014bcb060b95df7ba1 + Checksum: 0fb2bed9d164ad014bcb060b95df7ba1 + UncompressedSize: 9425 + CompressedSize: 3409 + BinaryOperation: 33188 + - RelativePath: PatchWithInputChunk/resFileIndexShort_build_previous.txt + Type: Resource + Location: 32/326b0449af10b716_849821e0c98e37de4edc5f7156cf5887 + Checksum: 849821e0c98e37de4edc5f7156cf5887 + UncompressedSize: 611 + CompressedSize: 299 + BinaryOperation: 33188 + - RelativePath: PatchWithInputChunk/PatchResourceGroup_previousBuild_latestBuild.yaml + Type: Resource + Location: 44/446d5f683db0d467_b7431ad6d859bfd67a4c3fae337b0b6e + Checksum: b7431ad6d859bfd67a4c3fae337b0b6e + UncompressedSize: 3902 + CompressedSize: 765 + BinaryOperation: 33188 + - RelativePath: PatchWithInputChunk/PreviousBuildResources/introMovie.txt + Type: Resource + Location: fd/fd2a47b3d5faa494_e9fadf6f2d386a0a0786bc863f20fa34 + Checksum: e9fadf6f2d386a0a0786bc863f20fa34 + UncompressedSize: 9091 + CompressedSize: 3232 + BinaryOperation: 33188 + - RelativePath: PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml + Type: Resource + Location: fb/fbc1cb8895f6c396_90d25dac9f4ed3233c5fda72bee3dfe6 + Checksum: 90d25dac9f4ed3233c5fda72bee3dfe6 + UncompressedSize: 32815 + CompressedSize: 5003 + BinaryOperation: 33188 + - RelativePath: PatchWithInputChunk/PreviousBuildResources/introMoviePrefixed.txt + Type: Resource + Location: 5d/5d2aadfa1344ac63_5e631fd37d3350e30095d1251b178f2c + Checksum: 5e631fd37d3350e30095d1251b178f2c + UncompressedSize: 9117 + CompressedSize: 3259 + BinaryOperation: 33188 + - RelativePath: PatchWithInputChunk/PreviousBuildResources/introMovieSomewhatChanged.txt + Type: Resource + Location: 60/6054e5e0cf24763e_e9fadf6f2d386a0a0786bc863f20fa34 + Checksum: e9fadf6f2d386a0a0786bc863f20fa34 + UncompressedSize: 9091 + CompressedSize: 3232 + BinaryOperation: 33188 \ No newline at end of file diff --git a/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_macOS.yaml b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_macOS.yaml new file mode 100644 index 0000000..9a68f0d --- /dev/null +++ b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_macOS.yaml @@ -0,0 +1,20 @@ +Version: 0.1.0 +Type: ResourceGroup +NumberOfResources: 2 +TotalResourcesSizeCompressed: 8235 +TotalResourcesSizeUnCompressed: 41906 +Resources: + - RelativePath: resourcesOnBranch/introMovie.txt + Type: Resource + Location: 1c/1c6368ffb440041a_e9fadf6f2d386a0a0786bc863f20fa34 + Checksum: e9fadf6f2d386a0a0786bc863f20fa34 + UncompressedSize: 9091 + CompressedSize: 3232 + BinaryOperation: 33188 + - RelativePath: resourcesOnBranch/videoCardCategories.yaml + Type: Resource + Location: ff/ffe4c396c9c935d4_90d25dac9f4ed3233c5fda72bee3dfe6 + Checksum: 90d25dac9f4ed3233c5fda72bee3dfe6 + UncompressedSize: 32815 + CompressedSize: 5003 + BinaryOperation: 33188 \ No newline at end of file From ed6e7d881e55031647cf6628ee3a5ec8637103c6 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 5 Feb 2026 11:40:33 +0000 Subject: [PATCH 077/124] Cleanup Cli Filter tests - Change location of temporary output files - Rename helper function CleanupTestOutputFiles() to RemoveFiles() --- .gitignore | 3 --- tests/src/CliTestFixture.cpp | 11 +++++++- tests/src/CliTestFixture.h | 3 ++- tests/src/ResourcesCliTest.cpp | 48 +++++++++++----------------------- 4 files changed, 27 insertions(+), 38 deletions(-) diff --git a/.gitignore b/.gitignore index ebe1990..25185dc 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,3 @@ CMakeUserPresets.json ### VCPKG ### # Temporary measure which fixes build agent issue vcpkg_registry_cache/ - -### Ignored Test Run Files ### -/tests/testData/IgnoredTestOutputFiles/ diff --git a/tests/src/CliTestFixture.cpp b/tests/src/CliTestFixture.cpp index 6f1b0e1..8c19ce1 100644 --- a/tests/src/CliTestFixture.cpp +++ b/tests/src/CliTestFixture.cpp @@ -34,7 +34,16 @@ int CliTestFixture::RunCli( std::vector& arguments, std::string& ou return exit_status; } -void CliTestFixture::CleanupTestOutputFiles( const std::vector& filesToRemove ) +// ------------------------------------------------------------- +// Description: +// Helper function to remove intermediate files generated as +// part of a test being run. +// Arguments: +// filesToRemove - Vector of file paths to remove. +// Return Value: +// Nothing (void) +// ------------------------------------------------------------- +void CliTestFixture::RemoveFiles( const std::vector& filesToRemove ) { for( const auto& filePath : filesToRemove ) { diff --git a/tests/src/CliTestFixture.h b/tests/src/CliTestFixture.h index 6477b10..4577367 100644 --- a/tests/src/CliTestFixture.h +++ b/tests/src/CliTestFixture.h @@ -14,7 +14,8 @@ struct CliTestFixture : public ResourcesTestFixture int RunCli( std::vector& arguments, std::string& output, std::string& errorOutput, const std::string& workingDirectory = "" ); - void CleanupTestOutputFiles( const std::vector& filesToRemove ); + // Helper to remove files as part of a test run and cleanup + void RemoveFiles( const std::vector& filesToRemove ); }; #endif // CliTestFixture_H \ No newline at end of file diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index 577fc41..2a779e3 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -324,32 +324,29 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryOldDocumentFormatWithP TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) { - // Alter the current working directory for the duration of this test - CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); - // Setup test parameters std::string output; std::string errorOutput; std::vector arguments; std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory - std::filesystem::path outputFilePath = "IgnoredTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1.yaml"; + std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_validSimpleExample1.yaml" ); std::filesystem::path filterIniFilePath = "ExampleIniFiles/validSimpleExample1.ini"; // Ensure any previous test output files are removed - CleanupTestOutputFiles( { outputFilePath } ); + RemoveFiles( { outputFilePath } ); arguments.push_back( "create-group" ); - arguments.push_back( inputDirectoryPath.string() ); + arguments.push_back( inputDirectoryPath.lexically_normal().string() ); arguments.push_back( "--verbosity-level" ); arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); - arguments.push_back( filterIniFilePath.string() ); + arguments.push_back( filterIniFilePath.lexically_normal().string() ); arguments.push_back( "--output-file" ); - arguments.push_back( outputFilePath.string() ); + arguments.push_back( outputFilePath.lexically_normal().string() ); int res = RunCli( arguments, output, errorOutput, TEST_DATA_BASE_PATH ); std::cout << "--- RunCli() output: ---" << std::endl; @@ -367,39 +364,33 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) #error Unsupported platform #endif EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ) << " Output file does not match expected gold file."; - - // Cleanup test output files - //CleanupTestOutputFiles( { outputFilePath } ); } TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) { - // Alter the current working directory for the duration of this test - CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); - // Setup test parameters std::string output; std::string errorOutput; std::vector arguments; std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory - std::filesystem::path outputFilePath = "IgnoredTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1.yaml"; + std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_validComplexExample1.yaml" ); std::filesystem::path filterIniFilePath = "ExampleIniFiles/validComplexExample1.ini"; // Ensure any previous test output files are removed - CleanupTestOutputFiles( { outputFilePath } ); + RemoveFiles( { outputFilePath } ); arguments.push_back( "create-group" ); - arguments.push_back( inputDirectoryPath.string() ); + arguments.push_back( inputDirectoryPath.lexically_normal().string() ); arguments.push_back( "--verbosity-level" ); arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); - arguments.push_back( filterIniFilePath.string() ); + arguments.push_back( filterIniFilePath.lexically_normal().string() ); arguments.push_back( "--output-file" ); - arguments.push_back( outputFilePath.string() ); + arguments.push_back( outputFilePath.lexically_normal().string() ); int res = RunCli( arguments, output, errorOutput, TEST_DATA_BASE_PATH ); std::cout << "--- RunCli() output: ---" << std::endl; @@ -417,33 +408,27 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) #error Unsupported platform #endif EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ) << " Output file does not match expected gold file."; - - // Cleanup test output files - //CleanupTestOutputFiles( { outputFilePath } ); } TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 ) { - // Alter the current working directory for the duration of this test - CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); - // Setup test parameters std::string output; std::string errorOutput; std::vector arguments; std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ) ; // The base testData directory - std::filesystem::path outputFilePath = "IgnoredTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1.yaml"; + std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_validSimpleAndComplexExample1.yaml" ); std::vector filterIniFilePaths = { "ExampleIniFiles/validSimpleExample1.ini", "ExampleIniFiles/validComplexExample1.ini" }; // Ensure any previous test output files are removed - CleanupTestOutputFiles( { outputFilePath } ); + RemoveFiles( { outputFilePath } ); arguments.push_back( "create-group" ); - arguments.push_back( inputDirectoryPath.string() ); + arguments.push_back( inputDirectoryPath.lexically_normal().string() ); arguments.push_back( "--verbosity-level" ); arguments.push_back( "3" ); @@ -451,11 +436,11 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 for( auto filterFilePath : filterIniFilePaths ) { arguments.push_back( "--filter-file" ); - arguments.push_back( filterFilePath.string() ); + arguments.push_back( filterFilePath.lexically_normal().string() ); } arguments.push_back( "--output-file" ); - arguments.push_back( outputFilePath.string() ); + arguments.push_back( outputFilePath.lexically_normal().string() ); int res = RunCli( arguments, output, errorOutput, TEST_DATA_BASE_PATH ); std::cout << "--- RunCli() output: ---" << std::endl; @@ -473,9 +458,6 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 #error Unsupported platform #endif EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ) << " Output file does not match expected gold file."; - - // Cleanup test output files - //CleanupTestOutputFiles( { outputFilePath } ); } //--------------------------------------- From 9f2a4f337ab30e425176b7f442fb81f323849b02 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 5 Feb 2026 14:14:49 +0000 Subject: [PATCH 078/124] Change FilterResourceFilter test names to be more descriptive --- tests/src/ResourceFilterTest.cpp | 95 ++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 28 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 9513224..433dfb7 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -18,7 +18,7 @@ #include "FilterResourcePathFile.h" #include "ResourceFilter.h" -TEST_F( ResourceFilterTest, Example1IniParsing ) +TEST_F( ResourceFilterTest, LoadAndParseIniFile_example1Ini_UsingIniReader ) { // Use the test fixture's helper to get the absolute path const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/example1.ini" ); @@ -47,7 +47,7 @@ TEST_F( ResourceFilterTest, Example1IniParsing ) // ----------------------------------------- -TEST_F( ResourceFilterTest, FilterResourceFilter_OnlyIncludeFilter ) +TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_IncludeFilterOnly ) { ResourceTools::FilterResourceFilter filter( "[ .this .is .included ]" ); const auto& includes = filter.GetIncludeFilter(); @@ -59,8 +59,11 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_OnlyIncludeFilter ) EXPECT_TRUE( excludes.empty() ); } -TEST_F( ResourceFilterTest, FilterResourceFilter_OnlyExcludeFilter_Toplevel ) +TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_TopLevelExcludeFilterOnly ) { + // Top-level filter refers to the "filter" attribute of a NamedSection. + // When there is no include filter specified at the top-level "filter" attribute, + // a wild-card "*" should be added as default include. ResourceTools::FilterResourceFilter filter( "![ .excluded .extension ]", true ); const auto& includes = filter.GetIncludeFilter(); const auto& excludes = filter.GetExcludeFilter(); @@ -69,12 +72,16 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_OnlyExcludeFilter_Toplevel ) EXPECT_EQ( excludes[0], ".excluded" ); EXPECT_EQ( excludes[1], ".extension" ); + // Include filter should contain wildcard when no explicit include filter specified at top-level ("filter" attribute) EXPECT_EQ( includes.size(), 1 ); - EXPECT_EQ( includes[0], "*" ); // Wild-card added when no include filter specified for TOP-LEVEL filter + EXPECT_EQ( includes[0], "*" ); } -TEST_F( ResourceFilterTest, FilterResourceFilter_OnlyExcludeFilter_Inline ) +TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_InLineExcludeFilterOnly ) { + // "InLine" filter refers to an optional filter element at the end of a + // respaths/resfile attribute line (class FilterResourcePathFileEntry) + // The InLine filter applies only to that line (in addition to any parent filter present, if applicable). ResourceTools::FilterResourceFilter filter( "![ .excluded .extension ]" ); const auto& includes = filter.GetIncludeFilter(); const auto& excludes = filter.GetExcludeFilter(); @@ -83,39 +90,44 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_OnlyExcludeFilter_Inline ) EXPECT_EQ( excludes[0], ".excluded" ); EXPECT_EQ( excludes[1], ".extension" ); + // No wildcard should be added to include filter at in-line level EXPECT_EQ( includes.size(), 0 ); - EXPECT_TRUE( includes.empty() ); // No wild-card added when no include filter specified INLINE + EXPECT_TRUE( includes.empty() ); } -TEST_F( ResourceFilterTest, FilterResourceFilter_ComplexIncludeExcludeFilter ) +TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRules_UseComplexIncludeExcludeFilters ) { ResourceTools::FilterResourceFilter filter( "[ .red .gr2 .dds .png .yaml ] [ .txt ] ![ .csv .xls ] [ .bat .sh ] ![ .blk .yel ]" ); const auto& includes = filter.GetIncludeFilter(); const auto& excludes = filter.GetExcludeFilter(); + std::vector expectedIncludes = { ".red", ".gr2", ".dds", ".png", ".yaml", ".txt", ".bat", ".sh" }; std::vector expectedExcludes = { ".csv", ".xls", ".blk", ".yel" }; - EXPECT_EQ( includes, expectedIncludes ); - EXPECT_EQ( excludes, expectedExcludes ); + EXPECT_EQ( includes, expectedIncludes ) << "Include filters do not match expected values"; + EXPECT_EQ( excludes, expectedExcludes ) << "Exclude filters do not match expected values"; } -TEST_F( ResourceFilterTest, FilterResourceFilter_SimpleIncludeFilter ) +TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_SimpleIncludeFilterOnly ) { ResourceTools::FilterResourceFilter filter( "[ .red ]" ); const auto& includes = filter.GetIncludeFilter(); + EXPECT_EQ( includes.size(), 1 ); EXPECT_EQ( includes[0], ".red" ); } -TEST_F( ResourceFilterTest, FilterResourceFilter_SimpleExcludeFilter ) +TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_SimpleExcludeFilterOnly ) { ResourceTools::FilterResourceFilter filter( "![ .blk ]" ); const auto& excludes = filter.GetExcludeFilter(); + EXPECT_EQ( excludes.size(), 1 ); EXPECT_EQ( excludes[0], ".blk" ); } -TEST_F( ResourceFilterTest, FilterResourceFilter_IncludeExcludeInclude ) +TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_CombineIncludeExcludeIncludeFilters ) { + // This test, combines multiple include and exclude filters, to test the logic of adding/removing filter elements: // Include .in1 and .in2 // Exclude .in2, .ex1, and .ex2 (removes .in2 from include) // Include .ex1, .in3 and .in1 (removes .ex1 from exclude, adds .in3, keeps .in1) @@ -124,17 +136,19 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_IncludeExcludeInclude ) ResourceTools::FilterResourceFilter filter( "[ .in1 .in2 ] ![ .in2 .ex1 .ex2 ] [ .ex1 .in3 .in1 ]" ); const auto& includes = filter.GetIncludeFilter(); const auto& excludes = filter.GetExcludeFilter(); + std::vector expectedIncludes = { ".in1", ".ex1", ".in3" }; std::vector expectedExcludes = { ".in2", ".ex2" }; EXPECT_EQ( includes, expectedIncludes ); EXPECT_EQ( excludes, expectedExcludes ); } -TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingIncludeBracketBeforeNextOpenExcludeBracket ) +TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_MissingClosingIncludeBracketBeforeNextOpenExcludeBracket ) { try { - // This test filter has a missing closing bracket for the first include filter, before the next exclude filter starts + // This test filter has a missing closing bracket for the first include filter + // before the next exclude filter starts. ResourceTools::FilterResourceFilter filter( "[ .in1 ! [ .ex1 ]" ); FAIL() << "Expected std::invalid_argument to be thrown"; } @@ -148,10 +162,12 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingIncludeBracketBef } } -TEST_F( ResourceFilterTest, FilterResourceFilter_ExcludeMarkerWithoutBracket_v1 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_ExcludeMarkerWithoutOpenBracket ) { try { + // This test filter has an exclude marker "!" but is missing the open bracket + // for the exclude filter. ResourceTools::FilterResourceFilter filter( "! .ex1 ]" ); FAIL() << "Expected std::invalid_argument to be thrown"; } @@ -165,10 +181,12 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ExcludeMarkerWithoutBracket_v1 } } -TEST_F( ResourceFilterTest, FilterResourceFilter_ExcludeMarkerWithoutBracket_v2 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_ExcludeMarkerWithoutOpenBracketAfterValidIncludeFilter ) { try { + // This test filter has a valid include filter, followed by an exclude marker "!" + // but is missing the open bracket for the exclude filter. ResourceTools::FilterResourceFilter filter( " [ .in1 ] ! .ex1 ]" ); FAIL() << "Expected std::invalid_argument to be thrown"; } @@ -182,10 +200,12 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ExcludeMarkerWithoutBracket_v2 } } -TEST_F( ResourceFilterTest, FilterResourceFilter_ExcludeMarkerWithoutBracket_v3 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_ExcludeMarkerWithoutBracketAfterValidIncludeAndExcludeFilters ) { try { + // This test filter has a valid include and exclude filters, + // followed by an exclude marker "!" and no filter definition after that. ResourceTools::FilterResourceFilter filter( " [ .in1 ] ![ .ex1 ] ! " ); FAIL() << "Expected std::invalid_argument to be thrown"; } @@ -199,10 +219,11 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ExcludeMarkerWithoutBracket_v3 } } -TEST_F( ResourceFilterTest, FilterResourceFilter_MissingOpeningBracket_v1 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_MissingOpeningIncludeBracketAtStart ) { try { + // This test filter is missing the opening bracket for the include filter. ResourceTools::FilterResourceFilter filter( ".in1 .in2 ]" ); FAIL() << "Expected std::invalid_argument to be thrown"; } @@ -216,10 +237,12 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_MissingOpeningBracket_v1 ) } } -TEST_F( ResourceFilterTest, FilterResourceFilter_MissingOpeningBracket_v2 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_MissingOpeningIncludeBracketForSecondIncludeFilter ) { try { + // This test filter is missing the opening bracket for the second include filter, + // after a valid first include filter. ResourceTools::FilterResourceFilter filter( " [ .in1 .in2 ] .in3 ] " ); FAIL() << "Expected std::invalid_argument to be thrown"; } @@ -233,10 +256,11 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_MissingOpeningBracket_v2 ) } } -TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingBracket_v1 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_MissingClosingIncludeBracket ) { try { + // This test filter is missing the closing bracket for the first and only include filter. ResourceTools::FilterResourceFilter filter( "[ .in1 .in2 " ); FAIL() << "Expected std::invalid_argument to be thrown"; } @@ -250,10 +274,11 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingBracket_v1 ) } } -TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingBracket_v2 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_MissingClosingIncludeBracketForSecondIncludeFilter ) { try { + // This test filter is missing the closing bracket for the second include filter. ResourceTools::FilterResourceFilter filter( "[ .in1 .in2 ] [ .in3 " ); FAIL() << "Expected std::invalid_argument to be thrown"; } @@ -267,10 +292,11 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingBracket_v2 ) } } -TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingBracket_v3 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_MissingClosingIncludeBracketForMiddleIncludeFilter ) { try { + // This test filter is missing the closing bracket for the middle include filter. ResourceTools::FilterResourceFilter filter( "[ .in1 .in2 ] [ .in3 [ .in4 ]" ); FAIL() << "Expected std::invalid_argument to be thrown"; } @@ -284,48 +310,61 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_MissingClosingBracket_v3 ) } } -TEST_F( ResourceFilterTest, FilterResourceFilter_CondensedValidFilterStringv1 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_UseCondensedValidIncludeExcludeIncludeFilters ) { + // This test filter combines multiple include and exclude filters without extra whitespaces. + // Done to test the logic of adding/removing filter elements and that the parsing logic is not dependent on whitespaces. ResourceTools::FilterResourceFilter filter( "[inToken1 inToken2]![exToken1 exToken2][inToken3]" ); const auto& includes = filter.GetIncludeFilter(); const auto& excludes = filter.GetExcludeFilter(); + std::vector expectedIncludes = { "inToken1", "inToken2", "inToken3" }; std::vector expectedExcludes = { "exToken1", "exToken2" }; EXPECT_EQ( includes, expectedIncludes ); EXPECT_EQ( excludes, expectedExcludes ); } -TEST_F( ResourceFilterTest, FilterResourceFilter_CondensedValidFilterStringv2 ) +TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_UseCondensedValidExcludeIncludeExcludeFilters ) { + // This test filter combines multiple include and exclude filters without extra whitespaces. + // Done to test the logic of adding/removing filter elements and that the parsing logic is not dependent on whitespaces. ResourceTools::FilterResourceFilter filter( "![exToken1][inToken1 inToken2]![exToken2][inToken3]" ); const auto& includes = filter.GetIncludeFilter(); const auto& excludes = filter.GetExcludeFilter(); + std::vector expectedIncludes = { "inToken1", "inToken2", "inToken3" }; std::vector expectedExcludes = { "exToken1", "exToken2" }; EXPECT_EQ( includes, expectedIncludes ); EXPECT_EQ( excludes, expectedExcludes ); } -TEST_F( ResourceFilterTest, FilterResourceFilter_EmptyFilterString_TopLevel ) +TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_EmptyTopLevelFilter ) { + // When the top-level "filter" attribute of a NamedSection is empty, + // a wild-card "*" should be added to the include filter by default. + // Exclude filter should be empty though. ResourceTools::FilterResourceFilter filter( "", true ); const auto& includes = filter.GetIncludeFilter(); const auto& excludes = filter.GetExcludeFilter(); + // Wildcard "*" should be added when no include filter is specified for top-level "filter" attribute EXPECT_EQ( includes.size(), 1 ); - EXPECT_EQ( includes[0], "*" ); // Wild-card added when no include filter specified on TOP-LEVEL filter + EXPECT_EQ( includes[0], "*" ); EXPECT_TRUE( excludes.empty() ); } -TEST_F( ResourceFilterTest, FilterResourceFilter_EmptyFilterString_Inline ) +TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_EmptyInLineFilter ) { + // When an in-line filter of (respaths/resfile line) is empty (the default behavior), + // no wildcard "*" should be added to the include filter. ResourceTools::FilterResourceFilter filter( "" ); const auto& includes = filter.GetIncludeFilter(); const auto& excludes = filter.GetExcludeFilter(); + // No wildcard should be added to an in-line include filter EXPECT_EQ( includes.size(), 0 ); - EXPECT_TRUE( includes.empty() ); // No wild-card added when no include filter specified INLINE + EXPECT_TRUE( includes.empty() ); EXPECT_TRUE( excludes.empty() ); } From 92255d70b1850b58e94d261df7b2bdb61ae978ee Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 5 Feb 2026 15:09:19 +0000 Subject: [PATCH 079/124] Change FilterPrefixMap test names to be more descriptive --- tests/src/ResourceFilterTest.cpp | 43 ++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 433dfb7..39e08bd 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -371,8 +371,10 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_EmptyInLineFilter ) // ----------------------------------------- -TEST_F( ResourceFilterTest, FilterPrefixMap_SinglePrefixMultiplePaths ) +TEST_F( ResourceFilterTest, FilterPrefixMap_Validate_SinglePrefixWithMultiplePathsIsAllowed ) { + // This test validates that a single prefix (prefix1) with multiple different paths + // is correctly parsed and stored in the map. ResourceTools::FilterPrefixMap map( "prefix1:/somePath;../otherPath" ); const auto& prefixMapEntries = map.GetMapEntries(); ASSERT_EQ( prefixMapEntries.size(), 1 ) << "There should only be 1 prefix in the map"; @@ -387,8 +389,10 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_SinglePrefixMultiplePaths ) EXPECT_EQ( it->second.GetPaths(), expected ) << "Paths do not match expected values"; } -TEST_F( ResourceFilterTest, FilterPrefixMap_MultiplePrefixes ) +TEST_F( ResourceFilterTest, FilterPrefixMap_Validate_MultipleDifferentPrefixesAreAllowed ) { + // This test validates that multiple different prefixes (prefix1 & prefix2) + // with their associated paths are correctly parsed and stored in the map. ResourceTools::FilterPrefixMap map( "prefix1:/path1;/path2 prefix2:/newPath1" ); const auto& prefixMapEntries = map.GetMapEntries(); ASSERT_EQ( prefixMapEntries.size(), 2 ) << "There should be 2 prefixes in the map"; @@ -409,8 +413,11 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_MultiplePrefixes ) EXPECT_EQ( it2->second.GetPaths(), expected2 ) << "Paths do not match expected values"; } -TEST_F( ResourceFilterTest, FilterPrefixMap_DuplicateSamePrefixPathsInDifferentOrder ) +TEST_F( ResourceFilterTest, FilterPrefixMap_Validate_DuplicateSamePrefixWithPathsInDifferentOrderIsAllowed ) { + // This test validates that if the same prefix (prefix1) is defined multiple times, + // with same paths but in different order (path1+path2 & path2+path1). + // That the paths are combined and stored in the map without duplicates. ResourceTools::FilterPrefixMap map( "prefix1:/path1;/path2 prefix1:/path2;/path1" ); // There should only be one prefix (prefix1) @@ -428,9 +435,12 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_DuplicateSamePrefixPathsInDifferentO EXPECT_EQ( it->first, it->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; } -TEST_F( ResourceFilterTest, FilterPrefixMap_MultiplePrefixesAppendToPaths ) +TEST_F( ResourceFilterTest, FilterPrefixMap_Validate_MultipleSamePrefixesCanAppendToPaths ) { + // This test validates that if the same prefix (prefix1) is defined multiple times (first and last), + // with different paths (path2+path1 & path3+path1), that the paths are combined and stored in the map without duplicates. ResourceTools::FilterPrefixMap map( "prefix1:/path2;/path1 prefix2:/otherPath1;/otherPath2 prefix1:/path3;/path1" ); + const auto& prefixMapEntries = map.GetMapEntries(); ASSERT_EQ( prefixMapEntries.size(), 2 ) << "There should be 2 prefixes in the map"; auto it1 = prefixMapEntries.find( "prefix1" ); @@ -449,10 +459,13 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_MultiplePrefixesAppendToPaths ) EXPECT_EQ( it2->first, it2->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; } -TEST_F( ResourceFilterTest, FilterPrefixMap_DifferentWhitespacesBetweenPrefixes ) +TEST_F( ResourceFilterTest, FilterPrefixMap_Validata_DifferentWhitespacesBetweenPrefixesAreAllowed ) { + // This test validates that different whitespaces (space, tab, new line) + // between prefix definitions are handled correctly. std::string input = "prefix1:/path1\tprefixTab:/path2\nprefixNewLine:/path3"; ResourceTools::FilterPrefixMap map( input ); + const auto& prefixMapEntries = map.GetMapEntries(); ASSERT_EQ( prefixMapEntries.size(), 3 ) << "There should only be 3 prefix in the map"; auto it1 = prefixMapEntries.find( "prefix1" ); @@ -476,8 +489,10 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_DifferentWhitespacesBetweenPrefixes EXPECT_EQ( it3->first, it3->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; } -TEST_F( ResourceFilterTest, FilterPrefixMap_Invalid_MissingColon ) +TEST_F( ResourceFilterTest, FilterPrefixMap_CheckFailure_MissingColonAfterPrefixBeforePaths ) { + // This test validates that if a prefix definition is missing a colon ":" + // after the prefix and before the paths section, that an exception is thrown. try { ResourceTools::FilterPrefixMap prefixmap( "prefix1/path1" ); @@ -493,8 +508,10 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_Invalid_MissingColon ) } } -TEST_F( ResourceFilterTest, FilterPrefixMap_Invalid_EmptyPrefix ) +TEST_F( ResourceFilterTest, FilterPrefixMap_CheckFailure_MissingPrefixBeforePaths ) { + // This test validates that if a prefix definition is missing the prefix itself + // (lhs of colon) before the paths section, that an exception is thrown. try { ResourceTools::FilterPrefixMap prefixmap( ":/path1" ); @@ -510,8 +527,10 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_Invalid_EmptyPrefix ) } } -TEST_F( ResourceFilterTest, FilterPrefixMap_Invalid_NoPaths ) +TEST_F( ResourceFilterTest, FilterPrefixMap_CheckFailure_MissingPathsAfterPrefix ) { + // This test validates that if a prefix definition is missing the paths section + // (rhs of colon) after the prefix, that an exception is thrown. try { ResourceTools::FilterPrefixMap prefixmap( "prefix1:" ); @@ -527,8 +546,10 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_Invalid_NoPaths ) } } -TEST_F( ResourceFilterTest, FilterPrefixMapEntry_PrefixMismatchOnAppend ) +TEST_F( ResourceFilterTest, FilterPrefixMapEntry_CheckFailure_PrefixMissingInMapWhenAppendingPath ) { + // This test validates that when trying to append paths to a FilterPrefixMapEntry + // with a different prefix than the existing one of the mapEntry that an exception is thrown. try { ResourceTools::FilterPrefixMapEntry entry( "prefix1", "/path1" ); @@ -545,8 +566,10 @@ TEST_F( ResourceFilterTest, FilterPrefixMapEntry_PrefixMismatchOnAppend ) } } -TEST_F( ResourceFilterTest, FilterPrefixMapEntry_InvalidNoPathsOnAppend ) +TEST_F( ResourceFilterTest, FilterPrefixMapEntry_CheckFailure_EmptyPathWhenAppendingPathToPrefix ) { + // This test validates that when trying to append an empty path to a FilterPrefixMapEntry + // that an exception is thrown, since empty paths are not allowed. try { ResourceTools::FilterPrefixMapEntry entry( "prefix1", "" ); // Empty string for paths From e5232971a6cb0f0bc03e844d1da2cbe9337feba8 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 5 Feb 2026 15:30:37 +0000 Subject: [PATCH 080/124] Change FilterDefaultSection test names to be more descriptive --- tests/src/ResourceFilterTest.cpp | 42 ++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 39e08bd..89901c7 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -587,11 +587,14 @@ TEST_F( ResourceFilterTest, FilterPrefixMapEntry_CheckFailure_EmptyPathWhenAppen // ----------------------------------------- -TEST_F( ResourceFilterTest, FilterDefaultSection_InitializeValid ) +TEST_F( ResourceFilterTest, FilterDefaultSection_Validate_DefaultSectionWithMultiplePrefixesIsAllowed ) { + // This test validates that a FilterDefaultSection can be initialized + // with multiple different prefixes and their associated paths. std::string input = "prefix1:/path1;../path2 prefix2:/path3"; ResourceTools::FilterDefaultSection defaultSection( input ); const auto& prefixMapEntries = defaultSection.GetPrefixMap().GetMapEntries(); + ASSERT_EQ( prefixMapEntries.size(), 2 ) << "There should be 2 prefixes in the map"; auto it1 = prefixMapEntries.find( "prefix1" ); auto it2 = prefixMapEntries.find( "prefix2" ); @@ -608,8 +611,11 @@ TEST_F( ResourceFilterTest, FilterDefaultSection_InitializeValid ) EXPECT_EQ( it2->first, it2->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; } -TEST_F( ResourceFilterTest, FilterDefaultSection_Initialize_InvalidMissingColon ) +TEST_F( ResourceFilterTest, FilterDefaultSection_CheckFailure_InitializeWithMissingColonInPrefixmap ) { + // This test validates that when trying to initialize a FilterDefaultSection + // with a prefixmap string missing a colon ":" after the prefix definition + // that an exception is thrown. try { ResourceTools::FilterDefaultSection defaultSection( "prefix1/path1" ); @@ -625,8 +631,11 @@ TEST_F( ResourceFilterTest, FilterDefaultSection_Initialize_InvalidMissingColon } } -TEST_F( ResourceFilterTest, FilterDefaultSection_Initialize_InvalidEmptyPrefix ) +TEST_F( ResourceFilterTest, FilterDefaultSection_CheckFailure_InitializeWithEmptyPrefixInPrefixmap ) { + // This test validates that when trying to initialize a FilterDefaultSection + // with a prefixmap string missing the prefix definition (empty prefix) + // that an exception is thrown. try { ResourceTools::FilterDefaultSection defaultSection( ":/path1" ); @@ -642,8 +651,17 @@ TEST_F( ResourceFilterTest, FilterDefaultSection_Initialize_InvalidEmptyPrefix ) } } -// ----------------------------------------- - +// ------------------------------------------------------------- +// Description: +// Helper function, for tests in this file, to verify that all +// expected paths are present in the resolved map. +// Arguments: +// allExpectedPaths - Set of all expected paths to be found in the map +// resolvedMap - Map of resolved paths +// messagePrefix - Optional prefix to add to the failure message +// Return Value: +// None (void) +// ------------------------------------------------------------- void MapContainsPaths( const std::set& allExpectedPaths, const std::map& resolvedMap, const std::string messagePrefix = "" ) @@ -658,6 +676,19 @@ void MapContainsPaths( const std::set& allExpectedPaths, } } +// ------------------------------------------------------------- +// Description: +// Helper function, for tests in this file, to validate that the resolved +// path map contains all expected paths with the correct include/exclude filters. +// Arguments: +// expectedPaths - Set of expected paths to be found in the map +// resolvedPathMap - Map of resolved paths with their filters +// expectedIncludes - Vector of expected include filters for each path +// expectedExcludes - Vector of expected exclude filters for each path +// messagePrefix - Optional prefix to add to the failure message +// Return Value: +// None (void) +// ------------------------------------------------------------- void ValidatePathMap( const std::set& expectedPaths, const std::map& resolvedPathMap, const std::vector& expectedIncludes, @@ -681,6 +712,7 @@ void ValidatePathMap( const std::set& expectedPaths, } } +// ----------------------------------------- TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_NoFilter ) { From fd410643d79681c273b097e26f5a9978b899243f Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 5 Feb 2026 16:14:04 +0000 Subject: [PATCH 081/124] Change FilterResourcePathFile test names to be more descriptive --- tests/src/ResourceFilterTest.cpp | 36 +++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 89901c7..05cf3e1 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -714,8 +714,10 @@ void ValidatePathMap( const std::set& expectedPaths, // ----------------------------------------- -TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_NoFilter ) +TEST_F( ResourceFilterTest, FilterResourcePathFile_Validate_SingleLineAttribute_WithNoInlineFilterIsAllowed ) { + // This test validates that a FilterResourcePathFile can be initialized with a single line attribute + // that contains a prefix and path, but no in-line filter. std::string prefixMapStr = "prefix1:/path1;../path2"; std::string parentFilterStr = "[ .in1 .in2 ] ![ .ex1 ]"; ResourceTools::FilterPrefixMap prefixMap( prefixMapStr ); @@ -733,8 +735,10 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_NoFilter ) ValidatePathMap( expectedPaths, resolvedPathMap, expectedIncludes, expectedExcludes ); } -TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_InlineIncludeExclude ) +TEST_F( ResourceFilterTest, FilterResourcePathFile_Validate_SingleLineAttribute_WithInlineFilterNoOverridesIsAllowed ) { + // This test validates that a FilterResourcePathFile can be initialized with a single line attribute + // that contains a prefix and path, with an in-line filter that does not override any of the parent filters. std::string prefixMapStr = "prefix1:/path1"; std::string parentFilterStr = "[ .in1 .in2 ] ![ .ex1 ]"; ResourceTools::FilterPrefixMap prefixMap( prefixMapStr ); @@ -752,8 +756,11 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_InlineIncludeExclu ValidatePathMap( expectedPaths, resolvedPathMap, expectedIncludes, expectedExcludes ); } -TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_InlineOverridesParentFilter ) +TEST_F( ResourceFilterTest, FilterResourcePathFile_Validate_SingleLineAttribute_WithInlineFilterOverridingParentFilterIsAllowed ) { + // This test validates that a FilterResourcePathFile can be initialized with a single + // line attribute with an in-line filter that overrides some of the parent filters by + // moving some filters from include to exclude and vice versa. std::string prefixMapStr = "prefix1:/path1;../subPath2;/path3"; std::string parentFilterStr = "[ .parIn1 .parIn2 ] ![ .parEx1 ]"; ResourceTools::FilterPrefixMap prefixMap( prefixMapStr ); @@ -772,8 +779,12 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_InlineOverridesPar ValidatePathMap( expectedPaths, resolvedPathMap, expectedIncludes, expectedExcludes ); } -TEST_F( ResourceFilterTest, FilterResourcePathFile_MultiLine_MixedFiltersWithOverrides ) +TEST_F( ResourceFilterTest, FilterResourcePathFile_Validate_MultiLineAttribute_WithMixedInlineFilterOverridesIsAllowed ) { + // This test validates that a FilterResourcePathFile can be initialized with a multi-line attribute + // that contains multiple prefixes and paths. + // The in-line filters may override parent filters in different ways. + // Some with no overrides, others with include/exclude filters switched around, etc. std::string prefixMapStr = "prefix1:/path1;../path2 prefix2:/path3"; std::string parentFilterStr = "[ .parIn1 .parIn2 ] ![ .parEx1 ]"; ResourceTools::FilterPrefixMap prefixMap( prefixMapStr ); @@ -823,8 +834,11 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_MultiLine_MixedFiltersWithOve } } -TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_DuplicateOverrides ) +TEST_F( ResourceFilterTest, FilterResourcePathFile_Validate_SingleLineAttribute_WithDuplicateIncludeExcludeInlineOverridesIsAllowed ) { + // This test validates that a FilterResourcePathFile can be initialized with a single line attribute. + // With an in-line filter that has duplicate include and exclude entries (some same as parent filters). + // The final resolved filters should not contain any duplicates. std::string prefixMapStr = "prefix1:/path1"; std::string parentFilterStr = "[ .parIn1 .parIn2 ] ![ .parEx1 ]"; ResourceTools::FilterPrefixMap prefixMap( prefixMapStr ); @@ -843,8 +857,10 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_SingleLine_DuplicateOverrides ValidatePathMap( expectedPaths, resolvedPathMap, expectedIncludes, expectedExcludes ); } -TEST_F( ResourceFilterTest, FilterResourcePathFile_Invalid_MissingPrefix ) +TEST_F( ResourceFilterTest, FilterResourcePathFile_CheckFailure_MissingPrefixThrowsException ) { + // This test validates that when trying to initialize a FilterResourcePathFile with an + // invalid raw attribute string (missing prefix), that an exception is thrown. std::string prefixMapStr = "prefix1:/path1;../path2 prefix2:/path3"; std::string parentFilterStr = "[ .in1 .in2 ] ![ .ex1 ]"; ResourceTools::FilterPrefixMap prefixMap( prefixMapStr ); @@ -854,8 +870,10 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_Invalid_MissingPrefix ) EXPECT_THROW( ResourceTools::FilterResourcePathFile pathFile( rawPathFileAttrib, prefixMap, parentFilter ), std::invalid_argument ); } -TEST_F( ResourceFilterTest, FilterResourcePathFile_Invalid_UnknownPrefix ) +TEST_F( ResourceFilterTest, FilterResourcePathFile_CheckFailure_UnknownPrefixThrowsException ) { + // This test validates that when trying to initialize a FilterResourcePathFile with an + // invalid raw attribute string (unknown prefix, not in the prefix map), that an exception is thrown. std::string prefixMapStr = "prefix1:/path1;../path2 prefix2:/path3"; std::string parentFilterStr = "[ .in1 .in2 ] ![ .ex1 ]"; ResourceTools::FilterPrefixMap prefixMap( prefixMapStr ); @@ -865,8 +883,10 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_Invalid_UnknownPrefix ) EXPECT_THROW( ResourceTools::FilterResourcePathFile pathFile( rawPathFileAttrib, prefixMap, parentFilter ), std::invalid_argument ); } -TEST_F( ResourceFilterTest, FilterResourcePathFile_Invalid_MalformedInlineFilter ) +TEST_F( ResourceFilterTest, FilterResourcePathFile_CheckFailure_MalformedInlineFilterThrowsException ) { + // This test validates that when trying to initialize a FilterResourcePathFile with an + // invalid raw attribute string (malformed in-line filter), that an exception is thrown. std::string prefixMapStr = "prefix1:/path1;../path2 prefix2:/path3"; std::string parentFilterStr = "[ .in1 .in2 ] ![ .ex1 ]"; ResourceTools::FilterPrefixMap prefixMap( prefixMapStr ); From 73c7817581736d35939fb3e7b944ea0afbde5919 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 5 Feb 2026 21:03:01 +0000 Subject: [PATCH 082/124] Change FilterNamedSection test names to be more descriptive --- tests/src/ResourceFilterTest.cpp | 136 ++++++++++++++++++++++--------- 1 file changed, 97 insertions(+), 39 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 05cf3e1..522be10 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -459,7 +459,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_Validate_MultipleSamePrefixesCanAppe EXPECT_EQ( it2->first, it2->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; } -TEST_F( ResourceFilterTest, FilterPrefixMap_Validata_DifferentWhitespacesBetweenPrefixesAreAllowed ) +TEST_F( ResourceFilterTest, FilterPrefixMap_Validate_DifferentWhitespacesBetweenPrefixesAreAllowed ) { // This test validates that different whitespaces (space, tab, new line) // between prefix definitions are handled correctly. @@ -898,9 +898,10 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_CheckFailure_MalformedInlineF // ----------------------------------------- -TEST_F( ResourceFilterTest, FilterNamedSection_Valid_SingleLineRespath ) +TEST_F( ResourceFilterTest, FilterNamedSection_Validate_SingleLineRespathIsAllowed ) { - std::string sectionName = "FilterNamedSection_Valid_SingleLineRespath"; + // This test validates that a FilterNamedSection can be initialized with a single line respath. + std::string sectionName = "FilterNamedSection_Validate_SingleLineRespathIsAllowed"; std::string filter = "[ .in1 .in2 ] ![ .ex1 ]"; std::string respaths = "testPrefix:/foo/bar"; std::string defaultParentPrefixMapStr = "testPrefix:/myPath"; @@ -922,9 +923,11 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_SingleLineRespath ) ValidatePathMap( expectedPaths, combinedMap, expectedIncludes, expectedExcludes, "CombinedResolvedPathMap" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_Valid_EmptyFilter_TopLevel ) +TEST_F( ResourceFilterTest, FilterNamedSection_Validate_EmptyTopLevelFilterIsAllowed ) { - std::string sectionName = "FilterNamedSection_Valid_EmptyFilter_TopLevel"; + // This test validates that a FilterNamedSection can be initialized with an empty filter at top-level, + // which should add a wildcard "*" include filter. + std::string sectionName = "FilterNamedSection_Validate_EmptyTopLevelFilterIsAllowed"; std::string defaultParentPrefixMapStr = "testPrefix:/myPath"; std::string filter = ""; // Empty filter string at top-level should add wildcard include std::string respaths = "testPrefix:/foo/bar"; @@ -946,9 +949,11 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_EmptyFilter_TopLevel ) ValidatePathMap( expectedPaths, combinedMap, expectedIncludes, expectedExcludes, "CombinedResolvedPathMap" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_Valid_OnlyExcludeFilter_TopLevel ) +TEST_F( ResourceFilterTest, FilterNamedSection_Validate_OnlyTopLevelExcludeFilterIsAllowed ) { - std::string sectionName = "FilterNamedSection_Valid_OnlyExcludeFilter_TopLevel"; + // This test validates that a FilterNamedSection can be initialized with only an exclude filter at top-level, + // which should add a wildcard "*" include filter. + std::string sectionName = "FilterNamedSection_Validate_OnlyTopLevelExcludeFilterIsAllowed"; std::string defaultParentPrefixMapStr = "testPrefix:/myPath"; std::string filter = "![ .ex1 ]"; // When there is only an exclude filter at top-level, it should add wildcard include std::string respaths = "testPrefix:/foo/bar"; @@ -970,9 +975,11 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_OnlyExcludeFilter_TopLevel ValidatePathMap( expectedPaths, combinedMap, expectedIncludes, expectedExcludes, "CombinedResolvedPathMap" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_Valid_MultiLineRespath ) +TEST_F( ResourceFilterTest, FilterNamedSection_Validate_MultiLineRespathWithSomeInlineOverridesIsAllowed ) { - std::string sectionName = "FilterNamedSection_Valid_MultiLineRespath"; + // This test validates that a FilterNamedSection can be initialized with a multi-line respath attribute. + // Some lines may have in-line filters that override the top-level filter, while others just use the top-level filter as-is. + std::string sectionName = "FilterNamedSection_Validate_MultiLineRespathWithSomeInlineOverridesIsAllowed"; std::string filter = "[ .in1 .in2 ] ![ .ex1 ]"; std::string respaths = "prefix1:/firstLine [ .inLine1 ] ![ .exLine1 ]\n" // Add entries to both include and exclude filters @@ -1004,9 +1011,11 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_MultiLineRespath ) ValidatePathMap( secondLinePaths, combinedMap, defaultIncludes, defaultExcludes, "SecondLine ResolvedRespathsMap" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_Valid_RespathAndResfile ) +TEST_F( ResourceFilterTest, FilterNamedSection_Validate_CombinedSingleLineRespathAndResfileAttributesIsAllowed ) { - std::string sectionName = "FilterNamedSection_Valid_RespathAndResfile"; + // This test validates that a FilterNamedSection can be initialized with both respath and resfile attributes. + // The combined resolved map contains entries from both attributes. + std::string sectionName = "FilterNamedSection_Validate_CombinedSingleLineRespathAndResfileAttributesIsAllowed"; std::string defaultParentPrefixMapStr = "prefix1:/pathA;/pathB prefix2:/pathC"; std::string filter = "[ .in1 .in2 ] ![ .ex1 ]"; std::string respaths = "prefix1:/respaths"; @@ -1040,9 +1049,11 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_RespathAndResfile ) } -TEST_F( ResourceFilterTest, FilterNamedSection_Valid_RespathSet_ResfileEmpty ) +TEST_F( ResourceFilterTest, FilterNamedSection_Validate_RespathWithoutResfileAttributeIsAllowed ) { - std::string sectionName = "FilterNamedSection_Valid_RespathSet_ResfileEmpty"; + // This test validates that a FilterNamedSection can be initialized with only + // a respath attribute and no resfile attribute. + std::string sectionName = "FilterNamedSection_Validate_RespathWithoutResfileAttributeIsAllowed"; std::string parentPrefixMapStr = "prefix1:/pathA"; std::string filter = "[ .in1 .in2 ] ![ .ex1 ]"; std::string respaths = "prefix1:/foo/bar"; @@ -1071,9 +1082,11 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_RespathSet_ResfileEmpty ) ValidatePathMap( onlyValidPaths, combinedMap, defaultIncludes, defaultExcludes, "ResolvedCombinedMap" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_Invalid_RespathMissing ) +TEST_F( ResourceFilterTest, FilterNamedSection_CheckFailure_MissingRespathAttributeThrowsException ) { - std::string sectionName = "FilterNamedSection_Invalid_RespathMissing"; + // This test validates that when trying to initialize a FilterNamedSection + // with a missing respath attribute, that an exception is thrown. + std::string sectionName = "FilterNamedSection_CheckFailure_MissingRespathAttributeThrowsException"; std::string defaultParentPrefixMapStr = "prefix1:/path1"; std::string filter = "[ .in1 ]"; std::string resfile = "prefix1:/foo/bar"; @@ -1096,9 +1109,12 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Invalid_RespathMissing ) } } -TEST_F( ResourceFilterTest, FilterNamedSection_Valid_CombinedResolvedMap ) +TEST_F( ResourceFilterTest, FilterNamedSection_Validate_CombinedResolvedMapOnSamePrefixIsAllowed ) { - std::string sectionName = "FilterNamedSection_Valid_CombinedResolvedMap"; + // This test validates that a FilterNamedSection can be initialized with both + // respath and resfile attributes using the same prefix, and that the combined + // resolved map contains entries from both attributes. + std::string sectionName = "FilterNamedSection_Validate_CombinedResolvedMapOnSamePrefixIsAllowed"; std::string defaultParentPrefixMapStr = "prefixA:/path1"; std::string filter = "[ .in1 ]"; std::string respaths = "prefixA:/foo/bar"; @@ -1131,9 +1147,12 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_CombinedResolvedMap ) ValidatePathMap( combinedPaths, combinedMap, defaultIncludes, defaultExcludes, "ResolvedCombinedMap" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_Valid_CombinedResolvedMap_EmptyTopLevelFilter ) +TEST_F( ResourceFilterTest, FilterNamedSection_Validate_CombinedResolvedMapWithEmptyTopLevelFilterIsAllowed ) { - std::string sectionName = "FilterNamedSection_Valid_CombinedResolvedMap_EmptyTopLevelFilter"; + // This test validates that a FilterNamedSection can be initialized with both + // respath and resfile attributes using the same prefix, and an empty top-level filter. + // The combined resolved map contains entries from both attributes, with wildcard "*" include filter. + std::string sectionName = "FilterNamedSection_Validate_CombinedResolvedMapWithEmptyTopLevelFilterIsAllowed"; std::string defaultParentPrefixMapStr = "prefixA:/path1"; std::string filter = ""; // Empty top-level filter should add wildcard ("*") include std::string respaths = "prefixA:/foo/bar"; @@ -1174,9 +1193,12 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_CombinedResolvedMap_EmptyTo } } -TEST_F( ResourceFilterTest, FilterNamedSection_Valid_CombinedResolvedMap_OnlyExcludeTopLevelFilter ) +TEST_F( ResourceFilterTest, FilterNamedSection_Validate_CombinedResolvedMapWithOnlyExcludeTopLevelFilterIsAllowed ) { - std::string sectionName = "FilterNamedSection_Valid_CombinedResolvedMap_OnlyExcludeTopLevelFilter"; + // This test validates that a FilterNamedSection can be initialized with both + // respath and resfile attributes using the same prefix, and only an exclude filter at top-level. + // The combined resolved map contains entries from both attributes, with wildcard "*" include filter. + std::string sectionName = "FilterNamedSection_Validate_CombinedResolvedMapWithOnlyExcludeTopLevelFilterIsAllowed"; std::string defaultParentPrefixMapStr = "prefixA:/path1"; std::string filter = " ![ .ex1 ] "; // Only exclude filter at top-level should add wildcard ("*") include as well std::string respaths = "prefixA:/foo/bar"; @@ -1217,9 +1239,13 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_CombinedResolvedMap_OnlyExc } } -TEST_F( ResourceFilterTest, FilterNamedSection_Valid_DiffCombinedResolvedMap_EmptyTopLevelFilter_OverrideRespath ) +TEST_F( ResourceFilterTest, FilterNamedSection_Validate_EmptyTopLevelFilterWithRespathOverrideIsAllowed ) { - std::string sectionName = "FilterNamedSection_Valid_DiffCombinedResolvedMap_EmptyTopLevelFilter_OverrideRespath"; + // This test validates that a FilterNamedSection can be initialized with both + // respath and resfile attributes using the same prefix, and an empty top-level filter. + // The respath has in-line filters that override the top-level filter while the + // resfile uses the default "*" wildcard top-level filter. + std::string sectionName = "FilterNamedSection_Validate_EmptyTopLevelFilterWithRespathOverrideIsAllowed"; std::string defaultParentPrefixMapStr = "prefixA:/path1"; std::string filter = ""; // Empty top-level filter should add wildcard ("*") include filter std::string respaths = "prefixA:/foo/bar [ .inlineInclude ] ![ .inlineExclude ]"; // Inline filters to override @@ -1275,9 +1301,13 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_DiffCombinedResolvedMap_Emp } } -TEST_F( ResourceFilterTest, FilterNamedSection_Valid_DiffCombinedResolvedMap_EmptyTopLevelFilter_OverrideResfile ) +TEST_F( ResourceFilterTest, FilterNamedSection_Validate_EmptyTopLevelFilterWithResfileOverrideIsAllowed ) { - std::string sectionName = "FilterNamedSection_Valid_DiffCombinedResolvedMap_EmptyTopLevelFilter_OverrideResfile"; + // This test validates that a FilterNamedSection can be initialized with both + // respath and resfile attributes using the same prefix, and an empty top-level filter. + // The resfile has in-line filters that override the top-level filter while the + // respath uses the default "*" wildcard top-level filter. + std::string sectionName = "FilterNamedSection_Validate_EmptyTopLevelFilterWithResfileOverrideIsAllowed"; std::string defaultParentPrefixMapStr = "prefixA:/path1"; std::string filter = ""; // Empty top-level filter should add wildcard ("*") include filter std::string respaths = "prefixA:/foo/bar"; @@ -1333,9 +1363,13 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_DiffCombinedResolvedMap_Emp } } -TEST_F( ResourceFilterTest, FilterNamedSection_Valid_DiffCombinedResolvedMap_OnlyExcludeTopLevelFilter_OverrideRespath ) +TEST_F( ResourceFilterTest, FilterNamedSection_Validate_OnlyExcludeTopLevelFilterWithRespathOverrideIsAllowed ) { - std::string sectionName = "FilterNamedSection_Valid_DiffCombinedResolvedMap_OnlyExcludeTopLevelFilter_OverrideRespath"; + // This test validates that a FilterNamedSection can be initialized with both + // respath and resfile attributes using the same prefix, and only an exclude filter at top-level. + // The respath has in-line filters that override the top-level filter while the + // resfile uses the default "*" wildcard top-level filter. + std::string sectionName = "FilterNamedSection_Validate_OnlyExcludeTopLevelFilterWithRespathOverrideIsAllowed"; std::string defaultParentPrefixMapStr = "prefixA:/path1"; std::string filter = "![ .toplevelExclude ]"; // Only exclude filter at top-level should add wildcard ("*") include as well std::string respaths = "prefixA:/foo/bar [ .inlineInclude ] ![ .inlineExclude ]"; // Inline filters to override @@ -1393,9 +1427,13 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_DiffCombinedResolvedMap_Onl } } -TEST_F( ResourceFilterTest, FilterNamedSection_Valid_DiffCombinedResolvedMap_OnlyExcludeTopLevelFilter_OverrideResfile ) +TEST_F( ResourceFilterTest, FilterNamedSection_Validate_OnlyExcludeTopLevelFilterWithResfileOverrideIsAllowed ) { - std::string sectionName = "FilterNamedSection_Valid_DiffCombinedResolvedMap_OnlyExcludeTopLevelFilter_OverrideRespath"; + // This test validates that a FilterNamedSection can be initialized with both + // respath and resfile attributes using the same prefix, and only an exclude filter at top-level. + // The resfile has in-line filters that override the top-level filter while the + // respath uses the default "*" wildcard top-level filter. + std::string sectionName = "FilterNamedSection_Validate_OnlyExcludeTopLevelFilterWithResfileOverrideIsAllowed"; std::string defaultParentPrefixMapStr = "prefixA:/path1"; std::string filter = "![ .toplevelExclude ]"; // Only exclude filter at top-level should add wildcard ("*") include as well std::string respaths = "prefixA:/foo/bar"; @@ -1453,9 +1491,13 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_DiffCombinedResolvedMap_Onl } } -TEST_F( ResourceFilterTest, FilterNamedSection_Valid_CombinedResolvedMap_OverwrittenByResfileMap ) +TEST_F( ResourceFilterTest, FilterNamedSection_Validate_CombinedMapWithResfileOverrideIsAllowed ) { - std::string sectionName = "FilterNamedSection_Valid_CombinedResolvedMap_OverwrittenByResfileMap"; + // This test validates that a FilterNamedSection can be initialized with both + // respath and resfile attributes using the same prefix, and that the combined + // resolved map contains entries from both attributes, with the resfile filters + // overriding the top-level filters. + std::string sectionName = "FilterNamedSection_Validate_CombinedMapWithResfileOverrideIsAllowed"; std::string defaultParentPrefixMapStr = "prefix1:/pathA;/pathB"; std::string filter = "[ .in1 .in2 ] ![ .ex1 ]"; std::string respaths = "prefix1:/foo/bar"; @@ -1492,9 +1534,13 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_CombinedResolvedMap_Overwri ValidatePathMap( allPaths, respathsAgainMap, defaultIncludes, allExcludes, "ResolvedRespathsMap-Again" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_Valid_SameCombinedResolvedMap_EmptyTopLevelFilter_OverwrittenByResfileMap ) +TEST_F( ResourceFilterTest, FilterNamedSection_Validate_NoTopLevelFilterSameCombinedMapWithResfileOverrideIsAllowed ) { - std::string sectionName = "FilterNamedSection_Valid_SameCombinedResolvedMap_EmptyTopLevelFilter_OverwrittenByResfileMap"; + // This test validates that a FilterNamedSection can be initialized with both + // respath and resfile attributes using the same prefix and only default "*" top-level filter. + // The combined resolved map contains entries from both attributes, with the resfile + // filters overriding the top-level filter. + std::string sectionName = "FilterNamedSection_Validate_NoTopLevelFilterSameCombinedMapWithResfileOverrideIsAllowed"; std::string defaultParentPrefixMapStr = "prefix1:/pathA;/pathB"; std::string filter = ""; // Empty top-level filter should add wildcard ("*") include std::string respaths = "prefix1:/foo/bar"; @@ -1540,9 +1586,13 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_SameCombinedResolvedMap_Emp ValidatePathMap( allPaths, respathsAgainMap, defaultIncludes, allExcludes, "ResolvedRespathsMap-Again" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_Valid_SameCombinedResolvedMap_EmptyTopLevelFilter_OverwrittenByRespathMap ) +TEST_F( ResourceFilterTest, FilterNamedSection_Validate_NoTopLevelFilterSameCombinedMapWithRespathOverrideIsAllowed ) { - std::string sectionName = "FilterNamedSection_Valid_SameCombinedResolvedMap_EmptyTopLevelFilter_OverwrittenByRespathMap"; + // This test validates that a FilterNamedSection can be initialized with both + // respath and resfile attributes using the same prefix and only default "*" top-level filter. + // The combined resolved map contains entries from both attributes, with the respath + // filters overriding the top-level filter. + std::string sectionName = "FilterNamedSection_Validate_NoTopLevelFilterSameCombinedMapWithRespathOverrideIsAllowed"; std::string defaultParentPrefixMapStr = "prefix1:/pathA;/pathB"; std::string filter = ""; // Empty top-level filter should add wildcard ("*") include std::string respaths = "prefix1:/foo/bar [ .extra ]"; // Same path, extra include filter @@ -1592,9 +1642,13 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_SameCombinedResolvedMap_Emp ValidatePathMap( allPaths, resfileAgainMap, defaultIncludes, allExcludes, "ResolvedResfileMap-Again" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_Valid_SameCombinedResolvedMap_ExcludeOnlyTopLevelFilter_OverwrittenByResfileMap ) +TEST_F( ResourceFilterTest, FilterNamedSection_Validate_OnlyExcludeFilterSameCombinedMapWithResfileOverrideIsAllowed ) { - std::string sectionName = "FilterNamedSection_Valid_SameCombinedResolvedMap_ExcludeOnlyTopLevelFilter_OverwrittenByResfileMap"; + // This test validates that a FilterNamedSection can be initialized with both respath and resfile + // attributes using the same prefix, and only an exclude filter at top-level (and default "*" include). + // The combined resolved map contains entries from both attributes, with the resfile filters + // overriding the top-level filter. + std::string sectionName = "FilterNamedSection_Validate_OnlyExcludeFilterSameCombinedMapWithResfileOverrideIsAllowed"; std::string defaultParentPrefixMapStr = "prefix1:/pathA;/pathB"; std::string filter = " ![ .topLevelExclude ]"; // Only exclude filter at top-level should add wildcard ("*") include std::string respaths = "prefix1:/foo/bar"; @@ -1640,9 +1694,13 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Valid_SameCombinedResolvedMap_Exc ValidatePathMap( allPaths, respathsAgainMap, defaultIncludes, allExcludes, "ResolvedRespathsMap-Again" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_Valid_SameCombinedResolvedMap_ExcludeOnlyTopLevelFilter_OverwrittenByRespathMap ) +TEST_F( ResourceFilterTest, FilterNamedSection_Validate_OnlyExcludeFilterSameCombinedMapWithRespathsOverrideIsAllowed ) { - std::string sectionName = "FilterNamedSection_Valid_SameCombinedResolvedMap_ExcludeOnlyTopLevelFilter_OverwrittenByRespathMap"; + // This test validates that a FilterNamedSection can be initialized with both respath and resfile + // attributes using the same prefix, and only an exclude filter at top-level (and default "*" include). + // The combined resolved map contains entries from both attributes, with the respaths filters + // overriding the top-level filter. + std::string sectionName = "FilterNamedSection_Validate_OnlyExcludeFilterSameCombinedMapWithRespathsOverrideIsAllowed"; std::string defaultParentPrefixMapStr = "prefix1:/pathA;/pathB"; std::string filter = " ![ .topLevelExclude ]"; // Only exclude filter at top-level should add wildcard ("*") include std::string respaths = "prefix1:/foo/bar [ .extra ]"; // Same path, extra include filter From 7e2106b189d64d8c3035495a5e958a799dd30974 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 5 Feb 2026 21:42:41 +0000 Subject: [PATCH 083/124] Change FilterResourceFile test names to be more descriptive --- tests/src/ResourceFilterTest.cpp | 33 +++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index 522be10..ba24026 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -1752,11 +1752,11 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Validate_OnlyExcludeFilterSameCom // ------------------------------------------ -TEST_F( ResourceFilterTest, FilterResourceFile_Load_example1_ini ) +TEST_F( ResourceFilterTest, FilterResourceFile_ValidateSuccessfulFileLoad_example1_ini ) { - // Use the test fixture's helper to get the absolute path + // This test validates that the example1.ini file can be loaded successfully + // and that the resolved paths match the expected values. const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/example1.ini" ); - try { ResourceTools::FilterResourceFile resourceFile( iniPath.generic_string() ); @@ -1790,10 +1790,11 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_example1_ini ) } } -TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidMissingDefaultSection_ini ) +TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_invalidMissingDefaultSection_ini ) { + // This test validates that loading an ini file missing the required [DEFAULT] + // section throws an exception. const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidMissingDefaultSection.ini" ); - try { ResourceTools::FilterResourceFile resourceFile( iniPath.generic_string() ); @@ -1810,10 +1811,11 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidMissingDefaultSection } } -TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidMissingNamedSection_ini ) +TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_invalidMissingNamedSection_ini ) { + // This test validates that loading an ini file missing any named sections + // throws an exception since at least one named section is required. const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidMissingNamedSection.ini" ); - try { ResourceTools::FilterResourceFile resourceFile( iniPath.generic_string() ); @@ -1830,10 +1832,10 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidMissingNamedSection_i } } -TEST_F( ResourceFilterTest, FilterResourceFile_Load_iniFileNotFound ) +TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_iniFileDoesNotExist ) { + // This test validates that loading a non-existent ini file throws an exception. const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/iniFileNotFound.ini" ); - try { ResourceTools::FilterResourceFile resourceFile( iniPath.generic_string() ); @@ -1850,8 +1852,9 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_iniFileNotFound ) } } -TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidPrefixmap_ini ) +TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_invalidPrefixmap_ini ) { + // This test validates that loading an ini file with an invalid prefixmap format throws an exception. const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidPrefixmap.ini" ); try { @@ -1869,8 +1872,9 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidPrefixmap_ini ) } } -TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidSectionFilter_ini ) +TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_invalidSectionFilter_ini ) { + // This test validates that loading an ini file with an invalid section filter format throws an exception. const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidSectionFilter.ini" ); try { @@ -1888,8 +1892,9 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidSectionFilter_ini ) } } -TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidInlineFilter_ini ) +TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_invalidInlineFilter_ini ) { + // This test validates that loading an ini file with an invalid inline filter format throws an exception. const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidInlineFilter.ini" ); try { @@ -1907,8 +1912,10 @@ TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidInlineFilter_ini ) } } -TEST_F( ResourceFilterTest, FilterResourceFile_Load_invalidPrefixMismatch_ini ) +TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_invalidPrefixMismatch_ini ) { + // This test validates that loading an ini file with a prefix in the respaths/resfile + // attributes that does not match any prefix in the prefixmap throws an exception. const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidPrefixMismatch.ini" ); try { From 6f4a1508a05c44865ee096c6e32efde83c244708 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 5 Feb 2026 22:39:32 +0000 Subject: [PATCH 084/124] Change ResourceFilter test names to be more descriptive --- tests/src/ResourceFilterTest.cpp | 111 +++++++++++++++++++------------ 1 file changed, 69 insertions(+), 42 deletions(-) diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceFilterTest.cpp index ba24026..0a44a04 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceFilterTest.cpp @@ -1935,13 +1935,12 @@ TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_invalidPre // ------------------------------------------ -TEST_F( ResourceFilterTest, ResourceFilter_Load_SingleFile_ThatDoesNotExist ) +TEST_F( ResourceFilterTest, ResourceFilter_ConfirmFileLoadFailure_SingleIniFileThatDoesNotExist ) { + // This test validates that initializing a ResourceFilter with a non-existent ini file throws an exception. const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/noSuchFile.ini" ); std::vector paths = { iniPath }; - ResourceTools::ResourceFilter resourceFilter; - try { resourceFilter.Initialize( paths ); @@ -1960,14 +1959,14 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load_SingleFile_ThatDoesNotExist ) } } -TEST_F( ResourceFilterTest, ResourceFilter_Load_MultipleFiles_OneThatDoesNotExist ) +TEST_F( ResourceFilterTest, ResourceFilter_ConfirmFileLoadFailure_MultipleFilesOneThatDoesNotExist ) { + // This test validates that initializing a ResourceFilter with multiple ini files + // where one is valid and another does not exist, results in an exception being thrown. const std::filesystem::path iniPath1 = GetTestFileFileAbsolutePath( "ExampleIniFiles/example1.ini" ); const std::filesystem::path iniPath2 = GetTestFileFileAbsolutePath( "ExampleIniFiles/noSuchFile.ini" ); std::vector paths = { iniPath1, iniPath2 }; - ResourceTools::ResourceFilter resourceFilter; - try { resourceFilter.Initialize( paths ); @@ -1986,16 +1985,17 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load_MultipleFiles_OneThatDoesNotExis } } -TEST_F( ResourceFilterTest, ResourceFilter_JustLoad_example1_ini ) +TEST_F( ResourceFilterTest, ResourceFilter_ConfirmSuccessfulFileLoad_example1_ini ) { + // This test validates that initializing a ResourceFilter with a valid ini file (example1.ini) + // successfully loads without throwing any exceptions. const std::filesystem::path iniPath1 = GetTestFileFileAbsolutePath( "ExampleIniFiles/example1.ini" ); std::vector paths = { iniPath1 }; - ResourceTools::ResourceFilter resourceFilter; - try { resourceFilter.Initialize( paths ); + ASSERT_TRUE( true ); // If we got here, the file loaded successfully without any exceptions } catch( const std::exception& e ) { @@ -2007,41 +2007,58 @@ TEST_F( ResourceFilterTest, ResourceFilter_JustLoad_example1_ini ) } } -TEST_F( ResourceFilterTest, ResourceFilter_Test_CurrentWorkingDirectoryChanger ) +TEST_F( ResourceFilterTest, ResourceFilter_Validate_RaiiClassCurrentWorkingDirectoryChanger_ChangesWorkingDirectoryForDurationOfTest ) { - // RAII class to change the current working directory for the duration of this test - // Needed so both relative paths in the .ini file resolve correctly, based on paths to the location of the example .ini files - CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); - - const std::filesystem::path iniPath1 = "ExampleIniFiles/example1.ini"; - std::vector paths = { iniPath1 }; + // This test validates that the RAII CurrentWorkingDirectoryChanger class correctly changes + // the current working directory for the duration of the test. + std::filesystem::path pathBeforeChange = std::filesystem::current_path(); + std::filesystem::path testDataPath = TEST_DATA_BASE_PATH; - ResourceTools::ResourceFilter resourceFilter; - try { - resourceFilter.Initialize( paths ); - ASSERT_EQ( resourceFilter.HasFilters(), true ); - ASSERT_EQ( resourceFilter.GetFullResolvedPathMap().size(), 7 ); + // RAII class to change the current working directory for the duration of this test + // Needed so both relative paths in the .ini file resolve correctly, + // based on paths to the location of the example .ini files + CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); - // Check that the "binaryFileIndex_v0_0_0.txt" file (and it's path) is included correctly - std::filesystem::path oneValidRelativePath = "./Indicies/binaryFileIndex_v0_0_0.txt"; - ASSERT_EQ( resourceFilter.ShouldInclude( oneValidRelativePath ), true ); - } - catch( const std::exception& e ) - { - FAIL() << "Exception in test, should not have failed: " << e.what(); - } - catch( ... ) - { - FAIL() << "Unknown exception thrown while initializing ResourceFilter with example1.ini"; + const std::filesystem::path iniPath1 = "ExampleIniFiles/example1.ini"; + std::vector paths = { iniPath1 }; + ResourceTools::ResourceFilter resourceFilter; + try + { + resourceFilter.Initialize( paths ); + ASSERT_EQ( resourceFilter.HasFilters(), true ); + ASSERT_EQ( resourceFilter.GetFullResolvedPathMap().size(), 7 ); + + // Check that the "binaryFileIndex_v0_0_0.txt" file (and it's path) is included correctly + std::filesystem::path oneValidRelativePath = "./Indicies/binaryFileIndex_v0_0_0.txt"; + ASSERT_EQ( resourceFilter.ShouldInclude( oneValidRelativePath ), true ); + } + catch( const std::exception& e ) + { + FAIL() << "Exception in test, should not have failed: " << e.what(); + } + catch( ... ) + { + FAIL() << "Unknown exception thrown while initializing ResourceFilter with example1.ini"; + } + + // Make sure the working directory is set to the testDataPath and not the original path + ASSERT_EQ( std::filesystem::current_path().lexically_normal().string(), testDataPath.lexically_normal().string() ) << "Current working directory should be the TEST_DATA_BASE_PATH"; + ASSERT_NE( std::filesystem::current_path().lexically_normal().string(), pathBeforeChange.lexically_normal().string() ) << "Current working directory should not be same as at start of test"; } + + // After the RAII object goes out of scope, the working directory should be restored to the original path + ASSERT_EQ( std::filesystem::current_path().lexically_normal().string(), pathBeforeChange.lexically_normal().string() ) << "Current working directory should have been restored"; + ASSERT_NE( std::filesystem::current_path().lexically_normal().string(), testDataPath.lexically_normal().string() ) << "Current working directory should not be the TEST_DATA_BASE_PATH"; } -TEST_F( ResourceFilterTest, ResourceFilter_Load_validSimpleExample1_ini_usingRelativePaths ) +TEST_F( ResourceFilterTest, ResourceFilter_ValidateSuccessfulFileLoadUsingRelativePaths_validSimpleExample1_ini ) { + // This test validates that initializing a ResourceFilter with a valid ini file (validSimpleExample1.ini), + // using relative paths, loads successfully and the expected paths and filters are present. + // Alter the current working directory for the duration of this test CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); - try { const std::filesystem::path iniPath1 = "ExampleIniFiles/validSimpleExample1.ini"; @@ -2080,11 +2097,13 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load_validSimpleExample1_ini_usingRel } } -TEST_F( ResourceFilterTest, ResourceFilter_Load_validSimpleExample1_ini_usingAbsolutePaths ) +TEST_F( ResourceFilterTest, ResourceFilter_ValidateSuccessfulFileLoadUsingAbsolutePaths_validSimpleExample1_ini ) { + // This test validates that initializing a ResourceFilter with a valid ini file (validSimpleExample1.ini), + // using absolute paths, loads successfully and the expected paths and filters are present. + // Alter the current working directory for the duration of this test CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); - try { const std::filesystem::path iniPath1 = "ExampleIniFiles/validSimpleExample1.ini"; @@ -2124,11 +2143,13 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load_validSimpleExample1_ini_usingAbs } } -TEST_F( ResourceFilterTest, ResourceFilter_Load_validComplexExample1_ini_usingRelativePaths ) +TEST_F( ResourceFilterTest, ResourceFilter_ValidateSuccessfulFileLoadUsingRelativePaths_validComplexExample1_ini ) { + // This test validates that initializing a ResourceFilter with a valid ini file (validComplexExample1.ini), + // using relative paths, loads successfully and the expected paths and filters are present. + // Alter the current working directory for the duration of this test CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); - try { const std::filesystem::path iniPath1 = "ExampleIniFiles/validComplexExample1.ini"; @@ -2233,11 +2254,13 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load_validComplexExample1_ini_usingRe } } -TEST_F( ResourceFilterTest, ResourceFilter_Load_validComplexExample1_ini_usingAbsolutePaths ) +TEST_F( ResourceFilterTest, ResourceFilter_ValidateSuccessfulFileLoadUsingAbsolutePath_validComplexExample1_ini ) { + // This test validates that initializing a ResourceFilter with a valid ini file (validComplexExample1.ini), + // using absolute paths, loads successfully and the expected paths and filters are present. + // Alter the current working directory for the duration of this test CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); - try { const std::filesystem::path iniPath1 = "ExampleIniFiles/validComplexExample1.ini"; @@ -2342,11 +2365,15 @@ TEST_F( ResourceFilterTest, ResourceFilter_Load_validComplexExample1_ini_usingAb } } -TEST_F( ResourceFilterTest, ResourceFilter_Load2iniFiles_validComplexExample1_and_validSimpleExample1 ) +TEST_F( ResourceFilterTest, ResourceFilter_ValidateSuccessfulLoadOf2IniFiles_validComplexExample1_and_validSimpleExample1 ) { + // This test validates that initializing a ResourceFilter with two valid ini files + // (validComplexExample1.ini and validSimpleExample1.ini), using relative paths, + // loads successfully and the expected paths and filters from both ini files are + // present in the resulting ResourceFilter. + // Alter the current working directory for the duration of this test CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); - try { std::vector paths = { From f8b16aa4d316f546c006770a684aa0b567353ed6 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 5 Feb 2026 23:03:05 +0000 Subject: [PATCH 085/124] Order files in tools/CMakeLists.txt in alphabetical order --- tools/CMakeLists.txt | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index bc71deb..1b5641f 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -12,23 +12,23 @@ set(SRC_FILES include/Downloader.h include/FileDataStreamIn.h include/FileDataStreamOut.h + include/FilterDefaultSection.h + include/FilterNamedSection.h + include/FilterPrefixMap.h + include/FilterPrefixMapEntry.h + include/FilterResourceFile.h + include/FilterResourceFilter.h + include/FilterResourcePathFile.h + include/FilterResourcePathFileEntry.h include/GzipCompressionStream.h include/GzipDecompressionStream.h include/Md5ChecksumStream.h include/Patching.h + include/ResourceFilter.h include/ResourceTools.h include/RollingChecksum.h include/ScopedFile.h include/StatusCallback.h - include/FilterResourceFile.h - include/FilterDefaultSection.h - include/FilterNamedSection.h - include/FilterPrefixMap.h - include/FilterResourceFilter.h - include/FilterResourcePathFile.h - include/FilterPrefixMapEntry.h - include/FilterResourcePathFileEntry.h - include/ResourceFilter.h src/BundleStreamIn.cpp src/BundleStreamOut.cpp @@ -37,22 +37,22 @@ set(SRC_FILES src/Downloader.cpp src/FileDataStreamIn.cpp src/FileDataStreamOut.cpp - src/GzipCompressionStream.cpp - src/GzipDecompressionStream.cpp - src/Md5ChecksumStream.cpp - src/ResourceTools.cpp - src/ScopedFile.cpp - src/Patching.cpp - src/RollingChecksum.cpp - src/FilterResourceFile.cpp src/FilterDefaultSection.cpp src/FilterNamedSection.cpp src/FilterPrefixMap.cpp + src/FilterPrefixMapEntry.cpp + src/FilterResourceFile.cpp src/FilterResourceFilter.cpp src/FilterResourcePathFile.cpp - src/FilterPrefixMapEntry.cpp src/FilterResourcePathFileEntry.cpp + src/GzipCompressionStream.cpp + src/GzipDecompressionStream.cpp + src/Md5ChecksumStream.cpp src/ResourceFilter.cpp + src/ResourceTools.cpp + src/ScopedFile.cpp + src/Patching.cpp + src/RollingChecksum.cpp ) add_library(resources-tools STATIC ${SRC_FILES}) From 64ed63e060448dfe395565b9b4a5b77ed62b3488 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 5 Feb 2026 23:38:36 +0000 Subject: [PATCH 086/124] Add comments as per coding guidelines to CurrentWorkingDirectoryChanger class --- tests/src/ResourcesTestFixture.h | 50 +++++++++++++++++++------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/tests/src/ResourcesTestFixture.h b/tests/src/ResourcesTestFixture.h index f8b5987..6198fb3 100644 --- a/tests/src/ResourcesTestFixture.h +++ b/tests/src/ResourcesTestFixture.h @@ -25,50 +25,60 @@ struct ResourcesTestFixture : public ::testing::Test bool DirectoryIsSubset( const std::filesystem::path& dir1, const std::filesystem::path& dir2 ); // Test that all files in dir1 exist in dir2, and the contents of the files in both directories are the same. }; -// RAII helper class to change the current working directory temporarily (within a scope) -class CurrentWorkingDirectoryChanger { +// ------------------------------------------------------------- +// Description: +// CurrentWorkingDirectoryChanger is a RAII helper class that changes +// the current working directory to a specified path on construction +// and restores the original working directory once destructed. +// As long as the CurrentWorkingDirectoryChanger object is in scope, +// the working directory remains changed to its new value. +// ------------------------------------------------------------- +class CurrentWorkingDirectoryChanger +{ public: - // Constructor acquires the current path and changes it - explicit CurrentWorkingDirectoryChanger(const std::filesystem::path& new_path) : - original_path_(std::filesystem::current_path()) + // Constructor saves the current working directory and changes it to the new path + explicit CurrentWorkingDirectoryChanger( const std::filesystem::path& new_path ) : + m_original_path( std::filesystem::current_path() ) { try { - std::cout << "CurrentWorkingDirectoryChanger - Original directory: " << original_path_.generic_string() << std::endl; - std::filesystem::current_path(new_path); // Change to new path - std::cout << "CurrentWorkingDirectoryChanger - Changed directory to: " << std::filesystem::current_path().generic_string() << std::endl; + std::cout << "WorkingDirectory - before change: " << m_original_path.generic_string() << std::endl; + std::filesystem::current_path( new_path ); + std::cout << "WorkingDirectory - after change: " << std::filesystem::current_path().generic_string() + << std::endl; } - catch (const std::filesystem::filesystem_error& e) + catch( const std::filesystem::filesystem_error& e ) { - std::cerr << "CurrentWorkingDirectoryChanger - Error changing directory: " << e.what() << std::endl; + std::cerr << "Error changing working directory: " << e.what() << std::endl; } } - // Destructor restores the original path + // Destructor restores working directory to the original path ~CurrentWorkingDirectoryChanger() { try { - std::filesystem::current_path(original_path_); // Restore original path - std::cout << "CurrentWorkingDirectoryChanger - Restored directory to: " << std::filesystem::current_path().generic_string() << std::endl; + std::filesystem::current_path( m_original_path ); + std::cout << "WorkingDirectory - restored to: " << m_original_path.generic_string() << std::endl; } - catch (const std::filesystem::filesystem_error& e) + catch( const std::filesystem::filesystem_error& e ) { - std::cerr << "CurrentWorkingDirectoryChanger - Error restoring directory: " << e.what() << std::endl; + std::cerr << "Error restoring working directory: " << e.what() << std::endl; } } // Disable copy and move operations - CurrentWorkingDirectoryChanger(const CurrentWorkingDirectoryChanger&) = delete; + CurrentWorkingDirectoryChanger( const CurrentWorkingDirectoryChanger& ) = delete; - CurrentWorkingDirectoryChanger& operator=(const CurrentWorkingDirectoryChanger&) = delete; + CurrentWorkingDirectoryChanger& operator=( const CurrentWorkingDirectoryChanger& ) = delete; - CurrentWorkingDirectoryChanger(CurrentWorkingDirectoryChanger&&) = delete; + CurrentWorkingDirectoryChanger( CurrentWorkingDirectoryChanger&& ) = delete; - CurrentWorkingDirectoryChanger& operator=(CurrentWorkingDirectoryChanger&&) = delete; + CurrentWorkingDirectoryChanger& operator=( CurrentWorkingDirectoryChanger&& ) = delete; private: - std::filesystem::path original_path_; + // Store the original working directory path to restore upon class destruction + std::filesystem::path m_original_path; }; #endif // CarbonResourcesTestFixture_H \ No newline at end of file From 44ec1f489587842840485a6130e1fc22af5e851b Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 6 Feb 2026 00:19:13 +0000 Subject: [PATCH 087/124] Cleanup, simplify and comment the FilterDefaultSection class --- tools/CMakeLists.txt | 1 - tools/include/FilterDefaultSection.h | 18 ++++++++++++++++-- tools/src/FilterDefaultSection.cpp | 19 ------------------- 3 files changed, 16 insertions(+), 22 deletions(-) delete mode 100644 tools/src/FilterDefaultSection.cpp diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 1b5641f..4d758f8 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -37,7 +37,6 @@ set(SRC_FILES src/Downloader.cpp src/FileDataStreamIn.cpp src/FileDataStreamOut.cpp - src/FilterDefaultSection.cpp src/FilterNamedSection.cpp src/FilterPrefixMap.cpp src/FilterPrefixMapEntry.cpp diff --git a/tools/include/FilterDefaultSection.h b/tools/include/FilterDefaultSection.h index eebe924..5eca3a0 100644 --- a/tools/include/FilterDefaultSection.h +++ b/tools/include/FilterDefaultSection.h @@ -9,16 +9,30 @@ namespace ResourceTools { +// ------------------------------------------------------------- +// Description: +// FilterDefaultSection is a class that represents the [DEFAULT] +// section attribute of a .ini filter file. +// ------------------------------------------------------------- class FilterDefaultSection { public: FilterDefaultSection() = default; - explicit FilterDefaultSection( const std::string& prefixmapStr ); + // Constructs a FilterDefaultSection object + explicit FilterDefaultSection(const std::string& rawPrefixMap) + : m_prefixMap(rawPrefixMap) + { + } - const FilterPrefixMap& GetPrefixMap() const; + // Gets the prefix map of the [DEFAULT] section + const FilterPrefixMap& GetPrefixMap() const + { + return m_prefixMap; + } private: + // Map of prefixes to FilterPrefixMapEntry objects, from the parsed prefixmap attribute. FilterPrefixMap m_prefixMap; }; diff --git a/tools/src/FilterDefaultSection.cpp b/tools/src/FilterDefaultSection.cpp deleted file mode 100644 index e0cbf6a..0000000 --- a/tools/src/FilterDefaultSection.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright © 2025 CCP ehf. - -#include "FilterDefaultSection.h" - -namespace ResourceTools -{ - -FilterDefaultSection::FilterDefaultSection( const std::string& prefixmapStr ) : - m_prefixMap( prefixmapStr ) -{ -} - -const FilterPrefixMap& FilterDefaultSection::GetPrefixMap() const -{ - return m_prefixMap; -} - - -} From 6707e9f9b4869012785a9f607923b8993578bd90 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 6 Feb 2026 01:12:37 +0000 Subject: [PATCH 088/124] Cleanup, simplify and comment the FilterPrefixMap class --- tools/include/FilterDefaultSection.h | 2 +- tools/include/FilterPrefixmap.h | 13 +++++++--- tools/src/FilterPrefixmap.cpp | 38 +++++++++++++++++++++++++--- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/tools/include/FilterDefaultSection.h b/tools/include/FilterDefaultSection.h index 5eca3a0..e0c91ea 100644 --- a/tools/include/FilterDefaultSection.h +++ b/tools/include/FilterDefaultSection.h @@ -32,7 +32,7 @@ class FilterDefaultSection } private: - // Map of prefixes to FilterPrefixMapEntry objects, from the parsed prefixmap attribute. + // Container for the parsed prefixmap attribute. FilterPrefixMap m_prefixMap; }; diff --git a/tools/include/FilterPrefixmap.h b/tools/include/FilterPrefixmap.h index 2213e78..f59b050 100644 --- a/tools/include/FilterPrefixmap.h +++ b/tools/include/FilterPrefixmap.h @@ -12,21 +12,28 @@ namespace ResourceTools { -// Class representing the prefixmap attribute. +// ------------------------------------------------------------- +// Description: +// FilterPrefixMap is a class that represents the "prefixmap" +// attribute of a [DEFAULT] section inside a filter .ini file. +// ------------------------------------------------------------- class FilterPrefixMap { public: FilterPrefixMap() = default; + // Constructs a FilterPrefixMap object from a raw prefixmap string explicit FilterPrefixMap( const std::string& rawPrefixMap ); + // Gets the map of keyed prefixes to FilterPrefixMapEntry objects const std::map& GetMapEntries() const; private: + // Called from constructor to parse the raw prefixmap string and populate m_prefixMapEntries. + void ParsePrefixMap( const std::string& rawPrefixMap ); + // Map of prefixes to FilterPrefixMapEntry objects. std::map m_prefixMapEntries; - - void ParsePrefixMap( const std::string& rawPrefixMap ); }; } diff --git a/tools/src/FilterPrefixmap.cpp b/tools/src/FilterPrefixmap.cpp index f6bba17..a3f50d0 100644 --- a/tools/src/FilterPrefixmap.cpp +++ b/tools/src/FilterPrefixmap.cpp @@ -8,24 +8,48 @@ namespace ResourceTools { +// ------------------------------------------------------------- +// Description: +// Constructs a FilterPrefixMap object from a raw prefixmap string +// by calling the ParsePrefixMap() private function. +// Arguments: +// rawPrefixMap - string representation of the raw prefixmap attribute +// ------------------------------------------------------------- FilterPrefixMap::FilterPrefixMap( const std::string& rawPrefixMap ) { - m_prefixMapEntries.clear(); - ParsePrefixMap( rawPrefixMap ); } +// ------------------------------------------------------------- +// Description: +// Gets the map of keyed prefixes to FilterPrefixMapEntry objects. +// The FilterPrefixMapEntry objects contain a set of parsed paths for each prefix. +// Return Value: +// map of prefixes to FilterPrefixMapEntry objects +// ------------------------------------------------------------- const std::map& FilterPrefixMap::GetMapEntries() const { return m_prefixMapEntries; } +// ------------------------------------------------------------- +// Description: +// Parses the raw prefixmap string and populates m_prefixMapEntries +// with the corresponding FilterPrefixMapEntry objects. +// The raw prefixmap string is expected to be in the format: +// "prefix:path1;path2 prefix2:path3" +// The function throws std::invalid_argument if format is invalid. +// Arguments: +// rawPrefixMap - string representation of the raw prefixmap attribute +// Return Value: +// None (void) +// ------------------------------------------------------------- void FilterPrefixMap::ParsePrefixMap( const std::string& rawPrefixMap ) { std::size_t pos = 0; while( pos < rawPrefixMap.size() ) { - // Find the prefix (or error out if missing a colon) + // Find the prefix (or error out if missing a colon ":") std::size_t colon = rawPrefixMap.find( ':', pos ); if( colon == std::string::npos ) { @@ -43,7 +67,9 @@ void FilterPrefixMap::ParsePrefixMap( const std::string& rawPrefixMap ) // Find end of paths (next whitespace or end of string) std::size_t nextSpace = rawPrefixMap.find_first_of( " \t\r\n", pos ); - std::string rawPaths = ( nextSpace == std::string::npos ) ? rawPrefixMap.substr( pos ) : rawPrefixMap.substr( pos, nextSpace - pos ); + std::string rawPaths = ( nextSpace == std::string::npos ) ? + rawPrefixMap.substr( pos ) : + rawPrefixMap.substr( pos, nextSpace - pos ); if( rawPaths.empty() ) { @@ -64,12 +90,16 @@ void FilterPrefixMap::ParsePrefixMap( const std::string& rawPrefixMap ) // Go to the next token in the rawPrefixMap (or break if at end) if( nextSpace == std::string::npos ) + { break; + } pos = nextSpace + 1; // There was a whitespace, skip any additional spaces as well while( pos < rawPrefixMap.size() && std::isspace( static_cast( rawPrefixMap[pos] ) ) ) + { ++pos; + } } } From ea42b88a4a3d8650cd1a911a3047c9255b0df300 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 6 Feb 2026 11:23:10 +0000 Subject: [PATCH 089/124] Cleanup, simplify and comment the FilterPrefixMapEntry class --- tools/include/FilterPrefixMapEntry.h | 16 +++++++++-- tools/include/FilterPrefixmap.h | 3 +- tools/src/FilterPrefixMapEntry.cpp | 42 ++++++++++++++++++++++++++-- tools/src/FilterPrefixmap.cpp | 2 +- 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/tools/include/FilterPrefixMapEntry.h b/tools/include/FilterPrefixMapEntry.h index 80e53a0..30ecb6d 100644 --- a/tools/include/FilterPrefixMapEntry.h +++ b/tools/include/FilterPrefixMapEntry.h @@ -10,23 +10,33 @@ namespace ResourceTools { -// Class representing all (one or more) path entries for a given prefix identifier. +// ------------------------------------------------------------- +// Description: +// FilterPrefixMapEntry is a class that represents each +// "prefix1:pathA;pathB" (or "prefix2:pathC") entry, separated by +// a whitespace, from the "prefixmap" attribute. +// Located as part of the [DEFAULT] section of a filter .ini file. +// ------------------------------------------------------------- class FilterPrefixMapEntry { public: + // Constructs a FilterPrefixMapEntry object with the given prefix and raw paths (one or more ; separated). explicit FilterPrefixMapEntry( const std::string& prefix, const std::string& rawPaths ); - // Parse rawPaths and appends to existing paths if needed. + // Parses rawPaths and appends individual path entries to the m_paths set. void AppendPaths( const std::string& prefix, const std::string& rawPaths ); + // Gets the prefix identifier for this entry. const std::string& GetPrefix() const; + // Gets the ordered set of parsed paths for this entry. const std::set& GetPaths() const; private: + // The prefix identifier for this entry. std::string m_prefix; - // The set of parsed paths (sorted). + // Ordered set of parsed paths for this prefix. std::set m_paths; }; diff --git a/tools/include/FilterPrefixmap.h b/tools/include/FilterPrefixmap.h index f59b050..d80d64c 100644 --- a/tools/include/FilterPrefixmap.h +++ b/tools/include/FilterPrefixmap.h @@ -15,7 +15,8 @@ namespace ResourceTools // ------------------------------------------------------------- // Description: // FilterPrefixMap is a class that represents the "prefixmap" -// attribute of a [DEFAULT] section inside a filter .ini file. +// attribute (e.g: "prefix1:pathA;pathB prefix2:pathC") of a +// [DEFAULT] section inside a filter .ini file. // ------------------------------------------------------------- class FilterPrefixMap { diff --git a/tools/src/FilterPrefixMapEntry.cpp b/tools/src/FilterPrefixMapEntry.cpp index 8a11588..4b49ba8 100644 --- a/tools/src/FilterPrefixMapEntry.cpp +++ b/tools/src/FilterPrefixMapEntry.cpp @@ -7,14 +7,28 @@ namespace ResourceTools { +// ------------------------------------------------------------- +// Description: +// Constructs a FilterPrefixMapEntry object for a given prefix +// and the ";" semicolon separated rawPaths string. +// Arguments: +// prefix - identifier of the prefix the paths belong to (e.g: "prefix1") +// rawPaths - string of one or more paths separated by ";" (e.g: pathA;pathB) +// ------------------------------------------------------------- FilterPrefixMapEntry::FilterPrefixMapEntry( const std::string& prefix, const std::string& rawPaths ) : m_prefix( prefix ) { - m_paths.clear(); - AppendPaths( prefix, rawPaths ); } +// ------------------------------------------------------------- +// Description: +// Parses rawPaths and appends individual path entries to the m_paths set +// for this prefix. +// Arguments: +// prefix - identifier of the prefix the paths belong to (e.g: "prefix1") +// rawPaths - string of one or more paths separated by ";" (e.g: pathA;pathB) +// ------------------------------------------------------------- void FilterPrefixMapEntry::AppendPaths( const std::string& prefix, const std::string& rawPaths ) { if( prefix != m_prefix ) @@ -23,29 +37,51 @@ void FilterPrefixMapEntry::AppendPaths( const std::string& prefix, const std::st } std::size_t pos = 0; + // Loop through rawPaths and split by semicolons to extract individual paths (in case there are many) while( pos < rawPaths.size() ) { // Split the string up by semicolons (in case of multiple paths) std::size_t semicolon = rawPaths.find( ';', pos ); - std::string path = ( semicolon == std::string::npos ) ? rawPaths.substr( pos ) : rawPaths.substr( pos, semicolon - pos ); + std::string path = ( semicolon == std::string::npos ) ? + rawPaths.substr( pos ) : + rawPaths.substr( pos, semicolon - pos ); + if( !path.empty() ) + { m_paths.insert( path ); + } if( semicolon == std::string::npos ) + { break; + } pos = semicolon + 1; } + if( m_paths.empty() ) { throw std::invalid_argument( "Invalid prefixmap format: No paths appended for prefix: " + m_prefix ); } } +// ------------------------------------------------------------- +// Description: +// Gets the prefix identifier for this entry. +// Return Value: +// String representation of the prefix identifier (e.g: "prefix1") +// ------------------------------------------------------------- const std::string& FilterPrefixMapEntry::GetPrefix() const { return m_prefix; } +// ------------------------------------------------------------- +// Description: +// Gets the ordered set of parsed paths for this entry. +// Return Value: +// A set of strings representing the paths associated with +// this prefix (e.g: {"pathA", "pathB"}) +// ------------------------------------------------------------- const std::set& FilterPrefixMapEntry::GetPaths() const { return m_paths; diff --git a/tools/src/FilterPrefixmap.cpp b/tools/src/FilterPrefixmap.cpp index a3f50d0..a8c25c4 100644 --- a/tools/src/FilterPrefixmap.cpp +++ b/tools/src/FilterPrefixmap.cpp @@ -37,7 +37,7 @@ const std::map& FilterPrefixMap::GetMapEntrie // Parses the raw prefixmap string and populates m_prefixMapEntries // with the corresponding FilterPrefixMapEntry objects. // The raw prefixmap string is expected to be in the format: -// "prefix:path1;path2 prefix2:path3" +// "prefix1:pathA;pathB prefix2:pathC" // The function throws std::invalid_argument if format is invalid. // Arguments: // rawPrefixMap - string representation of the raw prefixmap attribute From e5ed360d5f2cdcc0befd38fa367fc3e75d822bd6 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 6 Feb 2026 13:31:41 +0000 Subject: [PATCH 090/124] Cleanup, simplify and comment the FilterNamedSection class --- tools/include/FilterNamedSection.h | 28 ++++++++--- tools/src/FilterNamedSection.cpp | 80 ++++++++++++++++++++++++------ 2 files changed, 86 insertions(+), 22 deletions(-) diff --git a/tools/include/FilterNamedSection.h b/tools/include/FilterNamedSection.h index 560610c..9728e8d 100644 --- a/tools/include/FilterNamedSection.h +++ b/tools/include/FilterNamedSection.h @@ -15,34 +15,50 @@ namespace ResourceTools { +// ------------------------------------------------------------- +// Description: +// FilterNamedSection is a class that represents all the contents +// of a named section (e.g: [SomeSectionName]) from a filter .ini file. +// ------------------------------------------------------------- class FilterNamedSection { public: - explicit FilterNamedSection( std::string sectionName, - const std::string& filter, - const std::string& respaths, - const std::string& resfile, + // Constructs a FilterNamedSection object for the given section. + // Using the raw string "filter", "respaths", optional "resfile", and + // parent "prefixmap" (from [DEFAULT] section) attributes as inputs. + explicit FilterNamedSection( const std::string& sectionName, + const std::string& rawFilter, + const std::string& rawRespaths, + const std::string& rawResfile, const FilterPrefixMap& parentPrefixMap ); + // Return the name of this section (e.g: [SomeSectionName]) from the .ini file. const std::string& GetSectionName() const; - // Return combined resolved path map from both respaths and optional resfile + // Return the combined resolved path map from both the "respaths" and optional "resfile" attributes. + // This is the main function to use to get the final resolved paths and their associated filters for this section. const std::map& GetCombinedResolvedPathMap(); + // Return the resolved path map from the "respaths" attribute. Only used in tests to verify correctness of data. const std::map& GetResolvedRespathsMap() const; + // Return the resolved path map from the optional "resfile" attribute. Only used in tests to verify correctness of data. const std::map& GetResolvedResfileMap() const; private: + // The name of this section (e.g: [SomeSectionName]) from the .ini file. std::string m_sectionName; + // The parsed "filter" attribute for this named section. FilterResourceFilter m_filter; + // The parsed "respaths" attribute. FilterResourcePathFile m_respaths; + // The optional parsed "resfile" attribute. std::optional m_resfile; - // Combined map of fully resolved respaths and resfile FilterResourceFilter objects + // Combined map of fully resolved "respaths" and "resfile" FilterResourceFilter objects std::map m_resolvedCombinedPathMap; }; diff --git a/tools/src/FilterNamedSection.cpp b/tools/src/FilterNamedSection.cpp index d205e60..4a3611b 100644 --- a/tools/src/FilterNamedSection.cpp +++ b/tools/src/FilterNamedSection.cpp @@ -7,51 +7,79 @@ namespace ResourceTools { -FilterNamedSection::FilterNamedSection( std::string sectionName, - const std::string& filter, - const std::string& respaths, - const std::string& resfile, +// ------------------------------------------------------------- +// Description: +// Constructs a FilterNamedSection object for a named section +// within a filter .ini file. +// Arguments: +// sectionName - name of the section (e.g: [SomeSectionName]) +// rawFilter - the "filter" attribute for this section (e.g: "[ .yaml .txt ]") +// respaths - the "respaths" attribute of this section (e.g: "prefix:/someSubPath/*") +// resfile - the optional "resfile" attribute of this section (e.g: "prefix:/pathToSomeFile.txt") +// parentPrefixMap - the FilterPrefixMap from the [DEFAULT] section of the .ini file, +// used to resolve prefixes in "respaths" and "resfile" attributes to actual paths. +// ------------------------------------------------------------- +FilterNamedSection::FilterNamedSection( const std::string& sectionName, + const std::string& rawFilter, + const std::string& rawRespaths, + const std::string& rawResfile, const FilterPrefixMap& parentPrefixMap ) : - m_sectionName( std::move( sectionName ) ), - m_filter( filter, true ), // isToplevelFilter = true - m_respaths( respaths, parentPrefixMap, m_filter ), - m_resfile( resfile.empty() ? std::nullopt : std::make_optional( resfile, parentPrefixMap, m_filter ) ) + m_sectionName( sectionName ), + m_filter( rawFilter, true ), // When constructing a [NamedSection], isToplevelFilter should always be true (as it's not an inline filter) + m_respaths( rawRespaths, parentPrefixMap, m_filter ), + m_resfile( rawResfile.empty() ? std::nullopt : std::make_optional( rawResfile, parentPrefixMap, m_filter ) ) { - m_resolvedCombinedPathMap.clear(); - - if( respaths.empty() ) + if( rawRespaths.empty() ) { throw std::invalid_argument( "Respaths attribute is empty for section: " + m_sectionName ); } } +// ------------------------------------------------------------- +// Description: +// Gets the name of this section (e.g: [SomeSectionName]) from the .ini file. +// Return Value: +// String representing the name of the section +// ------------------------------------------------------------- const std::string& FilterNamedSection::GetSectionName() const { return m_sectionName; } +// ------------------------------------------------------------- +// Description: +// Gets the combined resolved path map from both the "respaths" and optional "resfile" attributes. +// This is the main getter function to use for the full resolved paths +// and their associated filters from within this section. +// Return Value: +// Map of resolved paths to their associated FilterResourceFilter objects. +// Note: +// The combined map from both the "respaths" and "resfile" attributes +// is populated during the first call to this function (and then cached). +// ------------------------------------------------------------- const std::map& FilterNamedSection::GetCombinedResolvedPathMap() { // Only populate the Combined map if not already done so. if( m_resolvedCombinedPathMap.empty() ) { - // Populate the combined map. + // Populate the combined map with "respaths" attribute entries first (as they are non-optional) for( const auto& kv : m_respaths.GetResolvedPathMap() ) { m_resolvedCombinedPathMap.insert_or_assign( kv.first, kv.second ); } - // Add resfile to the combined map + // Add the "resfile" attribute entries to the combined map (in case there are any). + // Make sure to combine any filters if the same key already exists from the "respaths" attribute. if( m_resfile ) { - // Allow "resfile" to contain multiple entries (future proofing) + // Allow "resfile" to contain multiple entries (future proofing it, not currently utilized as such in existing .ini filter files) for( const auto& kv : m_resfile->GetResolvedPathMap() ) { - // Combine filters of both if same key already exists + // Combine filters of both if same key already exists (using the raw filter strings). + // Else just add the "resfile" attribue entry to the combined map as is. auto it = m_resolvedCombinedPathMap.find( kv.first ); if( it != m_resolvedCombinedPathMap.end() ) { - // Combine the filters (using raw filter strings) std::string combinedRawFilter = it->second.GetRawFilter() + " " + kv.second.GetRawFilter(); FilterResourceFilter combinedFilter( combinedRawFilter ); m_resolvedCombinedPathMap.insert_or_assign( kv.first, combinedFilter ); @@ -67,11 +95,31 @@ const std::map& FilterNamedSection::GetCombin return m_resolvedCombinedPathMap; } +// ------------------------------------------------------------- +// Description: +// Gets the resolved path map from the "respaths" attribute only. +// Return Value: +// Map of resolved paths to their associated FilterResourceFilter objects. +// Note: +// Users of this class should use the GetCombinedResolvedPathMap() +// function instead of this one to get the "full combined" result. +// This function is exposed only to enable tests to verify correctness of data. +// ------------------------------------------------------------- const std::map& FilterNamedSection::GetResolvedRespathsMap() const { return m_respaths.GetResolvedPathMap(); } +// ------------------------------------------------------------- +// Description: +// Gets the resolved path map from the optional "resfile" attribute only. +// Return Value: +// Map of resolved paths to their associated FilterResourceFilter objects. +// Note: +// Users of this class should use the GetCombinedResolvedPathMap() +// function instead of this one to get the "full combined" result. +// This function is exposed only to enable tests to verify correctness of data. +// ------------------------------------------------------------- const std::map& FilterNamedSection::GetResolvedResfileMap() const { if( m_resfile ) From 11653dea0f27ea32646cbbbafd4f085dd01d0244 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 6 Feb 2026 14:49:01 +0000 Subject: [PATCH 091/124] Make sure to use C++ 17 for arm64-osx (was set to 20) --- customized_toolchain/toolchains/arm64-osx-carbon.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/customized_toolchain/toolchains/arm64-osx-carbon.cmake b/customized_toolchain/toolchains/arm64-osx-carbon.cmake index adb6dc9..9e39308 100644 --- a/customized_toolchain/toolchains/arm64-osx-carbon.cmake +++ b/customized_toolchain/toolchains/arm64-osx-carbon.cmake @@ -3,7 +3,7 @@ if (NOT _CCP_TOOLCHAIN_FILE_LOADED) set(_CCP_TOOLCHAIN_FILE_LOADED 1) set (VCPKG_USE_HOST_TOOLS ON CACHE STRING "") - set (CMAKE_CXX_STANDARD 20 CACHE STRING "") + set (CMAKE_CXX_STANDARD 17 CACHE STRING "") set (CMAKE_CXX_STANDARD_REQUIRED ON CACHE STRING "") set (CMAKE_CXX_EXTENSIONS OFF CACHE STRING "") set (CMAKE_POSITION_INDEPENDENT_CODE ON CACHE STRING "") From d55fe537382a93fb6e3cfc2420cd1905254e710c Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 6 Feb 2026 15:06:26 +0000 Subject: [PATCH 092/124] Re-organize Filter tests as part of ResourceToolsTests --- tests/CMakeLists.txt | 10 +- tests/src/ResourceFilterTest.h | 14 -- ...erTest.cpp => ResourceToolsFilterTest.cpp} | 158 +++++++++--------- tests/src/ResourceToolsLibraryTest.cpp | 6 +- tests/src/ResourceToolsTest.h | 13 ++ 5 files changed, 99 insertions(+), 102 deletions(-) delete mode 100644 tests/src/ResourceFilterTest.h rename tests/src/{ResourceFilterTest.cpp => ResourceToolsFilterTest.cpp} (93%) create mode 100644 tests/src/ResourceToolsTest.h diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e49e30e..1cb7b6c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,15 +7,15 @@ find_package(tiny-process-library CONFIG REQUIRED) include(GoogleTest) set(SRC_FILES - src/ResourcesTestFixture.cpp - src/ResourcesTestFixture.h src/CliTestFixture.cpp src/CliTestFixture.h - src/ResourcesLibraryTest.cpp src/ResourcesCliTest.cpp + src/ResourcesLibraryTest.cpp + src/ResourcesTestFixture.cpp + src/ResourcesTestFixture.h + src/ResourceToolsFilterTest.cpp src/ResourceToolsLibraryTest.cpp - src/ResourceFilterTest.cpp - src/ResourceFilterTest.h + src/ResourceToolsTest.h ) add_executable(resources-test ${SRC_FILES}) diff --git a/tests/src/ResourceFilterTest.h b/tests/src/ResourceFilterTest.h deleted file mode 100644 index 12a635c..0000000 --- a/tests/src/ResourceFilterTest.h +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright © 2025 CCP ehf. - -#pragma once -#ifndef ResourceFilterTest_H -#define ResourceFilterTest_H - -#include "ResourcesTestFixture.h" - -// Inherit from ResourcesTestFixture to gain access to file and directory helper functions -class ResourceFilterTest : public ResourcesTestFixture -{ -}; - -#endif // ResourceFilterTest_H \ No newline at end of file diff --git a/tests/src/ResourceFilterTest.cpp b/tests/src/ResourceToolsFilterTest.cpp similarity index 93% rename from tests/src/ResourceFilterTest.cpp rename to tests/src/ResourceToolsFilterTest.cpp index 0a44a04..760f686 100644 --- a/tests/src/ResourceFilterTest.cpp +++ b/tests/src/ResourceToolsFilterTest.cpp @@ -1,6 +1,6 @@ // Copyright © 2025 CCP ehf. -#include "ResourceFilterTest.h" +#include "ResourceToolsTest.h" #include #include @@ -18,7 +18,7 @@ #include "FilterResourcePathFile.h" #include "ResourceFilter.h" -TEST_F( ResourceFilterTest, LoadAndParseIniFile_example1Ini_UsingIniReader ) +TEST_F( ResourceToolsTest, LoadAndParseIniFile_example1Ini_UsingIniReader ) { // Use the test fixture's helper to get the absolute path const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/example1.ini" ); @@ -47,7 +47,7 @@ TEST_F( ResourceFilterTest, LoadAndParseIniFile_example1Ini_UsingIniReader ) // ----------------------------------------- -TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_IncludeFilterOnly ) +TEST_F( ResourceToolsTest, FilterResourceFilter_ApplyRule_IncludeFilterOnly ) { ResourceTools::FilterResourceFilter filter( "[ .this .is .included ]" ); const auto& includes = filter.GetIncludeFilter(); @@ -59,7 +59,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_IncludeFilterOnly ) EXPECT_TRUE( excludes.empty() ); } -TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_TopLevelExcludeFilterOnly ) +TEST_F( ResourceToolsTest, FilterResourceFilter_ApplyRule_TopLevelExcludeFilterOnly ) { // Top-level filter refers to the "filter" attribute of a NamedSection. // When there is no include filter specified at the top-level "filter" attribute, @@ -77,7 +77,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_TopLevelExcludeFilter EXPECT_EQ( includes[0], "*" ); } -TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_InLineExcludeFilterOnly ) +TEST_F( ResourceToolsTest, FilterResourceFilter_ApplyRule_InLineExcludeFilterOnly ) { // "InLine" filter refers to an optional filter element at the end of a // respaths/resfile attribute line (class FilterResourcePathFileEntry) @@ -95,7 +95,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_InLineExcludeFilterOn EXPECT_TRUE( includes.empty() ); } -TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRules_UseComplexIncludeExcludeFilters ) +TEST_F( ResourceToolsTest, FilterResourceFilter_ApplyRules_UseComplexIncludeExcludeFilters ) { ResourceTools::FilterResourceFilter filter( "[ .red .gr2 .dds .png .yaml ] [ .txt ] ![ .csv .xls ] [ .bat .sh ] ![ .blk .yel ]" ); const auto& includes = filter.GetIncludeFilter(); @@ -107,7 +107,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRules_UseComplexIncludeExc EXPECT_EQ( excludes, expectedExcludes ) << "Exclude filters do not match expected values"; } -TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_SimpleIncludeFilterOnly ) +TEST_F( ResourceToolsTest, FilterResourceFilter_ApplyRule_SimpleIncludeFilterOnly ) { ResourceTools::FilterResourceFilter filter( "[ .red ]" ); const auto& includes = filter.GetIncludeFilter(); @@ -116,7 +116,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_SimpleIncludeFilterOn EXPECT_EQ( includes[0], ".red" ); } -TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_SimpleExcludeFilterOnly ) +TEST_F( ResourceToolsTest, FilterResourceFilter_ApplyRule_SimpleExcludeFilterOnly ) { ResourceTools::FilterResourceFilter filter( "![ .blk ]" ); const auto& excludes = filter.GetExcludeFilter(); @@ -125,7 +125,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_SimpleExcludeFilterOn EXPECT_EQ( excludes[0], ".blk" ); } -TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_CombineIncludeExcludeIncludeFilters ) +TEST_F( ResourceToolsTest, FilterResourceFilter_ApplyRule_CombineIncludeExcludeIncludeFilters ) { // This test, combines multiple include and exclude filters, to test the logic of adding/removing filter elements: // Include .in1 and .in2 @@ -143,7 +143,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_CombineIncludeExclude EXPECT_EQ( excludes, expectedExcludes ); } -TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_MissingClosingIncludeBracketBeforeNextOpenExcludeBracket ) +TEST_F( ResourceToolsTest, FilterResourceFilter_CheckFailure_MissingClosingIncludeBracketBeforeNextOpenExcludeBracket ) { try { @@ -162,7 +162,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_MissingClosingIncl } } -TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_ExcludeMarkerWithoutOpenBracket ) +TEST_F( ResourceToolsTest, FilterResourceFilter_CheckFailure_ExcludeMarkerWithoutOpenBracket ) { try { @@ -181,7 +181,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_ExcludeMarkerWitho } } -TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_ExcludeMarkerWithoutOpenBracketAfterValidIncludeFilter ) +TEST_F( ResourceToolsTest, FilterResourceFilter_CheckFailure_ExcludeMarkerWithoutOpenBracketAfterValidIncludeFilter ) { try { @@ -200,7 +200,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_ExcludeMarkerWitho } } -TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_ExcludeMarkerWithoutBracketAfterValidIncludeAndExcludeFilters ) +TEST_F( ResourceToolsTest, FilterResourceFilter_CheckFailure_ExcludeMarkerWithoutBracketAfterValidIncludeAndExcludeFilters ) { try { @@ -219,7 +219,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_ExcludeMarkerWitho } } -TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_MissingOpeningIncludeBracketAtStart ) +TEST_F( ResourceToolsTest, FilterResourceFilter_CheckFailure_MissingOpeningIncludeBracketAtStart ) { try { @@ -237,7 +237,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_MissingOpeningIncl } } -TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_MissingOpeningIncludeBracketForSecondIncludeFilter ) +TEST_F( ResourceToolsTest, FilterResourceFilter_CheckFailure_MissingOpeningIncludeBracketForSecondIncludeFilter ) { try { @@ -256,7 +256,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_MissingOpeningIncl } } -TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_MissingClosingIncludeBracket ) +TEST_F( ResourceToolsTest, FilterResourceFilter_CheckFailure_MissingClosingIncludeBracket ) { try { @@ -274,7 +274,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_MissingClosingIncl } } -TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_MissingClosingIncludeBracketForSecondIncludeFilter ) +TEST_F( ResourceToolsTest, FilterResourceFilter_CheckFailure_MissingClosingIncludeBracketForSecondIncludeFilter ) { try { @@ -292,7 +292,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_MissingClosingIncl } } -TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_MissingClosingIncludeBracketForMiddleIncludeFilter ) +TEST_F( ResourceToolsTest, FilterResourceFilter_CheckFailure_MissingClosingIncludeBracketForMiddleIncludeFilter ) { try { @@ -310,7 +310,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_CheckFailure_MissingClosingIncl } } -TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_UseCondensedValidIncludeExcludeIncludeFilters ) +TEST_F( ResourceToolsTest, FilterResourceFilter_ApplyRule_UseCondensedValidIncludeExcludeIncludeFilters ) { // This test filter combines multiple include and exclude filters without extra whitespaces. // Done to test the logic of adding/removing filter elements and that the parsing logic is not dependent on whitespaces. @@ -324,7 +324,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_UseCondensedValidIncl EXPECT_EQ( excludes, expectedExcludes ); } -TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_UseCondensedValidExcludeIncludeExcludeFilters ) +TEST_F( ResourceToolsTest, FilterResourceFilter_ApplyRule_UseCondensedValidExcludeIncludeExcludeFilters ) { // This test filter combines multiple include and exclude filters without extra whitespaces. // Done to test the logic of adding/removing filter elements and that the parsing logic is not dependent on whitespaces. @@ -338,7 +338,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_UseCondensedValidExcl EXPECT_EQ( excludes, expectedExcludes ); } -TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_EmptyTopLevelFilter ) +TEST_F( ResourceToolsTest, FilterResourceFilter_ApplyRule_EmptyTopLevelFilter ) { // When the top-level "filter" attribute of a NamedSection is empty, // a wild-card "*" should be added to the include filter by default. @@ -354,7 +354,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_EmptyTopLevelFilter ) EXPECT_TRUE( excludes.empty() ); } -TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_EmptyInLineFilter ) +TEST_F( ResourceToolsTest, FilterResourceFilter_ApplyRule_EmptyInLineFilter ) { // When an in-line filter of (respaths/resfile line) is empty (the default behavior), // no wildcard "*" should be added to the include filter. @@ -371,7 +371,7 @@ TEST_F( ResourceFilterTest, FilterResourceFilter_ApplyRule_EmptyInLineFilter ) // ----------------------------------------- -TEST_F( ResourceFilterTest, FilterPrefixMap_Validate_SinglePrefixWithMultiplePathsIsAllowed ) +TEST_F( ResourceToolsTest, FilterPrefixMap_Validate_SinglePrefixWithMultiplePathsIsAllowed ) { // This test validates that a single prefix (prefix1) with multiple different paths // is correctly parsed and stored in the map. @@ -389,7 +389,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_Validate_SinglePrefixWithMultiplePat EXPECT_EQ( it->second.GetPaths(), expected ) << "Paths do not match expected values"; } -TEST_F( ResourceFilterTest, FilterPrefixMap_Validate_MultipleDifferentPrefixesAreAllowed ) +TEST_F( ResourceToolsTest, FilterPrefixMap_Validate_MultipleDifferentPrefixesAreAllowed ) { // This test validates that multiple different prefixes (prefix1 & prefix2) // with their associated paths are correctly parsed and stored in the map. @@ -413,7 +413,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_Validate_MultipleDifferentPrefixesAr EXPECT_EQ( it2->second.GetPaths(), expected2 ) << "Paths do not match expected values"; } -TEST_F( ResourceFilterTest, FilterPrefixMap_Validate_DuplicateSamePrefixWithPathsInDifferentOrderIsAllowed ) +TEST_F( ResourceToolsTest, FilterPrefixMap_Validate_DuplicateSamePrefixWithPathsInDifferentOrderIsAllowed ) { // This test validates that if the same prefix (prefix1) is defined multiple times, // with same paths but in different order (path1+path2 & path2+path1). @@ -435,7 +435,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_Validate_DuplicateSamePrefixWithPath EXPECT_EQ( it->first, it->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; } -TEST_F( ResourceFilterTest, FilterPrefixMap_Validate_MultipleSamePrefixesCanAppendToPaths ) +TEST_F( ResourceToolsTest, FilterPrefixMap_Validate_MultipleSamePrefixesCanAppendToPaths ) { // This test validates that if the same prefix (prefix1) is defined multiple times (first and last), // with different paths (path2+path1 & path3+path1), that the paths are combined and stored in the map without duplicates. @@ -459,7 +459,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_Validate_MultipleSamePrefixesCanAppe EXPECT_EQ( it2->first, it2->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; } -TEST_F( ResourceFilterTest, FilterPrefixMap_Validate_DifferentWhitespacesBetweenPrefixesAreAllowed ) +TEST_F( ResourceToolsTest, FilterPrefixMap_Validate_DifferentWhitespacesBetweenPrefixesAreAllowed ) { // This test validates that different whitespaces (space, tab, new line) // between prefix definitions are handled correctly. @@ -489,7 +489,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_Validate_DifferentWhitespacesBetween EXPECT_EQ( it3->first, it3->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; } -TEST_F( ResourceFilterTest, FilterPrefixMap_CheckFailure_MissingColonAfterPrefixBeforePaths ) +TEST_F( ResourceToolsTest, FilterPrefixMap_CheckFailure_MissingColonAfterPrefixBeforePaths ) { // This test validates that if a prefix definition is missing a colon ":" // after the prefix and before the paths section, that an exception is thrown. @@ -508,7 +508,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_CheckFailure_MissingColonAfterPrefix } } -TEST_F( ResourceFilterTest, FilterPrefixMap_CheckFailure_MissingPrefixBeforePaths ) +TEST_F( ResourceToolsTest, FilterPrefixMap_CheckFailure_MissingPrefixBeforePaths ) { // This test validates that if a prefix definition is missing the prefix itself // (lhs of colon) before the paths section, that an exception is thrown. @@ -527,7 +527,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_CheckFailure_MissingPrefixBeforePath } } -TEST_F( ResourceFilterTest, FilterPrefixMap_CheckFailure_MissingPathsAfterPrefix ) +TEST_F( ResourceToolsTest, FilterPrefixMap_CheckFailure_MissingPathsAfterPrefix ) { // This test validates that if a prefix definition is missing the paths section // (rhs of colon) after the prefix, that an exception is thrown. @@ -546,7 +546,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMap_CheckFailure_MissingPathsAfterPrefix } } -TEST_F( ResourceFilterTest, FilterPrefixMapEntry_CheckFailure_PrefixMissingInMapWhenAppendingPath ) +TEST_F( ResourceToolsTest, FilterPrefixMapEntry_CheckFailure_PrefixMissingInMapWhenAppendingPath ) { // This test validates that when trying to append paths to a FilterPrefixMapEntry // with a different prefix than the existing one of the mapEntry that an exception is thrown. @@ -566,7 +566,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMapEntry_CheckFailure_PrefixMissingInMap } } -TEST_F( ResourceFilterTest, FilterPrefixMapEntry_CheckFailure_EmptyPathWhenAppendingPathToPrefix ) +TEST_F( ResourceToolsTest, FilterPrefixMapEntry_CheckFailure_EmptyPathWhenAppendingPathToPrefix ) { // This test validates that when trying to append an empty path to a FilterPrefixMapEntry // that an exception is thrown, since empty paths are not allowed. @@ -587,7 +587,7 @@ TEST_F( ResourceFilterTest, FilterPrefixMapEntry_CheckFailure_EmptyPathWhenAppen // ----------------------------------------- -TEST_F( ResourceFilterTest, FilterDefaultSection_Validate_DefaultSectionWithMultiplePrefixesIsAllowed ) +TEST_F( ResourceToolsTest, FilterDefaultSection_Validate_DefaultSectionWithMultiplePrefixesIsAllowed ) { // This test validates that a FilterDefaultSection can be initialized // with multiple different prefixes and their associated paths. @@ -611,7 +611,7 @@ TEST_F( ResourceFilterTest, FilterDefaultSection_Validate_DefaultSectionWithMult EXPECT_EQ( it2->first, it2->second.GetPrefix() ) << "Value of FilterPrefixMap.m_prefixMap key does not match associated FilterPrefixMapEntry.m_prefix"; } -TEST_F( ResourceFilterTest, FilterDefaultSection_CheckFailure_InitializeWithMissingColonInPrefixmap ) +TEST_F( ResourceToolsTest, FilterDefaultSection_CheckFailure_InitializeWithMissingColonInPrefixmap ) { // This test validates that when trying to initialize a FilterDefaultSection // with a prefixmap string missing a colon ":" after the prefix definition @@ -631,7 +631,7 @@ TEST_F( ResourceFilterTest, FilterDefaultSection_CheckFailure_InitializeWithMiss } } -TEST_F( ResourceFilterTest, FilterDefaultSection_CheckFailure_InitializeWithEmptyPrefixInPrefixmap ) +TEST_F( ResourceToolsTest, FilterDefaultSection_CheckFailure_InitializeWithEmptyPrefixInPrefixmap ) { // This test validates that when trying to initialize a FilterDefaultSection // with a prefixmap string missing the prefix definition (empty prefix) @@ -714,7 +714,7 @@ void ValidatePathMap( const std::set& expectedPaths, // ----------------------------------------- -TEST_F( ResourceFilterTest, FilterResourcePathFile_Validate_SingleLineAttribute_WithNoInlineFilterIsAllowed ) +TEST_F( ResourceToolsTest, FilterResourcePathFile_Validate_SingleLineAttribute_WithNoInlineFilterIsAllowed ) { // This test validates that a FilterResourcePathFile can be initialized with a single line attribute // that contains a prefix and path, but no in-line filter. @@ -735,7 +735,7 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_Validate_SingleLineAttribute_ ValidatePathMap( expectedPaths, resolvedPathMap, expectedIncludes, expectedExcludes ); } -TEST_F( ResourceFilterTest, FilterResourcePathFile_Validate_SingleLineAttribute_WithInlineFilterNoOverridesIsAllowed ) +TEST_F( ResourceToolsTest, FilterResourcePathFile_Validate_SingleLineAttribute_WithInlineFilterNoOverridesIsAllowed ) { // This test validates that a FilterResourcePathFile can be initialized with a single line attribute // that contains a prefix and path, with an in-line filter that does not override any of the parent filters. @@ -756,7 +756,7 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_Validate_SingleLineAttribute_ ValidatePathMap( expectedPaths, resolvedPathMap, expectedIncludes, expectedExcludes ); } -TEST_F( ResourceFilterTest, FilterResourcePathFile_Validate_SingleLineAttribute_WithInlineFilterOverridingParentFilterIsAllowed ) +TEST_F( ResourceToolsTest, FilterResourcePathFile_Validate_SingleLineAttribute_WithInlineFilterOverridingParentFilterIsAllowed ) { // This test validates that a FilterResourcePathFile can be initialized with a single // line attribute with an in-line filter that overrides some of the parent filters by @@ -779,7 +779,7 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_Validate_SingleLineAttribute_ ValidatePathMap( expectedPaths, resolvedPathMap, expectedIncludes, expectedExcludes ); } -TEST_F( ResourceFilterTest, FilterResourcePathFile_Validate_MultiLineAttribute_WithMixedInlineFilterOverridesIsAllowed ) +TEST_F( ResourceToolsTest, FilterResourcePathFile_Validate_MultiLineAttribute_WithMixedInlineFilterOverridesIsAllowed ) { // This test validates that a FilterResourcePathFile can be initialized with a multi-line attribute // that contains multiple prefixes and paths. @@ -834,7 +834,7 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_Validate_MultiLineAttribute_W } } -TEST_F( ResourceFilterTest, FilterResourcePathFile_Validate_SingleLineAttribute_WithDuplicateIncludeExcludeInlineOverridesIsAllowed ) +TEST_F( ResourceToolsTest, FilterResourcePathFile_Validate_SingleLineAttribute_WithDuplicateIncludeExcludeInlineOverridesIsAllowed ) { // This test validates that a FilterResourcePathFile can be initialized with a single line attribute. // With an in-line filter that has duplicate include and exclude entries (some same as parent filters). @@ -857,7 +857,7 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_Validate_SingleLineAttribute_ ValidatePathMap( expectedPaths, resolvedPathMap, expectedIncludes, expectedExcludes ); } -TEST_F( ResourceFilterTest, FilterResourcePathFile_CheckFailure_MissingPrefixThrowsException ) +TEST_F( ResourceToolsTest, FilterResourcePathFile_CheckFailure_MissingPrefixThrowsException ) { // This test validates that when trying to initialize a FilterResourcePathFile with an // invalid raw attribute string (missing prefix), that an exception is thrown. @@ -870,7 +870,7 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_CheckFailure_MissingPrefixThr EXPECT_THROW( ResourceTools::FilterResourcePathFile pathFile( rawPathFileAttrib, prefixMap, parentFilter ), std::invalid_argument ); } -TEST_F( ResourceFilterTest, FilterResourcePathFile_CheckFailure_UnknownPrefixThrowsException ) +TEST_F( ResourceToolsTest, FilterResourcePathFile_CheckFailure_UnknownPrefixThrowsException ) { // This test validates that when trying to initialize a FilterResourcePathFile with an // invalid raw attribute string (unknown prefix, not in the prefix map), that an exception is thrown. @@ -883,7 +883,7 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_CheckFailure_UnknownPrefixThr EXPECT_THROW( ResourceTools::FilterResourcePathFile pathFile( rawPathFileAttrib, prefixMap, parentFilter ), std::invalid_argument ); } -TEST_F( ResourceFilterTest, FilterResourcePathFile_CheckFailure_MalformedInlineFilterThrowsException ) +TEST_F( ResourceToolsTest, FilterResourcePathFile_CheckFailure_MalformedInlineFilterThrowsException ) { // This test validates that when trying to initialize a FilterResourcePathFile with an // invalid raw attribute string (malformed in-line filter), that an exception is thrown. @@ -898,7 +898,7 @@ TEST_F( ResourceFilterTest, FilterResourcePathFile_CheckFailure_MalformedInlineF // ----------------------------------------- -TEST_F( ResourceFilterTest, FilterNamedSection_Validate_SingleLineRespathIsAllowed ) +TEST_F( ResourceToolsTest, FilterNamedSection_Validate_SingleLineRespathIsAllowed ) { // This test validates that a FilterNamedSection can be initialized with a single line respath. std::string sectionName = "FilterNamedSection_Validate_SingleLineRespathIsAllowed"; @@ -923,7 +923,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Validate_SingleLineRespathIsAllow ValidatePathMap( expectedPaths, combinedMap, expectedIncludes, expectedExcludes, "CombinedResolvedPathMap" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_Validate_EmptyTopLevelFilterIsAllowed ) +TEST_F( ResourceToolsTest, FilterNamedSection_Validate_EmptyTopLevelFilterIsAllowed ) { // This test validates that a FilterNamedSection can be initialized with an empty filter at top-level, // which should add a wildcard "*" include filter. @@ -949,7 +949,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Validate_EmptyTopLevelFilterIsAll ValidatePathMap( expectedPaths, combinedMap, expectedIncludes, expectedExcludes, "CombinedResolvedPathMap" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_Validate_OnlyTopLevelExcludeFilterIsAllowed ) +TEST_F( ResourceToolsTest, FilterNamedSection_Validate_OnlyTopLevelExcludeFilterIsAllowed ) { // This test validates that a FilterNamedSection can be initialized with only an exclude filter at top-level, // which should add a wildcard "*" include filter. @@ -975,7 +975,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Validate_OnlyTopLevelExcludeFilte ValidatePathMap( expectedPaths, combinedMap, expectedIncludes, expectedExcludes, "CombinedResolvedPathMap" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_Validate_MultiLineRespathWithSomeInlineOverridesIsAllowed ) +TEST_F( ResourceToolsTest, FilterNamedSection_Validate_MultiLineRespathWithSomeInlineOverridesIsAllowed ) { // This test validates that a FilterNamedSection can be initialized with a multi-line respath attribute. // Some lines may have in-line filters that override the top-level filter, while others just use the top-level filter as-is. @@ -1011,7 +1011,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Validate_MultiLineRespathWithSome ValidatePathMap( secondLinePaths, combinedMap, defaultIncludes, defaultExcludes, "SecondLine ResolvedRespathsMap" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_Validate_CombinedSingleLineRespathAndResfileAttributesIsAllowed ) +TEST_F( ResourceToolsTest, FilterNamedSection_Validate_CombinedSingleLineRespathAndResfileAttributesIsAllowed ) { // This test validates that a FilterNamedSection can be initialized with both respath and resfile attributes. // The combined resolved map contains entries from both attributes. @@ -1049,7 +1049,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Validate_CombinedSingleLineRespat } -TEST_F( ResourceFilterTest, FilterNamedSection_Validate_RespathWithoutResfileAttributeIsAllowed ) +TEST_F( ResourceToolsTest, FilterNamedSection_Validate_RespathWithoutResfileAttributeIsAllowed ) { // This test validates that a FilterNamedSection can be initialized with only // a respath attribute and no resfile attribute. @@ -1082,7 +1082,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Validate_RespathWithoutResfileAtt ValidatePathMap( onlyValidPaths, combinedMap, defaultIncludes, defaultExcludes, "ResolvedCombinedMap" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_CheckFailure_MissingRespathAttributeThrowsException ) +TEST_F( ResourceToolsTest, FilterNamedSection_CheckFailure_MissingRespathAttributeThrowsException ) { // This test validates that when trying to initialize a FilterNamedSection // with a missing respath attribute, that an exception is thrown. @@ -1109,7 +1109,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_CheckFailure_MissingRespathAttrib } } -TEST_F( ResourceFilterTest, FilterNamedSection_Validate_CombinedResolvedMapOnSamePrefixIsAllowed ) +TEST_F( ResourceToolsTest, FilterNamedSection_Validate_CombinedResolvedMapOnSamePrefixIsAllowed ) { // This test validates that a FilterNamedSection can be initialized with both // respath and resfile attributes using the same prefix, and that the combined @@ -1147,7 +1147,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Validate_CombinedResolvedMapOnSam ValidatePathMap( combinedPaths, combinedMap, defaultIncludes, defaultExcludes, "ResolvedCombinedMap" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_Validate_CombinedResolvedMapWithEmptyTopLevelFilterIsAllowed ) +TEST_F( ResourceToolsTest, FilterNamedSection_Validate_CombinedResolvedMapWithEmptyTopLevelFilterIsAllowed ) { // This test validates that a FilterNamedSection can be initialized with both // respath and resfile attributes using the same prefix, and an empty top-level filter. @@ -1193,7 +1193,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Validate_CombinedResolvedMapWithE } } -TEST_F( ResourceFilterTest, FilterNamedSection_Validate_CombinedResolvedMapWithOnlyExcludeTopLevelFilterIsAllowed ) +TEST_F( ResourceToolsTest, FilterNamedSection_Validate_CombinedResolvedMapWithOnlyExcludeTopLevelFilterIsAllowed ) { // This test validates that a FilterNamedSection can be initialized with both // respath and resfile attributes using the same prefix, and only an exclude filter at top-level. @@ -1239,7 +1239,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Validate_CombinedResolvedMapWithO } } -TEST_F( ResourceFilterTest, FilterNamedSection_Validate_EmptyTopLevelFilterWithRespathOverrideIsAllowed ) +TEST_F( ResourceToolsTest, FilterNamedSection_Validate_EmptyTopLevelFilterWithRespathOverrideIsAllowed ) { // This test validates that a FilterNamedSection can be initialized with both // respath and resfile attributes using the same prefix, and an empty top-level filter. @@ -1301,7 +1301,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Validate_EmptyTopLevelFilterWithR } } -TEST_F( ResourceFilterTest, FilterNamedSection_Validate_EmptyTopLevelFilterWithResfileOverrideIsAllowed ) +TEST_F( ResourceToolsTest, FilterNamedSection_Validate_EmptyTopLevelFilterWithResfileOverrideIsAllowed ) { // This test validates that a FilterNamedSection can be initialized with both // respath and resfile attributes using the same prefix, and an empty top-level filter. @@ -1363,7 +1363,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Validate_EmptyTopLevelFilterWithR } } -TEST_F( ResourceFilterTest, FilterNamedSection_Validate_OnlyExcludeTopLevelFilterWithRespathOverrideIsAllowed ) +TEST_F( ResourceToolsTest, FilterNamedSection_Validate_OnlyExcludeTopLevelFilterWithRespathOverrideIsAllowed ) { // This test validates that a FilterNamedSection can be initialized with both // respath and resfile attributes using the same prefix, and only an exclude filter at top-level. @@ -1427,7 +1427,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Validate_OnlyExcludeTopLevelFilte } } -TEST_F( ResourceFilterTest, FilterNamedSection_Validate_OnlyExcludeTopLevelFilterWithResfileOverrideIsAllowed ) +TEST_F( ResourceToolsTest, FilterNamedSection_Validate_OnlyExcludeTopLevelFilterWithResfileOverrideIsAllowed ) { // This test validates that a FilterNamedSection can be initialized with both // respath and resfile attributes using the same prefix, and only an exclude filter at top-level. @@ -1491,7 +1491,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Validate_OnlyExcludeTopLevelFilte } } -TEST_F( ResourceFilterTest, FilterNamedSection_Validate_CombinedMapWithResfileOverrideIsAllowed ) +TEST_F( ResourceToolsTest, FilterNamedSection_Validate_CombinedMapWithResfileOverrideIsAllowed ) { // This test validates that a FilterNamedSection can be initialized with both // respath and resfile attributes using the same prefix, and that the combined @@ -1534,7 +1534,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Validate_CombinedMapWithResfileOv ValidatePathMap( allPaths, respathsAgainMap, defaultIncludes, allExcludes, "ResolvedRespathsMap-Again" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_Validate_NoTopLevelFilterSameCombinedMapWithResfileOverrideIsAllowed ) +TEST_F( ResourceToolsTest, FilterNamedSection_Validate_NoTopLevelFilterSameCombinedMapWithResfileOverrideIsAllowed ) { // This test validates that a FilterNamedSection can be initialized with both // respath and resfile attributes using the same prefix and only default "*" top-level filter. @@ -1586,7 +1586,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Validate_NoTopLevelFilterSameComb ValidatePathMap( allPaths, respathsAgainMap, defaultIncludes, allExcludes, "ResolvedRespathsMap-Again" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_Validate_NoTopLevelFilterSameCombinedMapWithRespathOverrideIsAllowed ) +TEST_F( ResourceToolsTest, FilterNamedSection_Validate_NoTopLevelFilterSameCombinedMapWithRespathOverrideIsAllowed ) { // This test validates that a FilterNamedSection can be initialized with both // respath and resfile attributes using the same prefix and only default "*" top-level filter. @@ -1642,7 +1642,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Validate_NoTopLevelFilterSameComb ValidatePathMap( allPaths, resfileAgainMap, defaultIncludes, allExcludes, "ResolvedResfileMap-Again" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_Validate_OnlyExcludeFilterSameCombinedMapWithResfileOverrideIsAllowed ) +TEST_F( ResourceToolsTest, FilterNamedSection_Validate_OnlyExcludeFilterSameCombinedMapWithResfileOverrideIsAllowed ) { // This test validates that a FilterNamedSection can be initialized with both respath and resfile // attributes using the same prefix, and only an exclude filter at top-level (and default "*" include). @@ -1694,7 +1694,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Validate_OnlyExcludeFilterSameCom ValidatePathMap( allPaths, respathsAgainMap, defaultIncludes, allExcludes, "ResolvedRespathsMap-Again" ); } -TEST_F( ResourceFilterTest, FilterNamedSection_Validate_OnlyExcludeFilterSameCombinedMapWithRespathsOverrideIsAllowed ) +TEST_F( ResourceToolsTest, FilterNamedSection_Validate_OnlyExcludeFilterSameCombinedMapWithRespathsOverrideIsAllowed ) { // This test validates that a FilterNamedSection can be initialized with both respath and resfile // attributes using the same prefix, and only an exclude filter at top-level (and default "*" include). @@ -1752,7 +1752,7 @@ TEST_F( ResourceFilterTest, FilterNamedSection_Validate_OnlyExcludeFilterSameCom // ------------------------------------------ -TEST_F( ResourceFilterTest, FilterResourceFile_ValidateSuccessfulFileLoad_example1_ini ) +TEST_F( ResourceToolsTest, FilterResourceFile_ValidateSuccessfulFileLoad_example1_ini ) { // This test validates that the example1.ini file can be loaded successfully // and that the resolved paths match the expected values. @@ -1790,7 +1790,7 @@ TEST_F( ResourceFilterTest, FilterResourceFile_ValidateSuccessfulFileLoad_exampl } } -TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_invalidMissingDefaultSection_ini ) +TEST_F( ResourceToolsTest, FilterResourceFile_ConfirmFileLoadFailure_invalidMissingDefaultSection_ini ) { // This test validates that loading an ini file missing the required [DEFAULT] // section throws an exception. @@ -1811,7 +1811,7 @@ TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_invalidMis } } -TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_invalidMissingNamedSection_ini ) +TEST_F( ResourceToolsTest, FilterResourceFile_ConfirmFileLoadFailure_invalidMissingNamedSection_ini ) { // This test validates that loading an ini file missing any named sections // throws an exception since at least one named section is required. @@ -1832,7 +1832,7 @@ TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_invalidMis } } -TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_iniFileDoesNotExist ) +TEST_F( ResourceToolsTest, FilterResourceFile_ConfirmFileLoadFailure_iniFileDoesNotExist ) { // This test validates that loading a non-existent ini file throws an exception. const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/iniFileNotFound.ini" ); @@ -1852,7 +1852,7 @@ TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_iniFileDoe } } -TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_invalidPrefixmap_ini ) +TEST_F( ResourceToolsTest, FilterResourceFile_ConfirmFileLoadFailure_invalidPrefixmap_ini ) { // This test validates that loading an ini file with an invalid prefixmap format throws an exception. const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidPrefixmap.ini" ); @@ -1872,7 +1872,7 @@ TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_invalidPre } } -TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_invalidSectionFilter_ini ) +TEST_F( ResourceToolsTest, FilterResourceFile_ConfirmFileLoadFailure_invalidSectionFilter_ini ) { // This test validates that loading an ini file with an invalid section filter format throws an exception. const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidSectionFilter.ini" ); @@ -1892,7 +1892,7 @@ TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_invalidSec } } -TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_invalidInlineFilter_ini ) +TEST_F( ResourceToolsTest, FilterResourceFile_ConfirmFileLoadFailure_invalidInlineFilter_ini ) { // This test validates that loading an ini file with an invalid inline filter format throws an exception. const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidInlineFilter.ini" ); @@ -1912,7 +1912,7 @@ TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_invalidInl } } -TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_invalidPrefixMismatch_ini ) +TEST_F( ResourceToolsTest, FilterResourceFile_ConfirmFileLoadFailure_invalidPrefixMismatch_ini ) { // This test validates that loading an ini file with a prefix in the respaths/resfile // attributes that does not match any prefix in the prefixmap throws an exception. @@ -1935,7 +1935,7 @@ TEST_F( ResourceFilterTest, FilterResourceFile_ConfirmFileLoadFailure_invalidPre // ------------------------------------------ -TEST_F( ResourceFilterTest, ResourceFilter_ConfirmFileLoadFailure_SingleIniFileThatDoesNotExist ) +TEST_F( ResourceToolsTest, ResourceFilter_ConfirmFileLoadFailure_SingleIniFileThatDoesNotExist ) { // This test validates that initializing a ResourceFilter with a non-existent ini file throws an exception. const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/noSuchFile.ini" ); @@ -1959,7 +1959,7 @@ TEST_F( ResourceFilterTest, ResourceFilter_ConfirmFileLoadFailure_SingleIniFileT } } -TEST_F( ResourceFilterTest, ResourceFilter_ConfirmFileLoadFailure_MultipleFilesOneThatDoesNotExist ) +TEST_F( ResourceToolsTest, ResourceFilter_ConfirmFileLoadFailure_MultipleFilesOneThatDoesNotExist ) { // This test validates that initializing a ResourceFilter with multiple ini files // where one is valid and another does not exist, results in an exception being thrown. @@ -1985,7 +1985,7 @@ TEST_F( ResourceFilterTest, ResourceFilter_ConfirmFileLoadFailure_MultipleFilesO } } -TEST_F( ResourceFilterTest, ResourceFilter_ConfirmSuccessfulFileLoad_example1_ini ) +TEST_F( ResourceToolsTest, ResourceFilter_ConfirmSuccessfulFileLoad_example1_ini ) { // This test validates that initializing a ResourceFilter with a valid ini file (example1.ini) // successfully loads without throwing any exceptions. @@ -2007,7 +2007,7 @@ TEST_F( ResourceFilterTest, ResourceFilter_ConfirmSuccessfulFileLoad_example1_in } } -TEST_F( ResourceFilterTest, ResourceFilter_Validate_RaiiClassCurrentWorkingDirectoryChanger_ChangesWorkingDirectoryForDurationOfTest ) +TEST_F( ResourceToolsTest, ResourceFilter_Validate_RaiiClassCurrentWorkingDirectoryChanger_ChangesWorkingDirectoryForDurationOfTest ) { // This test validates that the RAII CurrentWorkingDirectoryChanger class correctly changes // the current working directory for the duration of the test. @@ -2052,7 +2052,7 @@ TEST_F( ResourceFilterTest, ResourceFilter_Validate_RaiiClassCurrentWorkingDirec ASSERT_NE( std::filesystem::current_path().lexically_normal().string(), testDataPath.lexically_normal().string() ) << "Current working directory should not be the TEST_DATA_BASE_PATH"; } -TEST_F( ResourceFilterTest, ResourceFilter_ValidateSuccessfulFileLoadUsingRelativePaths_validSimpleExample1_ini ) +TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingRelativePaths_validSimpleExample1_ini ) { // This test validates that initializing a ResourceFilter with a valid ini file (validSimpleExample1.ini), // using relative paths, loads successfully and the expected paths and filters are present. @@ -2097,7 +2097,7 @@ TEST_F( ResourceFilterTest, ResourceFilter_ValidateSuccessfulFileLoadUsingRelati } } -TEST_F( ResourceFilterTest, ResourceFilter_ValidateSuccessfulFileLoadUsingAbsolutePaths_validSimpleExample1_ini ) +TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingAbsolutePaths_validSimpleExample1_ini ) { // This test validates that initializing a ResourceFilter with a valid ini file (validSimpleExample1.ini), // using absolute paths, loads successfully and the expected paths and filters are present. @@ -2143,7 +2143,7 @@ TEST_F( ResourceFilterTest, ResourceFilter_ValidateSuccessfulFileLoadUsingAbsolu } } -TEST_F( ResourceFilterTest, ResourceFilter_ValidateSuccessfulFileLoadUsingRelativePaths_validComplexExample1_ini ) +TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingRelativePaths_validComplexExample1_ini ) { // This test validates that initializing a ResourceFilter with a valid ini file (validComplexExample1.ini), // using relative paths, loads successfully and the expected paths and filters are present. @@ -2254,7 +2254,7 @@ TEST_F( ResourceFilterTest, ResourceFilter_ValidateSuccessfulFileLoadUsingRelati } } -TEST_F( ResourceFilterTest, ResourceFilter_ValidateSuccessfulFileLoadUsingAbsolutePath_validComplexExample1_ini ) +TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingAbsolutePath_validComplexExample1_ini ) { // This test validates that initializing a ResourceFilter with a valid ini file (validComplexExample1.ini), // using absolute paths, loads successfully and the expected paths and filters are present. @@ -2365,7 +2365,7 @@ TEST_F( ResourceFilterTest, ResourceFilter_ValidateSuccessfulFileLoadUsingAbsolu } } -TEST_F( ResourceFilterTest, ResourceFilter_ValidateSuccessfulLoadOf2IniFiles_validComplexExample1_and_validSimpleExample1 ) +TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulLoadOf2IniFiles_validComplexExample1_and_validSimpleExample1 ) { // This test validates that initializing a ResourceFilter with two valid ini files // (validComplexExample1.ini and validSimpleExample1.ini), using relative paths, diff --git a/tests/src/ResourceToolsLibraryTest.cpp b/tests/src/ResourceToolsLibraryTest.cpp index 85ff3ad..8c1e160 100644 --- a/tests/src/ResourceToolsLibraryTest.cpp +++ b/tests/src/ResourceToolsLibraryTest.cpp @@ -1,5 +1,7 @@ // Copyright © 2025 CCP ehf. +#include "ResourceToolsTest.h" + #include #include #include @@ -21,10 +23,6 @@ #include "Patching.h" #include "RollingChecksum.h" -struct ResourceToolsTest : public ResourcesTestFixture -{ -}; - TEST_F( ResourceToolsTest, Md5ChecksumGeneration ) { std::string input = "Dummy"; diff --git a/tests/src/ResourceToolsTest.h b/tests/src/ResourceToolsTest.h new file mode 100644 index 0000000..a2a9e07 --- /dev/null +++ b/tests/src/ResourceToolsTest.h @@ -0,0 +1,13 @@ +// Copyright © 2025 CCP ehf. + +#pragma once +#ifndef ResourceToolsTest_H +#define ResourceToolsTest_H + +#include "ResourcesTestFixture.h" + +class ResourceToolsTest : public ResourcesTestFixture +{ +}; + +#endif // ResourceToolsTest_H \ No newline at end of file From a1b2fc19225777568f12d8a10dd71a3c51b25a8f Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 6 Feb 2026 15:13:20 +0000 Subject: [PATCH 093/124] Adding gtest include in ResourceToolsFilterTest.cpp --- tests/src/ResourceToolsFilterTest.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/src/ResourceToolsFilterTest.cpp b/tests/src/ResourceToolsFilterTest.cpp index 760f686..003c32e 100644 --- a/tests/src/ResourceToolsFilterTest.cpp +++ b/tests/src/ResourceToolsFilterTest.cpp @@ -7,6 +7,8 @@ #include #include +#include + #include "INIReader.h" #include "FilterDefaultSection.h" From e365e6b3c6dc28ad8502fa300e2130c6f3ee5801 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 6 Feb 2026 15:26:53 +0000 Subject: [PATCH 094/124] Cleanup, simplify and comment the FilterResourceFilter class --- tools/include/FilterNamedSection.h | 4 +- tools/include/FilterResourceFilter.h | 33 ++++++++--- tools/src/FilterResourceFilter.cpp | 83 +++++++++++++++++++++------- 3 files changed, 90 insertions(+), 30 deletions(-) diff --git a/tools/include/FilterNamedSection.h b/tools/include/FilterNamedSection.h index 9728e8d..13b721b 100644 --- a/tools/include/FilterNamedSection.h +++ b/tools/include/FilterNamedSection.h @@ -17,8 +17,8 @@ namespace ResourceTools // ------------------------------------------------------------- // Description: -// FilterNamedSection is a class that represents all the contents -// of a named section (e.g: [SomeSectionName]) from a filter .ini file. +// FilterNamedSection is a class that represents all the contents +// of a named section (e.g: [SomeSectionName]) from a filter .ini file. // ------------------------------------------------------------- class FilterNamedSection { diff --git a/tools/include/FilterResourceFilter.h b/tools/include/FilterResourceFilter.h index 2f4760e..804c591 100644 --- a/tools/include/FilterResourceFilter.h +++ b/tools/include/FilterResourceFilter.h @@ -10,35 +10,52 @@ namespace ResourceTools { -// Class representing a resource filter with include and exclude filters. -// - This is for filter attribute of a NamedSection AND the "combined resolved" filter for each respaths/resfile line +// ------------------------------------------------------------- +// Description: +// FilterResourceFilter is a class that represents the parsed +// include and exclude filters for either a topLevel or inLine filter. +// ------------------------------------------------------------- class FilterResourceFilter { public: FilterResourceFilter() = default; + // Construct a FilterResourceFilter object by parsing the given raw filter string. + // isTopLevelFilter: + // - true = filter is from the "filter" attribute of a [NamedSection] + // - false = filter is an inline filter of a respaths/resfile line entry. explicit FilterResourceFilter( const std::string& rawFilter, bool isToplevelFilter = false ); - // Used as input when constructing a combined resolved filter for a respaths/resfile line. + // Getters for the raw filter string. + // Needed to construct combined filters for respaths/resfile attribute line entries. const std::string& GetRawFilter() const; + // Getters for the parsed include filter vector. const std::vector& GetIncludeFilter() const; + // Getters for the parsed exclude filter vector. const std::vector& GetExcludeFilter() const; private: + void ParseFilters(); + + // Static helper function placing filter tokens in the correct include/exclude vector. + static void PlaceTokenInCorrectVector( const std::string& token, std::vector& fromVector, std::vector& toVector ); + + // Indicates whether this FilterResourceFilter is a top-level filter, + // i.e. from the "filter" attribute (true) of a [NamedSection] + // or an inline filter (false) i.e. from a respaths/resfile line "prefix1:pathA [ newInclude ]". bool m_isToplevelFilter = false; + // The raw filter string as read from the .ini file (e.g: "[ .yaml .txt ] ![ .exe ]"). + // Stored to enable easy concatenation of filters when constructing combined resolved filters. std::string m_rawFilter; + // Vector of the parsed include filter tokens, e.g: { ".yaml", ".txt" }. std::vector m_includeFilter; + // Vector of the parsed exclude filter tokens, e.g: { ".exe" }. std::vector m_excludeFilter; - - void ParseFilters(); - - // Static helper placing tokens in the correct vector, moving it if need be. - static void PlaceTokenInCorrectVector( const std::string& token, std::vector& fromVector, std::vector& toVector ); }; } diff --git a/tools/src/FilterResourceFilter.cpp b/tools/src/FilterResourceFilter.cpp index 1c603ad..9782d44 100644 --- a/tools/src/FilterResourceFilter.cpp +++ b/tools/src/FilterResourceFilter.cpp @@ -9,6 +9,15 @@ namespace ResourceTools { +// ------------------------------------------------------------- +// Description: +// Construct a FilterResourceFilter object by parsing the given raw filter string. +// Arguments: +// rawFilter - the raw filter string to parse (e.g: "[ .yaml .txt ] ![ .exe ]") +// isTopLevelFilter - indicates whether the level of the filter: +// - true = filter is from the "filter" attribute of a [NamedSection] +// - false = (default value) filter is an inline filter of a respaths/resfile line entry. +// ------------------------------------------------------------- FilterResourceFilter::FilterResourceFilter( const std::string& rawFilter, bool isToplevelFilter /* = false */ ) : m_rawFilter( rawFilter ), m_isToplevelFilter( isToplevelFilter ) @@ -16,47 +25,57 @@ FilterResourceFilter::FilterResourceFilter( const std::string& rawFilter, bool i ParseFilters(); } +// ------------------------------------------------------------- +// Description: +// Gets the raw filter string attribute from the .ini file, +// e.g: "[ .yaml .txt ] ![ .exe ]", either topLevel or inLine. +// Return Value: +// The raw string representation of the filter. +// Note: +// This function is needed as input to easily construct combined +// filters for "topLevel parent" and respaths/resfile attribute +// line filter entries, from both of their raw representation. +// ------------------------------------------------------------- const std::string& FilterResourceFilter::GetRawFilter() const { return m_rawFilter; } +// ------------------------------------------------------------- +// Description: +// Gets the parsed include filter vector. +// Return Value: +// Vector of strings representing the include filter tokens, e.g: { ".yaml", ".txt" }. +// ------------------------------------------------------------- const std::vector& FilterResourceFilter::GetIncludeFilter() const { return m_includeFilter; } +// ------------------------------------------------------------- +// Description: +// Gets the parsed exclude filter vector. +// Return Value: +// Vector of strings representing the exclude filter tokens, e.g: { ".exclude" }. +// ------------------------------------------------------------- const std::vector& FilterResourceFilter::GetExcludeFilter() const { return m_excludeFilter; } -void FilterResourceFilter::PlaceTokenInCorrectVector( const std::string& token, std::vector& fromVector, std::vector& toVector ) -{ - // Remove token from the fromVector if present - auto it = std::find( fromVector.begin(), fromVector.end(), token ); - if( it != fromVector.end() ) - { - fromVector.erase( it ); - } - - // Add token to the toVector if not already present in it. - if( std::find( toVector.begin(), toVector.end(), token ) == toVector.end() ) - { - toVector.push_back( token ); - } -} - +// ------------------------------------------------------------- +// Description: +// Parses the raw filter string into the include and exclude +// filter vectors and places it in the correct vector. +// The raw filter string is expected to be in the format of one or more sections of the form: +// ------------------------------------------------------------- void FilterResourceFilter::ParseFilters() { - m_includeFilter.clear(); - m_excludeFilter.clear(); - std::string s = m_rawFilter; size_t pos = 0; while( pos < s.size() ) { - // Skip whitespace + // Skip whitespaces while( pos < s.size() && std::isspace( static_cast( s[pos] ) ) ) { ++pos; @@ -141,4 +160,28 @@ void FilterResourceFilter::ParseFilters() } } +// ------------------------------------------------------------- +// Description: +// Static helper function placing filter tokens in the correct include/exclude vector. +// Arguments: +// token - the filter token to place in the correct vector (e.g: ".yaml") +// fromVector - the vector to remove the token from (if present) +// toVector - the vector to add the token to (if not already present in it) +// ------------------------------------------------------------- +void FilterResourceFilter::PlaceTokenInCorrectVector( const std::string& token, std::vector& fromVector, std::vector& toVector ) +{ + // Remove token from the fromVector if present + auto it = std::find( fromVector.begin(), fromVector.end(), token ); + if( it != fromVector.end() ) + { + fromVector.erase( it ); + } + + // Add token to the toVector if not already present in it. + if( std::find( toVector.begin(), toVector.end(), token ) == toVector.end() ) + { + toVector.push_back( token ); + } +} + } From 6660d0297999ae50fd1f3e977d896a181f7a9aa3 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 6 Feb 2026 15:37:22 +0000 Subject: [PATCH 095/124] Cleanup, simplify and comment the FilterResourceFilter class (take 2) --- tools/include/FilterResourceFilter.h | 1 + tools/src/FilterResourceFilter.cpp | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/include/FilterResourceFilter.h b/tools/include/FilterResourceFilter.h index 804c591..d1fb42c 100644 --- a/tools/include/FilterResourceFilter.h +++ b/tools/include/FilterResourceFilter.h @@ -37,6 +37,7 @@ class FilterResourceFilter const std::vector& GetExcludeFilter() const; private: + // Parse the incoming m_rawFilter string and populate the m_includeFilter and m_excludeFilter vectors accordingly. void ParseFilters(); // Static helper function placing filter tokens in the correct include/exclude vector. diff --git a/tools/src/FilterResourceFilter.cpp b/tools/src/FilterResourceFilter.cpp index 9782d44..30e1a5c 100644 --- a/tools/src/FilterResourceFilter.cpp +++ b/tools/src/FilterResourceFilter.cpp @@ -67,7 +67,8 @@ const std::vector& FilterResourceFilter::GetExcludeFilter() const // Description: // Parses the raw filter string into the include and exclude // filter vectors and places it in the correct vector. -// The raw filter string is expected to be in the format of one or more sections of the form: +// Return Value: +// None (void). // ------------------------------------------------------------- void FilterResourceFilter::ParseFilters() { @@ -167,6 +168,8 @@ void FilterResourceFilter::ParseFilters() // token - the filter token to place in the correct vector (e.g: ".yaml") // fromVector - the vector to remove the token from (if present) // toVector - the vector to add the token to (if not already present in it) +// Return Value: +// None (void). // ------------------------------------------------------------- void FilterResourceFilter::PlaceTokenInCorrectVector( const std::string& token, std::vector& fromVector, std::vector& toVector ) { From 15871ddd8e5e458cc6af7e52226d3029c633340a Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Fri, 6 Feb 2026 17:00:48 +0000 Subject: [PATCH 096/124] Cleanup, simplify and comment the FilterResourcePathFileEntry class --- tools/include/FilterResourcePathFileEntry.h | 22 ++-- tools/src/FilterResourcePathFileEntry.cpp | 106 ++++++++++++++------ 2 files changed, 89 insertions(+), 39 deletions(-) diff --git a/tools/include/FilterResourcePathFileEntry.h b/tools/include/FilterResourcePathFileEntry.h index 5bb645c..9a6f7a7 100644 --- a/tools/include/FilterResourcePathFileEntry.h +++ b/tools/include/FilterResourcePathFileEntry.h @@ -13,34 +13,44 @@ namespace ResourceTools { +// ------------------------------------------------------------- +// Description: +// FilterResourcePathFileEntry is a class that represents a single line entry +// from a resfile/respaths attribute in a filter .ini file. +// Note: +// The raw representation of it from a filter .ini file can be: +// - "prefix:/pathPart/..." (sub-folder wildcard, without an inline filter) +// - "prefix:/pathPart/* [ .txt ] ![ .yaml] " (current folder wildcard, with an optional inline include and exclude filter) +// ------------------------------------------------------------- class FilterResourcePathFileEntry { public: - explicit FilterResourcePathFileEntry( std::string rawPathLine, + // Construct a FilterResourcePathFileEntry object for a single resfile/respaths attribute line entry. + explicit FilterResourcePathFileEntry( const std::string& rawPathLine, const FilterPrefixMap& parentPrefixMap, const FilterResourceFilter& parentSectionFilter ); + // Gets the (possibly combined) filter for this resfile/respaths attribute line entry. const FilterResourceFilter& GetEntryFilter() const; + // Gets the resolved path set for this resfile/respaths attribute line entry. const std::set& GetResolvedPaths() const; private: - std::string m_rawPathLine; - // The "parent" prefix map from the [DEFAULT] section const FilterPrefixMap& m_parentPrefixMap; // The "parent" filter from the [namedSection] const FilterResourceFilter& m_parentSectionFilter; - // The combined filter for this resfile/respaths attribute line, built from the parentSectionFilter and any inline filter. + // The combined filter for this resfile/respaths attribute line entry. FilterResourceFilter m_entryFilter; // The set of resolved paths (sorted). std::set m_resolvedPaths; - // Parse the m_rawPathLine by constructing the combined m_entryFilter and append paths to m_resolvedPaths based on the m_parentPrefixMap - void ParseRawPathLine(); + // Parse the rawPathLine and constructing the m_entryFilter and m_resolvedPaths + void ParseRawPathLine( const std::string& rawPathLine ); }; } diff --git a/tools/src/FilterResourcePathFileEntry.cpp b/tools/src/FilterResourcePathFileEntry.cpp index 0d2e4e4..2225f23 100644 --- a/tools/src/FilterResourcePathFileEntry.cpp +++ b/tools/src/FilterResourcePathFileEntry.cpp @@ -8,39 +8,84 @@ namespace ResourceTools { -FilterResourcePathFileEntry::FilterResourcePathFileEntry( std::string rawPathLine, +// ------------------------------------------------------------- +// Description: +// Construct a FilterResourcePathFileEntry object for a single resfile/respaths attribute line entry. +// Arguments: +// rawPathLine - the raw string for this line entry from the .ini file +// e.g: "prefix1:/pathA/* [ .txt ] ![ .yaml] +// parentPrefixMap - the FilterPrefixMap from the [DEFAULT] section, +// used to resolve prefixes in this line entry to actual paths. +// parentSectionFilter - the FilterResourceFilter from the same +// [namedSection] as this resfile/respaths attribute. +// Needed to create actual filter (with optional inline one) for this line entry. +// ------------------------------------------------------------- +FilterResourcePathFileEntry::FilterResourcePathFileEntry( const std::string& rawPathLine, const FilterPrefixMap& parentPrefixMap, const FilterResourceFilter& parentSectionFilter ) : - m_rawPathLine( std::move( rawPathLine ) ), m_parentPrefixMap( parentPrefixMap ), m_parentSectionFilter( parentSectionFilter ) { - ParseRawPathLine(); + ParseRawPathLine( rawPathLine ); } - +// ------------------------------------------------------------- +// Description: +// Gets the (possibly combined) filter for this resfile/respaths attribute line entry. +// Return Value: +// FilterResourceFilter object representing the combined +// include/exclude filters for this line entry. +// ------------------------------------------------------------- const FilterResourceFilter& FilterResourcePathFileEntry::GetEntryFilter() const { return m_entryFilter; } +// ------------------------------------------------------------- +// Description: +// Get resolved paths set for this resfile/respaths attribute line entry. +// Return Value: +// Set of strings representing the resolved relative paths for this line entry. +// ------------------------------------------------------------- const std::set& FilterResourcePathFileEntry::GetResolvedPaths() const { return m_resolvedPaths; } -void FilterResourcePathFileEntry::ParseRawPathLine() +// ------------------------------------------------------------- +// Description: +// Parses the rawPathLine and constructs/populates the +// m_entryFilter and m_resolvedPaths members. +// Arguments: +// rawPathLine - the raw string for this line entry +// e.g: "prefix1:/pathA/* [ .txt ] ![ .yaml]" +// ------------------------------------------------------------- +void FilterResourcePathFileEntry::ParseRawPathLine( const std::string& rawPathLine ) { - // Split on whitespace: first token is pathPart, rest is (optional) filterPart - std::string rawPathToken; - std::string rawOptionalFilterPart; + std::string rawPrefixPathToken; std::string combinedRawFilter; - std::istringstream iss( m_rawPathLine ); - iss >> rawPathToken; + // Split on whitespace: + // - first token is the prefix:pathPart, + // - rest would be the (optional) filterPart (to be read at later stage) + std::istringstream iss( rawPathLine ); + iss >> rawPrefixPathToken; + + // Validate that the rawPathToken is of the correct format "prefix:pathPart" + size_t colon = rawPrefixPathToken.find( ':' ); + if( colon == std::string::npos ) + { + throw std::invalid_argument( std::string( "Missing prefix in path for: " ) + rawPathLine ); + } + std::string prefixPart = rawPrefixPathToken.substr( 0, colon ); + std::string pathPart = rawPrefixPathToken.substr( colon + 1 ); + + // Now figure out which filter to use (inline or parent) if( !iss.eof() ) { - // There is an optional filter part. Construct it, will error out if wrong format + // There is more data, i.e. an optional filter exists. + // Construct it from the rest of the "line", will error out if filter format is wrong. + std::string rawOptionalFilterPart; std::getline( iss, rawOptionalFilterPart ); FilterResourceFilter inlineFilter = FilterResourceFilter( rawOptionalFilterPart ); @@ -52,44 +97,39 @@ void FilterResourcePathFileEntry::ParseRawPathLine() // No inline filter, use parent section filter as is combinedRawFilter = m_parentSectionFilter.GetRawFilter(); } - m_entryFilter = FilterResourceFilter( combinedRawFilter ); - // Validate the rawPathToken - size_t colon = rawPathToken.find( ':' ); - if( colon == std::string::npos ) - { - throw std::invalid_argument( std::string( "Missing prefix in path for: " ) + m_rawPathLine ); - } - std::string prefix = rawPathToken.substr( 0, colon ); - std::string rest = rawPathToken.substr( colon + 1 ); + // Construct the parsed (potentially) combined filter for this line entry. + m_entryFilter = FilterResourceFilter( combinedRawFilter ); + // Check that the prefix from the rawPathToken exists in the parent [DEFAULT] section prefix map. const auto& prefixMapEntries = m_parentPrefixMap.GetMapEntries(); - auto it = prefixMapEntries.find( prefix ); - if( it == prefixMapEntries.end() ) + auto foundPrefixMapEntry = prefixMapEntries.find( prefixPart ); + if( foundPrefixMapEntry == prefixMapEntries.end() ) { - throw std::invalid_argument( std::string( "Prefix '" ) + prefix + "' not present in prefixMap for line: " + m_rawPathLine ); + throw std::invalid_argument( std::string( "Prefix '" ) + prefixPart + "' not present in prefixMap for line: " + rawPathLine ); } - // Each FilterPrefixMapEntry may have multiple paths, combine/resolve all of them - const auto& prefixEntry = it->second; - const auto& prefixPaths = prefixEntry.GetPaths(); - for( const auto& basePrefixPath : prefixPaths ) + // Each FilterPrefixMapEntry may have multiple paths, combine/resolve for all of those paths + const auto& prefixEntry = foundPrefixMapEntry->second; + const auto& prefixPathsSet = prefixEntry.GetPaths(); + for( const auto& basePrefixMapPath : prefixPathsSet ) { // Ensure only one '/' at the join point - bool baseEndsWithSlash = !basePrefixPath.empty() && basePrefixPath.back() == '/'; - bool restStartsWithSlash = !rest.empty() && rest.front() == '/'; - std::string resolvedPath = basePrefixPath; + bool baseEndsWithSlash = !basePrefixMapPath.empty() && basePrefixMapPath.back() == '/'; + bool restStartsWithSlash = !pathPart.empty() && pathPart.front() == '/'; + + std::string resolvedPath = basePrefixMapPath; if( baseEndsWithSlash && restStartsWithSlash ) { - resolvedPath += rest.substr( 1 ); + resolvedPath += pathPart.substr( 1 ); } else if( !baseEndsWithSlash && !restStartsWithSlash ) { - resolvedPath += '/' + rest; + resolvedPath += '/' + pathPart; } else { - resolvedPath = basePrefixPath + rest; + resolvedPath = basePrefixMapPath + pathPart; } m_resolvedPaths.insert( resolvedPath ); } From 31cf40ce42d5b1d4ffb6c0303744a8c6513ef615 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Sat, 7 Feb 2026 01:25:57 +0000 Subject: [PATCH 097/124] Cleanup, simplify and comment the FilterResourcePathFile class - Adding 2 more tests as well. --- tests/src/ResourceToolsFilterTest.cpp | 156 +++++++++++++++++- ...InlineFilterOverrideOnSameRelativePath.ini | 24 +++ ...rideDifferentRelativeSameAbsolutePaths.ini | 28 ++++ tools/include/FilterResourcePathFile.h | 19 ++- tools/src/FilterResourcePathFile.cpp | 77 +++++++-- 5 files changed, 278 insertions(+), 26 deletions(-) create mode 100644 tests/testData/ExampleIniFiles/validInlineFilterOverrideOnSameRelativePath.ini create mode 100644 tests/testData/ExampleIniFiles/validOverrideDifferentRelativeSameAbsolutePaths.ini diff --git a/tests/src/ResourceToolsFilterTest.cpp b/tests/src/ResourceToolsFilterTest.cpp index 003c32e..d50675a 100644 --- a/tests/src/ResourceToolsFilterTest.cpp +++ b/tests/src/ResourceToolsFilterTest.cpp @@ -2095,7 +2095,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingRelativ } catch( ... ) { - FAIL() << "Test [ResourceFilter_Load_validSimpleExample1_ini] failed when it should have passed."; + FAIL() << "Test failed when it should have passed."; } } @@ -2141,7 +2141,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingAbsolut } catch( ... ) { - FAIL() << "Test [ResourceFilter_Load_validSimpleExample1_ini_usingAbsolutePaths] failed when it should have passed."; + FAIL() << "Test failed when it should have passed."; } } @@ -2248,11 +2248,11 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingRelativ } catch( const std::exception& e ) { - FAIL() << "Test [ResourceFilter_Load_validComplexExample1_ini_usingRelativePaths] failed with: " << e.what(); + FAIL() << "Test failed with: " << e.what(); } catch( ... ) { - FAIL() << "Test [ResourceFilter_Load_validComplexExample1_ini_usingRelativePaths] failed when it should have passed."; + FAIL() << "Test failed when it should have passed."; } } @@ -2359,11 +2359,11 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingAbsolut } catch( const std::exception& e ) { - FAIL() << "Test [ResourceFilter_Load_validComplexExample1_ini_usingRelativePaths] failed with: " << e.what(); + FAIL() << "Test failed with: " << e.what(); } catch( ... ) { - FAIL() << "Test [ResourceFilter_Load_validComplexExample1_ini_usingRelativePaths] failed when it should have passed."; + FAIL() << "Test failed when it should have passed."; } } @@ -2487,10 +2487,148 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulLoadOf2IniFiles_vali } catch( const std::exception& e ) { - FAIL() << "Test [ResourceFilter_Load2iniFiles_validComplexExample1_and_validSimpleExample1] failed with: " << e.what(); + FAIL() << "Test failed with: " << e.what(); } catch( ... ) { - FAIL() << "Test [ResourceFilter_Load2iniFiles_validComplexExample1_and_validSimpleExample1] failed when it should have passed."; + FAIL() << "Test failed when it should have passed."; } -} \ No newline at end of file +} + +TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccess_FileLoadOverrideUsingDifferentRelativeButSameAbsolutePaths_validOverrideDifferentRelativeSameAbsolutePaths_ini ) +{ + // This test validates that initializing a ResourceFilter with a valid ini file (validOverrideDifferentRelativeSameAbsolutePaths.ini), + // using two distinct relative paths (which result in the same absolute path), loads successfully and the expected paths and filters are present. + // See notes in the validOverrideDifferentRelativeSameAbsolutePaths.ini file for further details. + + // Alter the current working directory for the duration of this test + CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); + try + { + const std::filesystem::path iniPath = "ExampleIniFiles/validOverrideDifferentRelativeSameAbsolutePaths.ini"; + std::vector paths = { iniPath }; + ResourceTools::ResourceFilter resourceFilter; + resourceFilter.Initialize( paths ); + + // Validate correct included and exclude paths via the resourceFilter: + // - Because of the two distinct relative paths (./resourcesOnBranch/* and /resourcesOnBranch/*) + // where one has both as include and the other both as exclude, then priority rules say that + // both of the files should be EXCLUDED (i.e. equal priority means that exclude wins) + std::set validIncludeResolvedRelativePaths = {}; + std::set validExcludeResolvedRelativePaths = { + "resourcesOnBranch/introMovie.txt", + "resourcesOnBranch/videoCardCategories.yaml" + }; + + ASSERT_EQ( resourceFilter.HasFilters(), true ); + for( const auto& resolvedRelativeIncludePath : validIncludeResolvedRelativePaths ) + { + ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativeIncludePath ), true ) << "Should have included relative path: " << resolvedRelativeIncludePath.generic_string(); + } + for( const auto& resolvedRelativeExcludePath : validExcludeResolvedRelativePaths ) + { + ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativeExcludePath ), false ) << "Should have excluded relative path: " << resolvedRelativeExcludePath.generic_string(); + } + + // Additional checks to make sure the FullResolvedPathMap contains correct include/exclude filter data + const auto& fullPathMap = resourceFilter.GetFullResolvedPathMap(); + ASSERT_EQ( fullPathMap.size(), 2 ); + + std::set expectedPaths = { + "./resourcesOnBranch/*", + "resourcesOnBranch/*" + }; + MapContainsPaths( expectedPaths, fullPathMap, "FullResolvedPathMap from validOverrideDifferentRelativeSameAbsolutePaths.ini" ); + + for( const auto& kv : fullPathMap ) + { + if( kv.first == "./resourcesOnBranch/*" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 0 ); + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 2 ); + EXPECT_TRUE( std::find( kv.second.GetExcludeFilter().begin(), kv.second.GetExcludeFilter().end(), ".yaml" ) != kv.second.GetExcludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetExcludeFilter().begin(), kv.second.GetExcludeFilter().end(), ".txt" ) != kv.second.GetExcludeFilter().end() ); + } + else if( kv.first == "resourcesOnBranch/*" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 0 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".yaml" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".txt" ) != kv.second.GetIncludeFilter().end() ); + } + else + { + FAIL() << "Unexpected path found in FullResolvedPathMap: " << kv.first; + } + } + } + catch( ... ) + { + FAIL() << "Test failed when it should have passed."; + } +} + +TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccess_FileLoadRespathInlineOverrides_validInlineFilterOverrideOnSameRelativePath_ini ) +{ + // This test validates that initializing a ResourceFilter with a valid ini file (validInlineFilterOverrideOnSameRelativePath.ini), using two + // identical "respath" path entries with distinct overriding include/exclude filters, loads successfully and the expected paths and filters are present. + // See notes in the validInlineFilterOverrideOnSameRelativePath.ini file for further details. + + // Alter the current working directory for the duration of this test + CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); + try + { + const std::filesystem::path iniPath = "ExampleIniFiles/validInlineFilterOverrideOnSameRelativePath.ini"; + std::vector paths = { iniPath }; + ResourceTools::ResourceFilter resourceFilter; + resourceFilter.Initialize( paths ); + + // Validate correct included and exclude paths via the resourceFilter: + // - Because of the overriding rules for the identical "respath" path entries. + // We should end up with includes of BOTH .txt and .yaml files with no excludes. + std::set validIncludeResolvedRelativePaths = { + "resourcesOnBranch/introMovie.txt", + "resourcesOnBranch/videoCardCategories.yaml" + }; + std::set validExcludeResolvedRelativePaths = { + }; + + ASSERT_EQ( resourceFilter.HasFilters(), true ); + for( const auto& resolvedRelativeIncludePath : validIncludeResolvedRelativePaths ) + { + ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativeIncludePath ), true ) << "Should have included relative path: " << resolvedRelativeIncludePath.generic_string(); + } + for( const auto& resolvedRelativeExcludePath : validExcludeResolvedRelativePaths ) + { + ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativeExcludePath ), false ) << "Should have excluded relative path: " << resolvedRelativeExcludePath.generic_string(); + } + + // Additional checks to make sure the FullResolvedPathMap contains correct include/exclude filter data + const auto& fullPathMap = resourceFilter.GetFullResolvedPathMap(); + ASSERT_EQ( fullPathMap.size(), 1 ); + + std::set expectedPaths = { + "resourcesOnBranch/*" + }; + MapContainsPaths( expectedPaths, fullPathMap, "FullResolvedPathMap from validInlineFilterOverrideOnSameRelativePath.ini" ); + + for( const auto& kv : fullPathMap ) + { + if( kv.first == "resourcesOnBranch/*" ) + { + EXPECT_EQ( kv.second.GetIncludeFilter().size(), 2 ); + EXPECT_EQ( kv.second.GetExcludeFilter().size(), 0 ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".txt" ) != kv.second.GetIncludeFilter().end() ); + EXPECT_TRUE( std::find( kv.second.GetIncludeFilter().begin(), kv.second.GetIncludeFilter().end(), ".yaml" ) != kv.second.GetIncludeFilter().end() ); + } + else + { + FAIL() << "Unexpected path found in FullResolvedPathMap: " << kv.first; + } + } + } + catch( ... ) + { + FAIL() << "Test failed when it should have passed."; + } +} diff --git a/tests/testData/ExampleIniFiles/validInlineFilterOverrideOnSameRelativePath.ini b/tests/testData/ExampleIniFiles/validInlineFilterOverrideOnSameRelativePath.ini new file mode 100644 index 0000000..1de0a7d --- /dev/null +++ b/tests/testData/ExampleIniFiles/validInlineFilterOverrideOnSameRelativePath.ini @@ -0,0 +1,24 @@ +[DEFAULT] +prefixmap = resOnBranch:resourcesOnBranch + +#============================================================================= +# validInlineFilterOverrideOnSameRelativePath.ini - test file +# This test should: +# A. Have one prefixmap entry: +# * resOnBranch +# B. The respaths attribute should resolve as follows: +# - resOnBranch:/* ==> resourcesOnBranch/* with filter [ .yaml ] ![ .txt ] (parent) + ![ .yaml ] (inline #1) ==> 0 include & 2 exclude +# - resOnBranch:/* ==> resourcesOnBranch/* with filter [ .yaml ] ![ .txt ] + ![ .yaml ] (already combined filter, see line above) +# + [ .yaml ] ![ .txt ] (parent again) + [ .txt ] (inline #2) ==> 2 include & 0 exclude +# Because the first instance of the relative path has zero includes and excludes both extensions, +# and the second instance includes the .txt ON TOP of the first instance ALONG WITH the parent filter again. +# Then we should end up with a filter that will: +# - include both .txt and .yaml files +# - exclude nothing +#============================================================================= + +[testInlineFilterOverrideOnSameRelativePath] +filter = [ .yaml ] ![ .txt ] +respaths = resOnBranch:/* ![ .yaml ] + resOnBranch:/* [ .txt ] + diff --git a/tests/testData/ExampleIniFiles/validOverrideDifferentRelativeSameAbsolutePaths.ini b/tests/testData/ExampleIniFiles/validOverrideDifferentRelativeSameAbsolutePaths.ini new file mode 100644 index 0000000..ec6108a --- /dev/null +++ b/tests/testData/ExampleIniFiles/validOverrideDifferentRelativeSameAbsolutePaths.ini @@ -0,0 +1,28 @@ +[DEFAULT] +prefixmap = resRoot:. resOnBranch:resourcesOnBranch + +#============================================================================= +# validOverrideDifferentRelativeSameAbsolutePaths.ini - test file +# This test should: +# A. Have two entries in the prefixmap: +# * resRoot +# * resOnBranch +# B. The respaths attribute should resolve to the same actual location +# for both resRoot and resOnBranch (using the same type of wildcard, "*" in this case). + +# Resolution of respaths: +# - resRoot:/resourcesOnBranch/* ==> ./resourcesOnBranch/* with filter [ .yaml ] ![ .txt ] + ![ .yaml ] ==> 0 include & 2 exclude +# - resOnBranch:/* ==> resourcesOnBranch/* with filter [ .yaml ] ![ .txt ] + [ .txt ] ==> 2 include & 0 exclude +# Those are two different relative paths, that will resolve to the same actual path. +# Because one relative path includes both extensions and the other excludes both, +# all files will be EXCLUDED. +# This is because files at the same hierarchy priority (in this case, priority=0 +# because of wildcard search in the same directory), priority rules dictate that +# the files should be excluded. +#============================================================================= + +[testOverrideOfTwoRespathsUsingDifferentPrefixesThatResolveToDifferentRelativeButSameActualPaths] +filter = [ .yaml ] ![ .txt ] +respaths = resRoot:/resourcesOnBranch/* ![ .yaml ] + resOnBranch:/* [ .txt ] + diff --git a/tools/include/FilterResourcePathFile.h b/tools/include/FilterResourcePathFile.h index bbd0c44..acde6e9 100644 --- a/tools/include/FilterResourcePathFile.h +++ b/tools/include/FilterResourcePathFile.h @@ -12,20 +12,27 @@ namespace ResourceTools { -// Class representing a resfile/respaths attribute. +// ------------------------------------------------------------- +// Description: +// FilterResourcePathFile is a class that represents a resfile/respaths +// attribute from a filter .ini file. +// Each "respaths" attribute may contain multiple line entries, +// stored in the FilterResourcePathFileEntry class. +// ------------------------------------------------------------- class FilterResourcePathFile { public: - explicit FilterResourcePathFile( std::string rawPathFileAttrib, + // Construct a FilterResourcePathFile object for a resfile/respaths attribute. + explicit FilterResourcePathFile( const std::string& rawPathFileAttrib, const FilterPrefixMap& parentPrefixMap, const FilterResourceFilter& parentSectionFilter ); - // Get the map of fully resolved paths to their combined FilterResourceFilter objects. + // Gets a map of fully resolved relative paths and the associated FilterResourceFilter include/exclude filters. const std::map& GetResolvedPathMap() const; private: - // The raw (multiline) respath attribute (same for resfile). - std::string m_rawPathFileAttrib; + // Parse the rawPathFileAttrib and populate the m_resolvedPathMap. + void ParseRawPathFileAttribute( const std::string& rawPathFileAttrib ); // The "parent" prefix map from the [DEFAULT] section const FilterPrefixMap& m_parentPrefixMap; @@ -35,8 +42,6 @@ class FilterResourcePathFile // Map of fully resolved paths to their combined FilterResourceFilter objects. std::map m_resolvedPathMap; - - void ParseRawPathFileAttribute(); }; } diff --git a/tools/src/FilterResourcePathFile.cpp b/tools/src/FilterResourcePathFile.cpp index c78da88..451de65 100644 --- a/tools/src/FilterResourcePathFile.cpp +++ b/tools/src/FilterResourcePathFile.cpp @@ -9,28 +9,56 @@ namespace ResourceTools { -FilterResourcePathFile::FilterResourcePathFile( std::string rawPathFileAttrib, +// ------------------------------------------------------------- +// Description: +// Constructs a FilterResourcePathFile object for a resfile/respaths attribute. +// Arguments: +// rawPathFileAttrib - the raw string for this resfile/respaths attribute from the filter .ini file. +// This can contain multiple lines of path entries, see FilterResourcePathFileEntry class. +// parentPrefixMap - the FilterPrefixMap from the [DEFAULT] section, used to +// resolve prefixes in the resfile/respaths attribute to actual paths. +// parentSectionFilter - the FilterResourceFilter from the same [namedSection] as this +// resfile/respaths attribute. Needed to create actual filters (some with optional inline) +// for each line entry (FilterResourcePathFileEntry) of the resfile/respaths attribute. +// Note: +// The parsing of the rawPathFileAttrib takes place in the ParseRawPathFileAttribute() function. +// ------------------------------------------------------------- +FilterResourcePathFile::FilterResourcePathFile( const std::string& rawPathFileAttrib, const FilterPrefixMap& parentPrefixMap, const FilterResourceFilter& parentSectionFilter ) : - m_rawPathFileAttrib( std::move( rawPathFileAttrib ) ), m_parentPrefixMap( parentPrefixMap ), m_parentSectionFilter( parentSectionFilter ) { - m_resolvedPathMap.clear(); - - ParseRawPathFileAttribute(); + ParseRawPathFileAttribute( rawPathFileAttrib ); } +// ------------------------------------------------------------- +// Description: +// Gets a map of fully resolved relative paths and their +// associated FilterResourceFilter include/exclude filters. +// Return Value: +// Map with a key = resolved relative path and value of +// the associated include/exclude filters. +// ------------------------------------------------------------- const std::map& FilterResourcePathFile::GetResolvedPathMap() const { return m_resolvedPathMap; } -void FilterResourcePathFile::ParseRawPathFileAttribute() +// ------------------------------------------------------------- +// Description: +// Parses the rawPathFileAttrib and populates the m_resolvedPathMap. +// Arguments: +// rawPathFileAttrib - the raw string for this resfile/respaths attribute +// from the filter .ini file. This can contain multiple lines of path +// entries that is represented by the FilterResourcePathFileEntry class. +// ------------------------------------------------------------- +void FilterResourcePathFile::ParseRawPathFileAttribute( const std::string& rawPathFileAttrib ) { - // Split m_rawPathFileAttrib into lines (in case of multiline attribute) - std::istringstream stream( m_rawPathFileAttrib ); + // Split rawPathFileAttrib into lines (in case of multiline attribute) + std::istringstream stream( rawPathFileAttrib ); std::string line; + while( std::getline( stream, line ) ) { // Trim whitespace from both ends @@ -51,11 +79,40 @@ void FilterResourcePathFile::ParseRawPathFileAttribute() // Add entries to the resolved path map auto lineEntry = FilterResourcePathFileEntry( rawPathLine, m_parentPrefixMap, m_parentSectionFilter ); - const auto resolvedPaths = lineEntry.GetResolvedPaths(); const auto entryFilter = lineEntry.GetEntryFilter(); + const auto resolvedPaths = lineEntry.GetResolvedPaths(); + for( const auto& path : resolvedPaths ) { - m_resolvedPathMap.insert_or_assign( path, entryFilter ); + // Check if the path already exists in the map, to determine if se should combine filters or not. + auto foundMapItem = m_resolvedPathMap.find( path ); + if( foundMapItem != m_resolvedPathMap.end() ) + { + // Combine the raw filters from the previous and current entry + const std::string& prevRawFilter = foundMapItem->second.GetRawFilter(); + const std::string& currRawFilter = entryFilter.GetRawFilter(); + std::string combinedRawFilter; + + if( !prevRawFilter.empty() && !currRawFilter.empty() ) + { + combinedRawFilter = prevRawFilter + " " + currRawFilter; + } + else if( !prevRawFilter.empty() ) + { + combinedRawFilter = prevRawFilter; + } + else + { + combinedRawFilter = currRawFilter; + } + // Update the map with the combined filter + m_resolvedPathMap.insert_or_assign( path, FilterResourceFilter( combinedRawFilter ) ); + } + else + { + // Path not present, just insert as is + m_resolvedPathMap.insert( { path, entryFilter } ); + } } } } From 33cd2095357363418148f6ed68d8048ebe0155a4 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Sun, 8 Feb 2026 16:15:18 +0000 Subject: [PATCH 098/124] Cleanup, simplify and comment the FilterResourceFile class --- tools/include/FilterResourceFile.h | 18 ++++++++++---- tools/src/FilterResourceFile.cpp | 39 ++++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/tools/include/FilterResourceFile.h b/tools/include/FilterResourceFile.h index 21bc4bd..9552378 100644 --- a/tools/include/FilterResourceFile.h +++ b/tools/include/FilterResourceFile.h @@ -13,26 +13,34 @@ namespace ResourceTools { +// ------------------------------------------------------------- +// Description: +// FilterResourceFile is a class that represents the fully +// parsed resource .ini file. +// ------------------------------------------------------------- class FilterResourceFile { public: + // Construct a FilterResourceFile object by parsing the supplied resource .ini file. explicit FilterResourceFile( const std::filesystem::path& iniFilePath ); - // Returns the full resolved PathMaps for all named sections defined in the resource .ini file - // Key is the "resolved path", Value is the associated FilterResourceFilter (include and exclude filters) + // Generate, cache and returns the fully resolved PathMaps for all named sections + // within this resource .ini file. + // Key = "resolved path", Value = associated include/exclude filters const std::map& GetIniFileResolvedPathMap(); private: - std::filesystem::path m_iniFilePath; + // Parses the resource .ini file and populates the m_defaultSection and m_namedSections members. + void ParseIniFile( const std::filesystem::path& iniFilePath ); + // The parsed [DEFAULT] section of the resource .ini file FilterDefaultSection m_defaultSection; + // Vector of all th parsed [NamedSection(s)] defined in the .ini file std::vector m_namedSections; // Resolved PathMap for all named sections defined in a resource .ini file std::map m_iniFileResolvedPathMap; - - void ParseIniFile(); }; } diff --git a/tools/src/FilterResourceFile.cpp b/tools/src/FilterResourceFile.cpp index 6d6cb13..6d10410 100644 --- a/tools/src/FilterResourceFile.cpp +++ b/tools/src/FilterResourceFile.cpp @@ -9,14 +9,26 @@ namespace ResourceTools { -FilterResourceFile::FilterResourceFile( const std::filesystem::path& iniFilePath ) : - m_iniFilePath( iniFilePath ) +// ------------------------------------------------------------- +// Description: +// Construct a FilterResourceFile object by parsing the supplied resource .ini file. +// Arguments: +// iniFilePath - the file path to the resource .ini file to parse. +// ------------------------------------------------------------- +FilterResourceFile::FilterResourceFile( const std::filesystem::path& iniFilePath ) { - m_iniFileResolvedPathMap.clear(); - - ParseIniFile(); + ParseIniFile( iniFilePath ); } +// ------------------------------------------------------------- +// Description: +// Generate (on first call to function), cache and returns the fully +// resolved PathMaps for all named sections within .ini file. +// Return Value: +// Map of resolved paths to their associated filters for all named sections in the .ini file. +// - Key = "resolved path" +// - Value = associated include/exclude filters +// ------------------------------------------------------------- const std::map& FilterResourceFile::GetIniFileResolvedPathMap() { if( m_iniFileResolvedPathMap.empty() ) @@ -47,19 +59,26 @@ const std::map& FilterResourceFile::GetIniFil return m_iniFileResolvedPathMap; } -void FilterResourceFile::ParseIniFile() +// ------------------------------------------------------------- +// Description: +// Parses the resource .ini file and populates the m_defaultSection and +// m_namedSections members from the [DEFAULT] and [NamedSection(s)] respectively. +// Arguments: +// iniFilePath - the file path to the resource .ini file to parse. +// ------------------------------------------------------------- +void FilterResourceFile::ParseIniFile( const std::filesystem::path& iniFilePath ) { // Open, read and parse the resource INI file. - INIReader reader( m_iniFilePath.generic_string() ); + INIReader reader( iniFilePath.generic_string() ); if( reader.ParseError() != 0 ) { - throw std::runtime_error( "Failed to parse INI file: " + m_iniFilePath.generic_string() + " - " + reader.ParseErrorMessage() ); + throw std::runtime_error( "Failed to parse INI file: " + iniFilePath.generic_string() + " - " + reader.ParseErrorMessage() ); } // Parse the [DEFAULT] section if( !reader.HasSection( "DEFAULT" ) ) { - throw std::invalid_argument( "Missing [DEFAULT] section in INI file: " + m_iniFilePath.generic_string() ); + throw std::invalid_argument( "Missing [DEFAULT] section in INI file: " + iniFilePath.generic_string() ); } m_defaultSection = FilterDefaultSection( reader.Get( "DEFAULT", "prefixmap", "" ) ); @@ -69,7 +88,7 @@ void FilterResourceFile::ParseIniFile() if( allSections.size() <= 1 ) { // No namedSections defined - throw std::invalid_argument( "No namedSections defined in INI file: " + m_iniFilePath.generic_string() ); + throw std::invalid_argument( "No namedSections defined in INI file: " + iniFilePath.generic_string() ); } // Parse all other named sections From d4f0a3e2f5baf8b994417821f7b9e67ca91ab97f Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Sun, 8 Feb 2026 19:16:27 +0000 Subject: [PATCH 099/124] Rename ShouldInclude() to FilePathMatchesIncludeFilterRules() --- src/ResourceGroupImpl.cpp | 2 +- tests/src/ResourceToolsFilterTest.cpp | 20 ++++++++++---------- tools/include/ResourceFilter.h | 19 ++++++++----------- tools/src/ResourceFilter.cpp | 9 ++++++--- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/ResourceGroupImpl.cpp b/src/ResourceGroupImpl.cpp index 80930e1..e064777 100644 --- a/src/ResourceGroupImpl.cpp +++ b/src/ResourceGroupImpl.cpp @@ -98,7 +98,7 @@ Result ResourceGroup::ResourceGroupImpl::CreateFromDirectory( const CreateResour { // Resource filtering: // Check if the file, i.e. entry.path() should be included or excluded based on filtering rules - if( !resourceFilter.ShouldInclude( entry.path() ) ) + if( !resourceFilter.FilePathMatchesIncludeFilterRules( entry.path() ) ) { continue; } diff --git a/tests/src/ResourceToolsFilterTest.cpp b/tests/src/ResourceToolsFilterTest.cpp index d50675a..6813c06 100644 --- a/tests/src/ResourceToolsFilterTest.cpp +++ b/tests/src/ResourceToolsFilterTest.cpp @@ -2033,7 +2033,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_Validate_RaiiClassCurrentWorkingDirect // Check that the "binaryFileIndex_v0_0_0.txt" file (and it's path) is included correctly std::filesystem::path oneValidRelativePath = "./Indicies/binaryFileIndex_v0_0_0.txt"; - ASSERT_EQ( resourceFilter.ShouldInclude( oneValidRelativePath ), true ); + ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( oneValidRelativePath ), true ); } catch( const std::exception& e ) { @@ -2077,7 +2077,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingRelativ ASSERT_EQ( resourceFilter.HasFilters(), true ); for( const auto& resolvedRelativePath : validResolvedRelativePaths ) { - ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativePath ), true ) << "Should have included relative path: " << resolvedRelativePath.generic_string(); + ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedRelativePath ), true ) << "Should have included relative path: " << resolvedRelativePath.generic_string(); } // Additional check to make sure the FullResolvedPathMap contains correct data (either include or exclude): @@ -2123,7 +2123,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingAbsolut ASSERT_EQ( resourceFilter.HasFilters(), true ); for( const auto& resolvedAbsPath : validResolvedAbsolutePaths ) { - ASSERT_EQ( resourceFilter.ShouldInclude( resolvedAbsPath ), true ) << "Should have included absolute path: " << resolvedAbsPath.generic_string(); + ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedAbsPath ), true ) << "Should have included absolute path: " << resolvedAbsPath.generic_string(); } // Additional check to make sure the FullResolvedPathMap contains correct data (either include or exclude): @@ -2179,7 +2179,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingRelativ ASSERT_EQ( resourceFilter.HasFilters(), true ); for( const auto& resolvedRelativePath : validResolvedRelativePaths ) { - ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativePath ), true ) << "Should have included relative path: " << resolvedRelativePath.generic_string(); + ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedRelativePath ), true ) << "Should have included relative path: " << resolvedRelativePath.generic_string(); } // Additional check to make sure the FullResolvedPathMap contains correct data (either include or exclude): @@ -2290,7 +2290,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingAbsolut ASSERT_EQ( resourceFilter.HasFilters(), true ); for( const auto& resolvedAbsPath : validResolvedRelativePaths ) { - ASSERT_EQ( resourceFilter.ShouldInclude( resolvedAbsPath ), true ) << "Should have included relative path: " << resolvedAbsPath.generic_string(); + ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedAbsPath ), true ) << "Should have included relative path: " << resolvedAbsPath.generic_string(); } // Additional check to make sure the FullResolvedPathMap contains correct data (either include or exclude): @@ -2409,7 +2409,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulLoadOf2IniFiles_vali ASSERT_EQ( resourceFilter.HasFilters(), true ); for( const auto& resolvedRelativePath : validResolvedRelativePaths ) { - ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativePath ), true ) << "Should have included relative path: " << resolvedRelativePath.generic_string(); + ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedRelativePath ), true ) << "Should have included relative path: " << resolvedRelativePath.generic_string(); } // Additional check to make sure the FullResolvedPathMap contains correct data (either include or exclude): @@ -2523,11 +2523,11 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccess_FileLoadOverrideUsingD ASSERT_EQ( resourceFilter.HasFilters(), true ); for( const auto& resolvedRelativeIncludePath : validIncludeResolvedRelativePaths ) { - ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativeIncludePath ), true ) << "Should have included relative path: " << resolvedRelativeIncludePath.generic_string(); + ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedRelativeIncludePath ), true ) << "Should have included relative path: " << resolvedRelativeIncludePath.generic_string(); } for( const auto& resolvedRelativeExcludePath : validExcludeResolvedRelativePaths ) { - ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativeExcludePath ), false ) << "Should have excluded relative path: " << resolvedRelativeExcludePath.generic_string(); + ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedRelativeExcludePath ), false ) << "Should have excluded relative path: " << resolvedRelativeExcludePath.generic_string(); } // Additional checks to make sure the FullResolvedPathMap contains correct include/exclude filter data @@ -2596,11 +2596,11 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccess_FileLoadRespathInlineO ASSERT_EQ( resourceFilter.HasFilters(), true ); for( const auto& resolvedRelativeIncludePath : validIncludeResolvedRelativePaths ) { - ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativeIncludePath ), true ) << "Should have included relative path: " << resolvedRelativeIncludePath.generic_string(); + ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedRelativeIncludePath ), true ) << "Should have included relative path: " << resolvedRelativeIncludePath.generic_string(); } for( const auto& resolvedRelativeExcludePath : validExcludeResolvedRelativePaths ) { - ASSERT_EQ( resourceFilter.ShouldInclude( resolvedRelativeExcludePath ), false ) << "Should have excluded relative path: " << resolvedRelativeExcludePath.generic_string(); + ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedRelativeExcludePath ), false ) << "Should have excluded relative path: " << resolvedRelativeExcludePath.generic_string(); } // Additional checks to make sure the FullResolvedPathMap contains correct include/exclude filter data diff --git a/tools/include/ResourceFilter.h b/tools/include/ResourceFilter.h index 6f2ae0f..3eca401 100644 --- a/tools/include/ResourceFilter.h +++ b/tools/include/ResourceFilter.h @@ -22,31 +22,28 @@ class ResourceFilter void Initialize( const std::vector& iniFilePaths ); - bool HasFilters() const - { - return !m_filterFiles.empty(); - } + bool HasFilters() const; // Returns the full relative resolved PathMaps from all resource .ini file // Key is the "relative resolved path", Value is the associated FilterResourceFilter (include and exclude filters) const std::map& GetFullResolvedPathMap(); // Check if the inFilePath should be included or excluded based on filtering rules - bool ShouldInclude( const std::filesystem::path& inFilePath ); + bool FilePathMatchesIncludeFilterRules( const std::filesystem::path& inFilePath ); private: + // Helper function for wildcard matching paths (supports "*" and "...") + static bool WildcardMatch( const std::string& pattern, const std::string& checkStr ); + + // Helper function to normalize paths (i.e. deal with \ / .. . etc) + static std::string NormalizePath( const std::string& path ); + bool m_initialized{ false }; std::vector> m_filterFiles; // Resolved PathMap for all .ini files std::map m_fullResolvedPathMap; - - // Helper function for wildcard matching paths (supports "*" and "...") - static bool WildcardMatch( const std::string& pattern, const std::string& checkStr ); - - // Helper function to normalize paths (i.e. deal with \ / .. . etc) - static std::string NormalizePath( const std::string& path ); }; } diff --git a/tools/src/ResourceFilter.cpp b/tools/src/ResourceFilter.cpp index 7a7f66e..1ff3443 100644 --- a/tools/src/ResourceFilter.cpp +++ b/tools/src/ResourceFilter.cpp @@ -14,8 +14,6 @@ ResourceFilter::ResourceFilter( const std::vector& iniFil void ResourceFilter::Initialize( const std::vector& iniFilePaths ) { - m_fullResolvedPathMap.clear(); - if( m_initialized ) { throw std::runtime_error( "ResourceFilter is already initialized." ); @@ -39,6 +37,11 @@ void ResourceFilter::Initialize( const std::vector& iniFi m_initialized = true; } +bool ResourceFilter::HasFilters() const +{ + return !m_filterFiles.empty(); +} + const std::map& ResourceFilter::GetFullResolvedPathMap() { if( m_fullResolvedPathMap.empty() ) @@ -69,7 +72,7 @@ const std::map& ResourceFilter::GetFullResolv return m_fullResolvedPathMap; } -bool ResourceFilter::ShouldInclude( const std::filesystem::path& inFilePath ) +bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::path& inFilePath ) { // Make sure we work with the absolute path representation of the input file std::filesystem::path inFilePathAbs = std::filesystem::absolute( inFilePath ); From 4ac6ebec4bc1e35c194478f16f175496f058e1df Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Sun, 8 Feb 2026 19:51:35 +0000 Subject: [PATCH 100/124] Rename constants in WildcardMatch() as per coding guidelines. --- tools/include/ResourceFilter.h | 2 +- tools/src/ResourceFilter.cpp | 45 +++++++++++++++++----------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/tools/include/ResourceFilter.h b/tools/include/ResourceFilter.h index 3eca401..fdd707e 100644 --- a/tools/include/ResourceFilter.h +++ b/tools/include/ResourceFilter.h @@ -33,7 +33,7 @@ class ResourceFilter private: // Helper function for wildcard matching paths (supports "*" and "...") - static bool WildcardMatch( const std::string& pattern, const std::string& checkStr ); + static bool WildcardMatch( std::string pattern, const std::string& checkStr ); // Helper function to normalize paths (i.e. deal with \ / .. . etc) static std::string NormalizePath( const std::string& path ); diff --git a/tools/src/ResourceFilter.cpp b/tools/src/ResourceFilter.cpp index 1ff3443..5510461 100644 --- a/tools/src/ResourceFilter.cpp +++ b/tools/src/ResourceFilter.cpp @@ -97,8 +97,10 @@ bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::p if( resolvedNormalAbsPathStr == inFileNormalAbsPathStr ) { - // If there is an exact match on the full filename path, this means highest priority and - // SHOULD BE considered an "INCLUDE" even though resolvedPath has filters that might say otherwise. + // If there is an exact match on the full filename path, this means highest priority + // and the file path should be considered an "include". This is even though resolvedPath + // may have filters that indicate exclude (explicitly specifying a full filename path + // should override any wildcard exclusion filters on the same file path). bestIncludePriority = -1; continue; } @@ -192,55 +194,54 @@ bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::p // pattern = The resolved path from the .ini file (can contain wildcards) // checkStr = The input file path to check against the pattern -bool ResourceFilter::WildcardMatch( const std::string& pattern, const std::string& checkStr ) +bool ResourceFilter::WildcardMatch( std::string pattern, const std::string& checkStr ) { - // Replace ... with a unique token - std::string pat = pattern; - std::string token = "\x01"; + // Replace any "..." with a unique token (RECURSIVE_FOLDER_ELLIPSES_WILDCARD) + constexpr char RECURSIVE_FOLDER_ELLIPSES_WILDCARD = '\x01'; size_t pos; - while( ( pos = pat.find( "..." ) ) != std::string::npos ) + while( ( pos = pattern.find( "..." ) ) != std::string::npos ) { - pat.replace( pos, 3, token ); + pattern.replace( pos, 3, std::string(1, RECURSIVE_FOLDER_ELLIPSES_WILDCARD) ); } - // Escape special characters and deal with wildcards - std::string regexPat; - for( size_t i = 0; i < pat.size(); ++i ) + // Escape special characters and deal with wildcards ("*" and "..." i.e. RECURSIVE_FOLDER_ELLIPSES_WILDCARD) + std::string regexPattern; + for( size_t i = 0; i < pattern.size(); ++i ) { - if( pat[i] == '*' ) + if( pattern[i] == '*' ) { - regexPat += "[^/]*"; + regexPattern += "[^/]*"; } - else if( pat[i] == '\x01' ) + else if( pattern[i] == RECURSIVE_FOLDER_ELLIPSES_WILDCARD ) { - regexPat += ".*"; + regexPattern += ".*"; } - else if( std::string( ".^$|()[]{}+?\\" ).find( pat[i] ) != std::string::npos ) + else if( std::string( ".^$|()[]{}+?\\" ).find( pattern[i] ) != std::string::npos ) { // Regex special characters that need escaping - regexPat += '\\'; - regexPat += pat[i]; + regexPattern += '\\'; + regexPattern += pattern[i]; } else { - regexPat += pat[i]; + regexPattern += pattern[i]; } } try { - std::regex re( regexPat, std::regex::ECMAScript | std::regex::icase ); + std::regex re( regexPattern, std::regex::ECMAScript | std::regex::icase ); bool regexResult = std::regex_match( checkStr, re ); return regexResult; } catch( const std::regex_error& e ) { - std::string errorMsg = "Regex Exception during WildcardMatching - regexPattern: " + regexPat + " checkString: " + checkStr + " - error details: " + e.what(); + std::string errorMsg = "Regex Exception during WildcardMatching - regexPattern: " + regexPattern + " checkString: " + checkStr + " - error details: " + e.what(); throw std::runtime_error( errorMsg ); } catch( const std::exception& e ) { - std::string errorMsg = "Standard Exception during WildcardMatching - regexPattern: " + regexPat + " checkString: " + checkStr + " - error details: " + e.what(); + std::string errorMsg = "Standard Exception during WildcardMatching - regexPattern: " + regexPattern + " checkString: " + checkStr + " - error details: " + e.what(); throw std::runtime_error( errorMsg ); } } From 9836626bfac0ff349849f23aca4aeffa0c584ba0 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Sun, 8 Feb 2026 20:22:53 +0000 Subject: [PATCH 101/124] Cleanup, simplify and comment the ResourceFilter class --- tools/include/ResourceFilter.h | 25 +++++++++--- tools/src/FilterResourceFile.cpp | 2 + tools/src/ResourceFilter.cpp | 70 ++++++++++++++++++++++++++++++-- 3 files changed, 87 insertions(+), 10 deletions(-) diff --git a/tools/include/ResourceFilter.h b/tools/include/ResourceFilter.h index fdd707e..8e3c0af 100644 --- a/tools/include/ResourceFilter.h +++ b/tools/include/ResourceFilter.h @@ -13,36 +13,49 @@ namespace ResourceTools { +// ------------------------------------------------------------- +// Description: +// ResourceFilter is a class that wraps one or more filter .ini +// files and exposes a way (function FilePathMatchesIncludeFilterRules) +// to check if a given file path should be included or excluded based +// on all the filtering rules defined in those .ini file(s). +// ------------------------------------------------------------- class ResourceFilter { public: ResourceFilter() = default; + // Construct a ResourceFilter object by passing filter .ini file(s). explicit ResourceFilter( const std::vector& iniFilePaths ); + // Initializes the ResourceFilter by passing in and parsing the supplied filter .ini file(s). void Initialize( const std::vector& iniFilePaths ); + // Returns true if this ResourceFilter has any filter .ini files, false otherwise. bool HasFilters() const; - // Returns the full relative resolved PathMaps from all resource .ini file - // Key is the "relative resolved path", Value is the associated FilterResourceFilter (include and exclude filters) + // Returns the full resolved relative PathMaps from all resource .ini file(s). + // Key = "resolved relative path", Value = FilterResourceFilter (include/exclude filters) const std::map& GetFullResolvedPathMap(); - // Check if the inFilePath should be included or excluded based on filtering rules + // Check if the inFilePath should be included or excluded based on + // filtering rules from all the filter .ini file(s) bool FilePathMatchesIncludeFilterRules( const std::filesystem::path& inFilePath ); private: - // Helper function for wildcard matching paths (supports "*" and "...") + // Static helper function for wildcard matching path strings (supports "*" and "...") static bool WildcardMatch( std::string pattern, const std::string& checkStr ); - // Helper function to normalize paths (i.e. deal with \ / .. . etc) + // Static helper function to normalize paths (i.e. deal with \ / .. . etc) static std::string NormalizePath( const std::string& path ); + // A flag used to prevent multiple initializations of the ResourceFilter bool m_initialized{ false }; + // Vector of all the filter .ini files (wrapped in FilterResourceFile objects) std::vector> m_filterFiles; - // Resolved PathMap for all .ini files + // Resolved PathMap from all the filter .ini files std::map m_fullResolvedPathMap; }; diff --git a/tools/src/FilterResourceFile.cpp b/tools/src/FilterResourceFile.cpp index 6d10410..fdf7eee 100644 --- a/tools/src/FilterResourceFile.cpp +++ b/tools/src/FilterResourceFile.cpp @@ -65,6 +65,8 @@ const std::map& FilterResourceFile::GetIniFil // m_namedSections members from the [DEFAULT] and [NamedSection(s)] respectively. // Arguments: // iniFilePath - the file path to the resource .ini file to parse. +// Return Value: +// None (void). // ------------------------------------------------------------- void FilterResourceFile::ParseIniFile( const std::filesystem::path& iniFilePath ) { diff --git a/tools/src/ResourceFilter.cpp b/tools/src/ResourceFilter.cpp index 5510461..362d4a8 100644 --- a/tools/src/ResourceFilter.cpp +++ b/tools/src/ResourceFilter.cpp @@ -7,11 +7,24 @@ namespace ResourceTools { +// ------------------------------------------------------------- +// Description: +// Construct a ResourceFilter object by passing in filter .ini file(s). +// ------------------------------------------------------------- ResourceFilter::ResourceFilter( const std::vector& iniFilePaths ) { Initialize( iniFilePaths ); } +// ------------------------------------------------------------- +// Description: +// Initializes the ResourceFilter by passing in and creating +// FilterResourceFile objects for each of the supplied filter .ini file(s). +// Arguments: +// iniFilePaths - vector of file paths to the filter .ini files +// Return Value: +// None (void). +// ------------------------------------------------------------- void ResourceFilter::Initialize( const std::vector& iniFilePaths ) { if( m_initialized ) @@ -37,11 +50,31 @@ void ResourceFilter::Initialize( const std::vector& iniFi m_initialized = true; } +// ------------------------------------------------------------- +// Description: +// Determine if this ResourceFilter has any filters +// Return Value: +// True = There are filter .ini files present +// False = There are no filter .ini files +// ------------------------------------------------------------- bool ResourceFilter::HasFilters() const { return !m_filterFiles.empty(); } +// ------------------------------------------------------------- +// Description: +// Returns the full resolved relative PathMaps from all FilterResourceFile(s) +// in this ResourceFilter. +// Return Value: +// Map of resolved paths to their associated filters from all +// FilterResourceFile(s) +// Key = "resolved relative path" +// Value = FilterResourceFilter (with include/exclude filters) +// Note: +// The full resolved path map is generated and then cached on +// the first call to this function. +// ------------------------------------------------------------- const std::map& ResourceFilter::GetFullResolvedPathMap() { if( m_fullResolvedPathMap.empty() ) @@ -72,6 +105,17 @@ const std::map& ResourceFilter::GetFullResolv return m_fullResolvedPathMap; } +// ------------------------------------------------------------- +// Description: +// Check if the inFilePath should be included or excluded based on +// the filtering rules from all .ini file(s) in this ResourceFilter. +// Arguments: +// inFilePath - the file path to check against the filter rules, +// can be relative or absolute path. +// Return Value: +// True = the file path matches the include filter rules and should be included +// False = the file path does not match the include filter rules and should be excluded +// ------------------------------------------------------------- bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::path& inFilePath ) { // Make sure we work with the absolute path representation of the input file @@ -87,7 +131,6 @@ bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::p // Get the full resolved path map and iterate through it (contains relative paths) const auto& resolvedPathMap = GetFullResolvedPathMap(); - for( const auto& [resolvedRelativePathStr, filter] : resolvedPathMap ) { // Use normalized absolute paths for comparison @@ -191,9 +234,19 @@ bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::p return false; } - -// pattern = The resolved path from the .ini file (can contain wildcards) -// checkStr = The input file path to check against the pattern +// ------------------------------------------------------------- +// Description: +// Static helper function for wildcard matching path strings. +// Supports the following wildcards: +// - "*" = matches any sequence of characters (at the same folder level) +// - "..." = matches any sequence of characters (as any recursive folder level) +// Arguments: +// pattern - The resolved path from the .ini file (can contain wildcards) +// checkStr - The input file path to check against the pattern +// Return Value: +// True = the checkStr matches the pattern (exact or with wildcards) +// False = the checkStr does not match the pattern (neither exact nor with wildcards) +// ------------------------------------------------------------- bool ResourceFilter::WildcardMatch( std::string pattern, const std::string& checkStr ) { // Replace any "..." with a unique token (RECURSIVE_FOLDER_ELLIPSES_WILDCARD) @@ -246,6 +299,15 @@ bool ResourceFilter::WildcardMatch( std::string pattern, const std::string& chec } } +// ------------------------------------------------------------- +// Description: +// Static helper function to normalize paths by removing redundant +// path components such as "." and ".." and converting to a generic format. +// Arguments: +// path - the file path to normalize +// Return Value: +// Normalized path string in generic format (using '/' as separator) +// ------------------------------------------------------------- std::string ResourceFilter::NormalizePath( const std::string& path ) { std::filesystem::path p( path ); From b81076b7c89ca2722831b57f25f5853ff22b30f3 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 10 Feb 2026 13:48:48 +0000 Subject: [PATCH 102/124] Use .empty() instead of .size() where applicable --- cli/src/CreateResourceGroupCliOperation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/CreateResourceGroupCliOperation.cpp b/cli/src/CreateResourceGroupCliOperation.cpp index 6bc907f..7bdd317 100644 --- a/cli/src/CreateResourceGroupCliOperation.cpp +++ b/cli/src/CreateResourceGroupCliOperation.cpp @@ -152,7 +152,7 @@ void CreateResourceGroupCliOperation::PrintStartBanner( std::cout << "Export Resources: Off" << std::endl; } - if( createResourceGroupFromDirectoryParams.resourceFilterIniFiles.size() > 0 ) + if( !createResourceGroupFromDirectoryParams.resourceFilterIniFiles.empty() ) { std::cout << "Resource Filter INI File(s) used: " << std::endl; From 399b8601a841427ea67e971cbf511f769eafa830 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 10 Feb 2026 14:41:23 +0000 Subject: [PATCH 103/124] Add two CLI tests for error .ini files - One for .ini file that does not exist - Another for .ini file with wrong format --- tests/src/ResourceToolsFilterTest.cpp | 2 +- tests/src/ResourcesCliTest.cpp | 68 +++++++++++++++++++++++++++ tools/src/FilterResourceFile.cpp | 2 +- 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/tests/src/ResourceToolsFilterTest.cpp b/tests/src/ResourceToolsFilterTest.cpp index 6813c06..67cfd41 100644 --- a/tests/src/ResourceToolsFilterTest.cpp +++ b/tests/src/ResourceToolsFilterTest.cpp @@ -1825,7 +1825,7 @@ TEST_F( ResourceToolsTest, FilterResourceFile_ConfirmFileLoadFailure_invalidMiss } catch( const std::invalid_argument& e ) { - std::string expectedError = "No namedSections defined in INI file: " + iniPath.generic_string(); + std::string expectedError = "No [namedSection] defined in INI file: " + iniPath.generic_string(); EXPECT_STREQ( e.what(), expectedError.c_str() ); } catch( ... ) diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index 2a779e3..94f2d57 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -460,6 +460,74 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ) << " Output file does not match expected gold file."; } +TEST_F( ResourcesCliTest, CreateGroup_ConfirmFailureParsingWronglyFormattedIniFile_UsingFilter_invalidMissingNamedSection_ini ) +{ + // Test parameters: + std::string output; + std::string errorOutput; + std::vector arguments; + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_invalidMissingNamedSection.yaml" ); + std::filesystem::path filterIniFilePath = "ExampleIniFiles/invalidMissingNamedSection.ini"; + + // Ensure any previous test output files are removed + RemoveFiles( { outputFilePath } ); + + arguments.push_back( "create-group" ); + arguments.push_back( inputDirectoryPath.lexically_normal().string() ); + arguments.push_back( "--verbosity-level" ); + arguments.push_back( "3" ); + arguments.push_back( "--filter-file" ); + arguments.push_back( filterIniFilePath.lexically_normal().string() ); + arguments.push_back( "--output-file" ); + arguments.push_back( outputFilePath.lexically_normal().string() ); + + int res = RunCli( arguments, output, errorOutput, TEST_DATA_BASE_PATH ); + std::cout << "--- RunCli() output: ---" << std::endl; + std::cout << output << std::endl; + std::cout << "------------------------" << std::endl; + + // Should fail, expecting non-zero exit code + ASSERT_EQ( res, 1 ) << "CLI operation should fail for a filter .ini file with missing named section - with resultCode=1"; + // Check for expected error message + EXPECT_TRUE( errorOutput.find("No [namedSection] defined in INI file") != std::string::npos ) + << "Expected error message about missing [namedSection]. Actual error: " << errorOutput; +} + +TEST_F( ResourcesCliTest, CreateGroup_ConfirmFailureUsingNoExistentFilterFile_iniFileDoesNotExist ) +{ + // Test parameters: + std::string output; + std::string errorOutput; + std::vector arguments; + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_iniFileDoesNotExist.yaml" ); + std::filesystem::path filterIniFilePath = "ExampleIniFiles/iniFileNotFound.ini"; + + // Ensure any previous test output files are removed + RemoveFiles( { outputFilePath } ); + + arguments.push_back( "create-group" ); + arguments.push_back( inputDirectoryPath.lexically_normal().string() ); + arguments.push_back( "--verbosity-level" ); + arguments.push_back( "3" ); + arguments.push_back( "--filter-file" ); + arguments.push_back( filterIniFilePath.lexically_normal().string() ); + arguments.push_back( "--output-file" ); + arguments.push_back( outputFilePath.lexically_normal().string() ); + + int res = RunCli( arguments, output, errorOutput, TEST_DATA_BASE_PATH ); + std::cout << "--- RunCli() output: ---" << std::endl; + std::cout << output << std::endl; + std::cout << "------------------------" << std::endl; + + // Should fail, expecting non-zero exit code + ASSERT_EQ( res, 1 ) << "CLI operation should fail for a non-existent filter .ini file - with resultCode=1"; + // Check for expected error message + EXPECT_TRUE( errorOutput.find("unable to open file") != std::string::npos ) + << "Expected error message about unable to open file. Actual error: " << errorOutput; +} + //--------------------------------------- TEST_F( ResourcesCliTest, CreateBundle ) diff --git a/tools/src/FilterResourceFile.cpp b/tools/src/FilterResourceFile.cpp index fdf7eee..f48f18c 100644 --- a/tools/src/FilterResourceFile.cpp +++ b/tools/src/FilterResourceFile.cpp @@ -90,7 +90,7 @@ void FilterResourceFile::ParseIniFile( const std::filesystem::path& iniFilePath if( allSections.size() <= 1 ) { // No namedSections defined - throw std::invalid_argument( "No namedSections defined in INI file: " + iniFilePath.generic_string() ); + throw std::invalid_argument( "No [namedSection] defined in INI file: " + iniFilePath.generic_string() ); } // Parse all other named sections From 5d1efb7fe8e6e2a11b4f5a34bd8332d36a67253e Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 11 Feb 2026 11:12:21 +0000 Subject: [PATCH 104/124] Remove sending error message to std::cerr in ResourceGroupImpl - Updated tests accordingly. --- src/ResourceGroupImpl.cpp | 2 -- tests/src/ResourcesCliTest.cpp | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/ResourceGroupImpl.cpp b/src/ResourceGroupImpl.cpp index e064777..64731cb 100644 --- a/src/ResourceGroupImpl.cpp +++ b/src/ResourceGroupImpl.cpp @@ -79,7 +79,6 @@ Result ResourceGroup::ResourceGroupImpl::CreateFromDirectory( const CreateResour catch( const std::exception& e ) { std::string errorMsg = "Unable to create ResourceFilter - because of: " + std::string( e.what() ); - std::cerr << errorMsg << std::endl; return Result{ ResultType::FAILED_TO_INITIALIZE_RESOURCE_FILTER, errorMsg }; } } @@ -106,7 +105,6 @@ Result ResourceGroup::ResourceGroupImpl::CreateFromDirectory( const CreateResour catch( const std::exception& e ) { std::string errorMsg = "Unable to decide on include/exclude filtering for: " + entry.path().generic_string() + " - because of: " + std::string( e.what() ); - std::cerr << errorMsg << std::endl; return Result{ ResultType::FAILED_TO_APPLY_RESOURCE_FILTER_RULES, errorMsg }; } } diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index 94f2d57..e30733b 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -490,8 +490,8 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFailureParsingWronglyFormattedIniFi // Should fail, expecting non-zero exit code ASSERT_EQ( res, 1 ) << "CLI operation should fail for a filter .ini file with missing named section - with resultCode=1"; // Check for expected error message - EXPECT_TRUE( errorOutput.find("No [namedSection] defined in INI file") != std::string::npos ) - << "Expected error message about missing [namedSection]. Actual error: " << errorOutput; + EXPECT_TRUE( errorOutput.find("[ERROR: Failed to initialize ResourceFilter from .ini file]") != std::string::npos ) + << "Expected generic (top-level) error message about failure to initialize ResourceFilter from .ini file. Actual error: " << errorOutput; } TEST_F( ResourcesCliTest, CreateGroup_ConfirmFailureUsingNoExistentFilterFile_iniFileDoesNotExist ) @@ -524,8 +524,8 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFailureUsingNoExistentFilterFile_in // Should fail, expecting non-zero exit code ASSERT_EQ( res, 1 ) << "CLI operation should fail for a non-existent filter .ini file - with resultCode=1"; // Check for expected error message - EXPECT_TRUE( errorOutput.find("unable to open file") != std::string::npos ) - << "Expected error message about unable to open file. Actual error: " << errorOutput; + EXPECT_TRUE( errorOutput.find("[ERROR: Failed to initialize ResourceFilter from .ini file]") != std::string::npos ) + << "Expected generic (top-level) error message about failure to initialize ResourceFilter from .ini file. Actual error: " << errorOutput; } //--------------------------------------- From d369fe1ae949d64ff474fe6f7db081777fe52903 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 11 Feb 2026 12:32:02 +0000 Subject: [PATCH 105/124] Change RunCli() so that standard and errorOut are only populated if/when needed --- tests/src/CliTestFixture.cpp | 16 +-- tests/src/CliTestFixture.h | 2 +- tests/src/ResourcesCliTest.cpp | 247 +++++++++++++-------------------- 3 files changed, 100 insertions(+), 165 deletions(-) diff --git a/tests/src/CliTestFixture.cpp b/tests/src/CliTestFixture.cpp index 8c19ce1..1748ebc 100644 --- a/tests/src/CliTestFixture.cpp +++ b/tests/src/CliTestFixture.cpp @@ -6,11 +6,8 @@ #include -int CliTestFixture::RunCli( std::vector& arguments, std::string& output, std::string& errorOutput, const std::string& workingDirectory /* = "" (empty = do not alter it) */ ) +int CliTestFixture::RunCli( std::vector& arguments, std::string* standardOutput /* = nullptr */, std::string* errorOutput /* = nullptr */, const std::string& workingDirectory /* = "" (empty = do not alter it) */ ) { - std::string processOutput; - std::string processError; - arguments.insert( arguments.begin(), CARBON_RESOURCES_CLI_EXE_FULLPATH ); std::cout << "--- RunCli() arguments: ---" << std::endl; @@ -20,17 +17,16 @@ int CliTestFixture::RunCli( std::vector& arguments, std::string& ou } std::cout << "---------------------------" << std::endl; + // Only populate the output and errorOutput if the caller provided non-nullptr for them, otherwise discard them TinyProcessLib::Process process1a( - arguments, workingDirectory, - [&processOutput]( const char* bytes, size_t n ) { processOutput += std::string( bytes, n ); }, - [&processError]( const char* bytes, size_t n ) { processError += std::string( bytes, n ); } + arguments, + workingDirectory, + [standardOutput]( const char* bytes, size_t n ) { if (standardOutput != nullptr) { *standardOutput += std::string( bytes, n ); } }, + [errorOutput]( const char* bytes, size_t n ) { if (errorOutput != nullptr) { *errorOutput += std::string( bytes, n ); } } ); auto exit_status = process1a.get_exit_status(); - output = processOutput; - errorOutput = processError; - return exit_status; } diff --git a/tests/src/CliTestFixture.h b/tests/src/CliTestFixture.h index 4577367..71bc6df 100644 --- a/tests/src/CliTestFixture.h +++ b/tests/src/CliTestFixture.h @@ -12,7 +12,7 @@ struct CliTestFixture : public ResourcesTestFixture { - int RunCli( std::vector& arguments, std::string& output, std::string& errorOutput, const std::string& workingDirectory = "" ); + int RunCli( std::vector& arguments, std::string* standardOutput = nullptr, std::string* errorOutput = nullptr, const std::string& workingDirectory = "" ); // Helper to remove files as part of a test run and cleanup void RemoveFiles( const std::vector& filesToRemove ); diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index e30733b..d9cdc58 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -8,12 +8,9 @@ struct ResourcesCliTest : public CliTestFixture TEST_F( ResourcesCliTest, RunWithoutArguments ) { - std::string output; - std::string errorOutput; - std::vector arguments; - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments ); // Expect 4 which indicates failed with no command specified ASSERT_EQ( res, 4 ); @@ -21,14 +18,11 @@ TEST_F( ResourcesCliTest, RunWithoutArguments ) TEST_F( ResourcesCliTest, RunWithNonesenseArguments ) { - std::string output; - std::string errorOutput; - std::vector arguments; arguments.push_back( "Nonesense" ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments ); // Expect 3 which indicates failed due to invalid operation ASSERT_EQ( res, 3 ); @@ -36,14 +30,11 @@ TEST_F( ResourcesCliTest, RunWithNonesenseArguments ) TEST_F( ResourcesCliTest, RunCreateGroupWithNoArguments ) { - std::string output; - std::string errorOutput; - std::vector arguments; arguments.push_back( "create-group" ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments ); // Expect 2 which failed due to invalid operation arguments ASSERT_EQ( res, 2 ); @@ -51,14 +42,11 @@ TEST_F( ResourcesCliTest, RunCreateGroupWithNoArguments ) TEST_F( ResourcesCliTest, RunCreatePatchWithNoArguments ) { - std::string output; - std::string errorOutput; - std::vector arguments; arguments.push_back( "create-patch" ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments ); // Expect 2 which failed due to invalid operation arguments ASSERT_EQ( res, 2 ); @@ -66,14 +54,11 @@ TEST_F( ResourcesCliTest, RunCreatePatchWithNoArguments ) TEST_F( ResourcesCliTest, RunCreateBundleWithNoArguments ) { - std::string output; - std::string errorOutput; - std::vector arguments; arguments.push_back( "create-bundle" ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments ); // Expect 2 which failed due to invalid operation arguments ASSERT_EQ( res, 2 ); @@ -83,14 +68,11 @@ TEST_F( ResourcesCliTest, RunCreateBundleWithNoArguments ) TEST_F( ResourcesCliTest, RunApplyPatchWithNoArguments ) { - std::string output; - std::string errorOutput; - std::vector arguments; arguments.push_back( "apply-patch" ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments ); // Expect 2 which failed due to invalid operation arguments ASSERT_EQ( res, 2 ); @@ -98,14 +80,11 @@ TEST_F( ResourcesCliTest, RunApplyPatchWithNoArguments ) TEST_F( ResourcesCliTest, RunUnpackBundleWithNoArguments ) { - std::string output; - std::string errorOutput; - std::vector arguments; arguments.push_back( "unpack-bundle" ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments ); // Expect 2 which failed due to invalid operation arguments ASSERT_EQ( res, 2 ); @@ -113,9 +92,6 @@ TEST_F( ResourcesCliTest, RunUnpackBundleWithNoArguments ) TEST_F( ResourcesCliTest, CreateOperationWithInvalidInput ) { - std::string output; - std::string errorOutput; - std::vector arguments; arguments.push_back( "create-group" ); @@ -126,7 +102,7 @@ TEST_F( ResourcesCliTest, CreateOperationWithInvalidInput ) std::filesystem::path inputDirectory = "INVALID_PATH"; arguments.push_back( inputDirectory.string() ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments ); // Expect return 1 indicating failed during valid operation ASSERT_EQ( res, 1 ); @@ -135,9 +111,7 @@ TEST_F( ResourcesCliTest, CreateOperationWithInvalidInput ) #endif TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectory ) { - std::string output; std::string errorOutput; - std::vector arguments; arguments.push_back( "create-group" ); @@ -152,7 +126,7 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectory ) std::filesystem::path outputFile = "GroupOut/ResourceGroup.yaml"; arguments.push_back( outputFile.string() ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments, nullptr, &errorOutput ); ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; @@ -168,9 +142,7 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectory ) TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryExportResources ) { - std::string output; std::string errorOutput; - std::vector arguments; arguments.push_back( "create-group" ); @@ -178,12 +150,12 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryExportResources ) arguments.push_back( "--verbosity-level" ); arguments.push_back( "3" ); - arguments.push_back( "--export-resources" ); + arguments.push_back( "--export-resources" ); - arguments.push_back( "--export-resources-destination-type" ); + arguments.push_back( "--export-resources-destination-type" ); arguments.push_back( "LOCAL_RELATIVE" ); - arguments.push_back( "--export-resources-destination-path" ); + arguments.push_back( "--export-resources-destination-path" ); std::string exportOutputPath = "ExportedResources"; arguments.push_back( exportOutputPath ); @@ -194,7 +166,7 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryExportResources ) std::filesystem::path outputFile = "GroupOut/ResourceGroup.yaml"; arguments.push_back( outputFile.string() ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments, nullptr, &errorOutput ); ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; @@ -207,14 +179,12 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryExportResources ) #endif EXPECT_TRUE( FilesMatch( goldFile, outputFile ) ); - EXPECT_TRUE( DirectoryIsSubset( exportOutputPath, inputDirectory ) ); + EXPECT_TRUE( DirectoryIsSubset( exportOutputPath, inputDirectory ) ); } TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryWithSkipCompression ) { - std::string output; std::string errorOutput; - std::vector arguments; arguments.push_back( "create-group" ); @@ -222,7 +192,7 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryWithSkipCompression ) arguments.push_back( "--verbosity-level" ); arguments.push_back( "3" ); - arguments.push_back( "--skip-compression" ); + arguments.push_back( "--skip-compression" ); std::filesystem::path inputDirectory = GetTestFileFileAbsolutePath( "CreateResourceFiles/ResourceFiles" ); arguments.push_back( inputDirectory.string() ); @@ -231,7 +201,7 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryWithSkipCompression ) std::filesystem::path outputFile = "GroupOut/ResourceGroup.yaml"; arguments.push_back( outputFile.string() ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments, nullptr, &errorOutput ); ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; @@ -247,9 +217,7 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryWithSkipCompression ) TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryOldDocumentFormat ) { - std::string output; std::string errorOutput; - std::vector arguments; arguments.push_back( "create-group" ); @@ -267,7 +235,7 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryOldDocumentFormat ) arguments.push_back( "--document-version" ); arguments.push_back( "0.0.0" ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments, nullptr, &errorOutput ); ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; @@ -283,9 +251,7 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryOldDocumentFormat ) TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryOldDocumentFormatWithPrefix ) { - std::string output; std::string errorOutput; - std::vector arguments; arguments.push_back( "create-group" ); @@ -306,7 +272,7 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryOldDocumentFormatWithP arguments.push_back( "--resource-prefix" ); arguments.push_back( "test" ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments, nullptr, &errorOutput ); ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; @@ -348,7 +314,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); - int res = RunCli( arguments, output, errorOutput, TEST_DATA_BASE_PATH ); + int res = RunCli( arguments, &output, &errorOutput, TEST_DATA_BASE_PATH ); std::cout << "--- RunCli() output: ---" << std::endl; std::cout << output << std::endl; std::cout << "------------------------" << std::endl; @@ -392,7 +358,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); - int res = RunCli( arguments, output, errorOutput, TEST_DATA_BASE_PATH ); + int res = RunCli( arguments, &output, &errorOutput, TEST_DATA_BASE_PATH ); std::cout << "--- RunCli() output: ---" << std::endl; std::cout << output << std::endl; std::cout << "------------------------" << std::endl; @@ -416,7 +382,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 std::string output; std::string errorOutput; std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ) ; // The base testData directory + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_validSimpleAndComplexExample1.yaml" ); std::vector filterIniFilePaths = { "ExampleIniFiles/validSimpleExample1.ini", @@ -442,7 +408,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); - int res = RunCli( arguments, output, errorOutput, TEST_DATA_BASE_PATH ); + int res = RunCli( arguments, &output, &errorOutput, TEST_DATA_BASE_PATH ); std::cout << "--- RunCli() output: ---" << std::endl; std::cout << output << std::endl; std::cout << "------------------------" << std::endl; @@ -463,78 +429,76 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 TEST_F( ResourcesCliTest, CreateGroup_ConfirmFailureParsingWronglyFormattedIniFile_UsingFilter_invalidMissingNamedSection_ini ) { // Test parameters: - std::string output; - std::string errorOutput; - std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory - std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_invalidMissingNamedSection.yaml" ); - std::filesystem::path filterIniFilePath = "ExampleIniFiles/invalidMissingNamedSection.ini"; - - // Ensure any previous test output files are removed - RemoveFiles( { outputFilePath } ); - - arguments.push_back( "create-group" ); - arguments.push_back( inputDirectoryPath.lexically_normal().string() ); - arguments.push_back( "--verbosity-level" ); - arguments.push_back( "3" ); - arguments.push_back( "--filter-file" ); - arguments.push_back( filterIniFilePath.lexically_normal().string() ); - arguments.push_back( "--output-file" ); - arguments.push_back( outputFilePath.lexically_normal().string() ); - - int res = RunCli( arguments, output, errorOutput, TEST_DATA_BASE_PATH ); - std::cout << "--- RunCli() output: ---" << std::endl; - std::cout << output << std::endl; - std::cout << "------------------------" << std::endl; - - // Should fail, expecting non-zero exit code - ASSERT_EQ( res, 1 ) << "CLI operation should fail for a filter .ini file with missing named section - with resultCode=1"; - // Check for expected error message - EXPECT_TRUE( errorOutput.find("[ERROR: Failed to initialize ResourceFilter from .ini file]") != std::string::npos ) - << "Expected generic (top-level) error message about failure to initialize ResourceFilter from .ini file. Actual error: " << errorOutput; + std::string output; + std::string errorOutput; + std::vector arguments; + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_invalidMissingNamedSection.yaml" ); + std::filesystem::path filterIniFilePath = "ExampleIniFiles/invalidMissingNamedSection.ini"; + + // Ensure any previous test output files are removed + RemoveFiles( { outputFilePath } ); + + arguments.push_back( "create-group" ); + arguments.push_back( inputDirectoryPath.lexically_normal().string() ); + arguments.push_back( "--verbosity-level" ); + arguments.push_back( "3" ); + arguments.push_back( "--filter-file" ); + arguments.push_back( filterIniFilePath.lexically_normal().string() ); + arguments.push_back( "--output-file" ); + arguments.push_back( outputFilePath.lexically_normal().string() ); + + int res = RunCli( arguments, &output, &errorOutput, TEST_DATA_BASE_PATH ); + std::cout << "--- RunCli() output: ---" << std::endl; + std::cout << output << std::endl; + std::cout << "------------------------" << std::endl; + + // Should fail, expecting non-zero exit code + ASSERT_EQ( res, 1 ) << "CLI operation should fail for a filter .ini file with missing named section - with resultCode=1"; + // Check for expected error message + EXPECT_TRUE( errorOutput.find( "[ERROR: Failed to initialize ResourceFilter from .ini file]" ) != std::string::npos ) + << "Expected generic (top-level) error message about failure to initialize ResourceFilter from .ini file. Actual error: " << errorOutput; } TEST_F( ResourcesCliTest, CreateGroup_ConfirmFailureUsingNoExistentFilterFile_iniFileDoesNotExist ) { // Test parameters: - std::string output; - std::string errorOutput; - std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory - std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_iniFileDoesNotExist.yaml" ); - std::filesystem::path filterIniFilePath = "ExampleIniFiles/iniFileNotFound.ini"; - - // Ensure any previous test output files are removed - RemoveFiles( { outputFilePath } ); - - arguments.push_back( "create-group" ); - arguments.push_back( inputDirectoryPath.lexically_normal().string() ); - arguments.push_back( "--verbosity-level" ); - arguments.push_back( "3" ); - arguments.push_back( "--filter-file" ); - arguments.push_back( filterIniFilePath.lexically_normal().string() ); - arguments.push_back( "--output-file" ); - arguments.push_back( outputFilePath.lexically_normal().string() ); - - int res = RunCli( arguments, output, errorOutput, TEST_DATA_BASE_PATH ); - std::cout << "--- RunCli() output: ---" << std::endl; - std::cout << output << std::endl; - std::cout << "------------------------" << std::endl; - - // Should fail, expecting non-zero exit code - ASSERT_EQ( res, 1 ) << "CLI operation should fail for a non-existent filter .ini file - with resultCode=1"; - // Check for expected error message - EXPECT_TRUE( errorOutput.find("[ERROR: Failed to initialize ResourceFilter from .ini file]") != std::string::npos ) - << "Expected generic (top-level) error message about failure to initialize ResourceFilter from .ini file. Actual error: " << errorOutput; + std::string output; + std::string errorOutput; + std::vector arguments; + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_iniFileDoesNotExist.yaml" ); + std::filesystem::path filterIniFilePath = "ExampleIniFiles/iniFileNotFound.ini"; + + // Ensure any previous test output files are removed + RemoveFiles( { outputFilePath } ); + + arguments.push_back( "create-group" ); + arguments.push_back( inputDirectoryPath.lexically_normal().string() ); + arguments.push_back( "--verbosity-level" ); + arguments.push_back( "3" ); + arguments.push_back( "--filter-file" ); + arguments.push_back( filterIniFilePath.lexically_normal().string() ); + arguments.push_back( "--output-file" ); + arguments.push_back( outputFilePath.lexically_normal().string() ); + + int res = RunCli( arguments, &output, &errorOutput, TEST_DATA_BASE_PATH ); + std::cout << "--- RunCli() output: ---" << std::endl; + std::cout << output << std::endl; + std::cout << "------------------------" << std::endl; + + // Should fail, expecting non-zero exit code + ASSERT_EQ( res, 1 ) << "CLI operation should fail for a non-existent filter .ini file - with resultCode=1"; + // Check for expected error message + EXPECT_TRUE( errorOutput.find( "[ERROR: Failed to initialize ResourceFilter from .ini file]" ) != std::string::npos ) + << "Expected generic (top-level) error message about failure to initialize ResourceFilter from .ini file. Actual error: " << errorOutput; } //--------------------------------------- TEST_F( ResourcesCliTest, CreateBundle ) { - std::string output; std::string errorOutput; - std::vector arguments; arguments.push_back( "create-bundle" ); @@ -566,7 +530,7 @@ TEST_F( ResourcesCliTest, CreateBundle ) arguments.push_back( "1000" ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments, nullptr, &errorOutput ); EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; @@ -580,9 +544,7 @@ TEST_F( ResourcesCliTest, CreateBundle ) TEST_F( ResourcesCliTest, RemoveResourcesWithUnknownResourceIgnoreOnResourceNotFound ) { - std::string output; std::string errorOutput; - std::vector arguments; arguments.push_back( "remove-resources" ); @@ -606,16 +568,13 @@ TEST_F( ResourcesCliTest, RemoveResourcesWithUnknownResourceIgnoreOnResourceNotF arguments.push_back( "--ignore-missing-resources" ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments, nullptr, &errorOutput ); EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; } TEST_F( ResourcesCliTest, RemoveResourcesWithUnknownResourceWithInvalidPathToResourcesFile ) { - std::string output; - std::string errorOutput; - std::vector arguments; arguments.push_back( "remove-resources" ); @@ -637,16 +596,13 @@ TEST_F( ResourcesCliTest, RemoveResourcesWithUnknownResourceWithInvalidPathToRes arguments.push_back( resourceGroupAfterRemovePath.string() ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments ); EXPECT_EQ( res, 1 ); } TEST_F( ResourcesCliTest, RemoveResourcesWithUnknownResource ) { - std::string output; - std::string errorOutput; - std::vector arguments; arguments.push_back( "remove-resources" ); @@ -668,16 +624,14 @@ TEST_F( ResourcesCliTest, RemoveResourcesWithUnknownResource ) arguments.push_back( resourceGroupAfterRemovePath.string() ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments ); EXPECT_EQ( res, 1 ); } TEST_F( ResourcesCliTest, RemoveResources ) { - std::string output; std::string errorOutput; - std::vector arguments; arguments.push_back( "remove-resources" ); @@ -699,7 +653,7 @@ TEST_F( ResourcesCliTest, RemoveResources ) arguments.push_back( resourceGroupAfterRemovePath.string() ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments, nullptr, &errorOutput ); EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; @@ -711,9 +665,7 @@ TEST_F( ResourcesCliTest, RemoveResources ) TEST_F( ResourcesCliTest, DiffResourceGroupsWithTwoAdditions ) { - std::string output; std::string errorOutput; - std::vector arguments; arguments.push_back( "diff-group" ); @@ -735,7 +687,7 @@ TEST_F( ResourcesCliTest, DiffResourceGroupsWithTwoAdditions ) arguments.push_back( outputPath.string() ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments, nullptr, &errorOutput ); EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; @@ -751,9 +703,7 @@ TEST_F( ResourcesCliTest, DiffResourceGroupsWithTwoAdditions ) TEST_F( ResourcesCliTest, DiffResourceGroupsWithTwoChanges ) { - std::string output; std::string errorOutput; - std::vector arguments; arguments.push_back( "diff-group" ); @@ -775,7 +725,7 @@ TEST_F( ResourcesCliTest, DiffResourceGroupsWithTwoChanges ) arguments.push_back( outputPath.string() ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments, nullptr, &errorOutput ); EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; @@ -791,9 +741,7 @@ TEST_F( ResourcesCliTest, DiffResourceGroupsWithTwoChanges ) TEST_F( ResourcesCliTest, DiffResourceGroupsWithTwoSubtractions ) { - std::string output; std::string errorOutput; - std::vector arguments; arguments.push_back( "diff-group" ); @@ -815,7 +763,7 @@ TEST_F( ResourcesCliTest, DiffResourceGroupsWithTwoSubtractions ) arguments.push_back( outputPath.string() ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments, nullptr, &errorOutput ); EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; @@ -831,7 +779,6 @@ TEST_F( ResourcesCliTest, DiffResourceGroupsWithTwoSubtractions ) TEST_F( ResourcesCliTest, MergeGroup ) { - std::string output; std::string errorOutput; std::vector arguments; @@ -855,7 +802,7 @@ TEST_F( ResourcesCliTest, MergeGroup ) arguments.push_back( mergedOutputPath.string() ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments, nullptr, &errorOutput ); EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; @@ -867,9 +814,7 @@ TEST_F( ResourcesCliTest, MergeGroup ) TEST_F( ResourcesCliTest, CreatePatch ) { - std::string output; std::string errorOutput; - std::vector arguments; arguments.push_back( "create-patch" ); @@ -910,7 +855,7 @@ TEST_F( ResourcesCliTest, CreatePatch ) arguments.push_back( "--chunk-size" ); arguments.push_back( "50000000" ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments, nullptr, &errorOutput ); EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; @@ -924,9 +869,7 @@ TEST_F( ResourcesCliTest, CreatePatch ) TEST_F( ResourcesCliTest, CreateGroup ) { - std::string output; std::string errorOutput; - std::vector arguments; arguments.push_back( "create-group" ); @@ -944,7 +887,7 @@ TEST_F( ResourcesCliTest, CreateGroup ) arguments.push_back( outputFilename ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments, nullptr, &errorOutput ); EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; @@ -963,9 +906,7 @@ TEST_F( ResourcesCliTest, CreateGroup ) TEST_F( ResourcesCliTest, ApplyPatch ) { - std::string output; std::string errorOutput; - std::vector arguments; arguments.push_back( "apply-patch" ); @@ -1008,7 +949,7 @@ TEST_F( ResourcesCliTest, ApplyPatch ) std::filesystem::copy( resourcesToPatchBasePath, outputBasePath ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments, nullptr, &errorOutput ); EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; @@ -1019,9 +960,7 @@ TEST_F( ResourcesCliTest, ApplyPatch ) TEST_F( ResourcesCliTest, UnpackBundle ) { - std::string output; std::string errorOutput; - std::vector arguments; arguments.push_back( "unpack-bundle" ); @@ -1043,7 +982,7 @@ TEST_F( ResourcesCliTest, UnpackBundle ) arguments.push_back( "LOCAL_RELATIVE" ); - int res = RunCli( arguments, output, errorOutput ); + int res = RunCli( arguments, nullptr, &errorOutput ); EXPECT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; From b26ad54779e74cbb1cd1c0ff235445271a1a9c48 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 11 Feb 2026 16:28:57 +0000 Subject: [PATCH 106/124] Change GetResolvedResfileMap() to return a pointer to a map (because of optional) --- tests/src/ResourceToolsFilterTest.cpp | 154 ++++++++++++++------------ tools/include/FilterNamedSection.h | 4 +- tools/src/FilterNamedSection.cpp | 11 +- 3 files changed, 92 insertions(+), 77 deletions(-) diff --git a/tests/src/ResourceToolsFilterTest.cpp b/tests/src/ResourceToolsFilterTest.cpp index 67cfd41..72b80c2 100644 --- a/tests/src/ResourceToolsFilterTest.cpp +++ b/tests/src/ResourceToolsFilterTest.cpp @@ -917,11 +917,11 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_SingleLineRespathIsAllowe std::vector expectedExcludes = { ".ex1" }; const auto& resolvedRespathMap = namedSection.GetResolvedRespathsMap(); - const auto& resolvedResfileMap = namedSection.GetResolvedResfileMap(); + const auto* resolvedResfileMap = namedSection.GetResolvedResfileMap(); const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); ValidatePathMap( expectedPaths, resolvedRespathMap, expectedIncludes, expectedExcludes, "ResolvedRespathsMap" ); - EXPECT_TRUE( resolvedResfileMap.empty() ); + EXPECT_TRUE( !resolvedResfileMap || resolvedResfileMap->empty() ); ValidatePathMap( expectedPaths, combinedMap, expectedIncludes, expectedExcludes, "CombinedResolvedPathMap" ); } @@ -943,11 +943,11 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_EmptyTopLevelFilterIsAllo std::vector expectedExcludes = {}; const auto& resolvedRespathMap = namedSection.GetResolvedRespathsMap(); - const auto& resolvedResfileMap = namedSection.GetResolvedResfileMap(); + const auto* resolvedResfileMap = namedSection.GetResolvedResfileMap(); const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); ValidatePathMap( expectedPaths, resolvedRespathMap, expectedIncludes, expectedExcludes, "ResolvedRespathsMap" ); - EXPECT_TRUE( resolvedResfileMap.empty() ); + EXPECT_TRUE( !resolvedResfileMap || resolvedResfileMap->empty() ); ValidatePathMap( expectedPaths, combinedMap, expectedIncludes, expectedExcludes, "CombinedResolvedPathMap" ); } @@ -969,11 +969,11 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_OnlyTopLevelExcludeFilter std::vector expectedExcludes = { ".ex1" }; const auto& resolvedRespathMap = namedSection.GetResolvedRespathsMap(); - const auto& resolvedResfileMap = namedSection.GetResolvedResfileMap(); + const auto* resolvedResfileMap = namedSection.GetResolvedResfileMap(); const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); ValidatePathMap( expectedPaths, resolvedRespathMap, expectedIncludes, expectedExcludes, "ResolvedRespathsMap" ); - EXPECT_TRUE( resolvedResfileMap.empty() ); + EXPECT_TRUE( !resolvedResfileMap || resolvedResfileMap->empty() ); ValidatePathMap( expectedPaths, combinedMap, expectedIncludes, expectedExcludes, "CombinedResolvedPathMap" ); } @@ -1001,13 +1001,13 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_MultiLineRespathWithSomeI std::vector firstLineExcludes = { ".ex1", ".exLine1" }; const auto& resolvedRespathsMap = namedSection.GetResolvedRespathsMap(); - const auto& resolvedResfileMap = namedSection.GetResolvedResfileMap(); + const auto* resolvedResfileMap = namedSection.GetResolvedResfileMap(); const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); MapContainsPaths( allExpectedPaths, resolvedRespathsMap, "ResolvedRespathsMap" ); ValidatePathMap( firstLinePaths, resolvedRespathsMap, firstLineIncludes, firstLineExcludes, "FirstLine ResolvedRespathsMap" ); ValidatePathMap( secondLinePaths, resolvedRespathsMap, defaultIncludes, defaultExcludes, "SecondLine ResolvedRespathsMap" ); - EXPECT_TRUE( resolvedResfileMap.empty() ); + EXPECT_TRUE( !resolvedResfileMap || resolvedResfileMap->empty() ); MapContainsPaths( allExpectedPaths, combinedMap, "CombinedResolvedMap" ); ValidatePathMap( firstLinePaths, combinedMap, firstLineIncludes, firstLineExcludes, "FirstLine ResolvedRespathsMap" ); ValidatePathMap( secondLinePaths, combinedMap, defaultIncludes, defaultExcludes, "SecondLine ResolvedRespathsMap" ); @@ -1034,16 +1034,17 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_CombinedSingleLineRespath std::vector defaultExcludes = { ".ex1" }; const auto& respathsMap = namedSection.GetResolvedRespathsMap(); - const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto* resfileMap = namedSection.GetResolvedResfileMap(); const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); ASSERT_EQ( respathsMap.size(), 2 ); // prefix1 has two paths MapContainsPaths( respathsPaths, respathsMap, "ResolvedRespathsMap" ); ValidatePathMap( respathsPaths, respathsMap, defaultIncludes, defaultExcludes, "ResolvedRespathsMap" ); - ASSERT_EQ( resfileMap.size(), 1 ); // prefix2 has one path - MapContainsPaths( resfilesPaths, resfileMap, "ResolvedResfileMap" ); - ValidatePathMap( resfilesPaths, resfileMap, defaultIncludes, defaultExcludes, "ResolvedResfileMap" ); + ASSERT_TRUE( resfileMap != nullptr ); + ASSERT_EQ( resfileMap->size(), 1 ); // prefix2 has one path + MapContainsPaths( resfilesPaths, *resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( resfilesPaths, *resfileMap, defaultIncludes, defaultExcludes, "ResolvedResfileMap" ); ASSERT_EQ( combinedMap.size(), 3 ); // 2 from respaths, 1 from resfile MapContainsPaths( allExpectedPaths, combinedMap, "ResolvedCombinedMap" ); @@ -1069,15 +1070,14 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_RespathWithoutResfileAttr std::vector defaultExcludes = { ".ex1" }; const auto& respathsMap = namedSection.GetResolvedRespathsMap(); - const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto* resfileMap = namedSection.GetResolvedResfileMap(); const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); ASSERT_EQ( respathsMap.size(), 1 ); MapContainsPaths( onlyValidPaths, respathsMap, "ResolvedRespathsMap" ); ValidatePathMap( onlyValidPaths, respathsMap, defaultIncludes, defaultExcludes, "ResolvedRespathsMap" ); - ASSERT_EQ( resfileMap.size(), 0 ); // Nothing in resfile - EXPECT_TRUE( resfileMap.empty() ); + ASSERT_TRUE( !resfileMap || resfileMap->empty() ); // Nothing in resfile ASSERT_EQ( combinedMap.size(), 1 ); // 1 from respaths, 0 from resfile MapContainsPaths( onlyValidPaths, combinedMap, "ResolvedCombinedMap" ); @@ -1133,16 +1133,17 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_CombinedResolvedMapOnSame std::vector defaultExcludes = {}; const auto& respathsMap = namedSection.GetResolvedRespathsMap(); - const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto* resfileMap = namedSection.GetResolvedResfileMap(); const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); ASSERT_EQ( respathsMap.size(), 1 ); // "/path1/foo/bar" MapContainsPaths( respathsPaths, respathsMap, "ResolvedRespathsMap" ); ValidatePathMap( respathsPaths, respathsMap, defaultIncludes, defaultExcludes, "ResolvedRespathsMap" ); - ASSERT_EQ( resfileMap.size(), 1 ); // "/path1/loo/bar" - MapContainsPaths( resfilesPaths, resfileMap, "ResolvedResfileMap" ); - ValidatePathMap( resfilesPaths, resfileMap, defaultIncludes, defaultExcludes, "ResolvedResfileMap" ); + ASSERT_TRUE( resfileMap != nullptr ); + ASSERT_EQ( resfileMap->size(), 1 ); // "/path1/loo/bar" + MapContainsPaths( resfilesPaths, *resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( resfilesPaths, *resfileMap, defaultIncludes, defaultExcludes, "ResolvedResfileMap" ); ASSERT_EQ( combinedMap.size(), 2 ); // both MapContainsPaths( combinedPaths, combinedMap, "ResolvedCombinedMap" ); @@ -1171,16 +1172,17 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_CombinedResolvedMapWithEm std::vector defaultExcludes = {}; const auto& respathsMap = namedSection.GetResolvedRespathsMap(); - const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto* resfileMap = namedSection.GetResolvedResfileMap(); const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); ASSERT_EQ( respathsMap.size(), 1 ); // "/path1/foo/bar" MapContainsPaths( respathsPaths, respathsMap, "ResolvedRespathsMap" ); ValidatePathMap( respathsPaths, respathsMap, defaultIncludes, defaultExcludes, "ResolvedRespathsMap" ); - ASSERT_EQ( resfileMap.size(), 1 ); // "/path1/loo/bar" - MapContainsPaths( resfilesPaths, resfileMap, "ResolvedResfileMap" ); - ValidatePathMap( resfilesPaths, resfileMap, defaultIncludes, defaultExcludes, "ResolvedResfileMap" ); + ASSERT_TRUE( resfileMap != nullptr ); + ASSERT_EQ( resfileMap->size(), 1 ); // "/path1/loo/bar" + MapContainsPaths( resfilesPaths, *resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( resfilesPaths, *resfileMap, defaultIncludes, defaultExcludes, "ResolvedResfileMap" ); ASSERT_EQ( combinedMap.size(), 2 ); // both MapContainsPaths( combinedPaths, combinedMap, "ResolvedCombinedMap" ); @@ -1217,16 +1219,17 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_CombinedResolvedMapWithOn std::vector defaultExcludes = { ".ex1" }; const auto& respathsMap = namedSection.GetResolvedRespathsMap(); - const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto* resfileMap = namedSection.GetResolvedResfileMap(); const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); ASSERT_EQ( respathsMap.size(), 1 ); // "/path1/foo/bar" MapContainsPaths( respathsPaths, respathsMap, "ResolvedRespathsMap" ); ValidatePathMap( respathsPaths, respathsMap, defaultIncludes, defaultExcludes, "ResolvedRespathsMap" ); - ASSERT_EQ( resfileMap.size(), 1 ); // "/path1/loo/bar" - MapContainsPaths( resfilesPaths, resfileMap, "ResolvedResfileMap" ); - ValidatePathMap( resfilesPaths, resfileMap, defaultIncludes, defaultExcludes, "ResolvedResfileMap" ); + ASSERT_TRUE( resfileMap != nullptr ); + ASSERT_EQ( resfileMap->size(), 1 ); // "/path1/loo/bar" + MapContainsPaths( resfilesPaths, *resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( resfilesPaths, *resfileMap, defaultIncludes, defaultExcludes, "ResolvedResfileMap" ); ASSERT_EQ( combinedMap.size(), 2 ); // both MapContainsPaths( combinedPaths, combinedMap, "ResolvedCombinedMap" ); @@ -1266,16 +1269,17 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_EmptyTopLevelFilterWithRe std::vector overrideExcludes = { ".inlineExclude" }; const auto& respathsMap = namedSection.GetResolvedRespathsMap(); - const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto* resfileMap = namedSection.GetResolvedResfileMap(); const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); ASSERT_EQ( respathsMap.size(), 1 ); // "/path1/foo/bar" + "[ *, .inlineInclude ] ![ .inlineExclude ]" MapContainsPaths( respathsPaths, respathsMap, "ResolvedRespathsMap" ); ValidatePathMap( respathsPaths, respathsMap, overrideIncludes, overrideExcludes, "ResolvedRespathsMap" ); - ASSERT_EQ( resfileMap.size(), 1 ); // "/path1/loo/bar" + "[ * ]" - MapContainsPaths( resfilesPaths, resfileMap, "ResolvedResfileMap" ); - ValidatePathMap( resfilesPaths, resfileMap, defaultIncludes, defaultExcludes, "ResolvedResfileMap" ); + ASSERT_TRUE( resfileMap != nullptr ); + ASSERT_EQ( resfileMap->size(), 1 ); // "/path1/loo/bar" + "[ * ]" + MapContainsPaths( resfilesPaths, *resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( resfilesPaths, *resfileMap, defaultIncludes, defaultExcludes, "ResolvedResfileMap" ); ASSERT_EQ( combinedMap.size(), 2 ); // both MapContainsPaths( combinedPaths, combinedMap, "ResolvedCombinedMap" ); @@ -1328,16 +1332,17 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_EmptyTopLevelFilterWithRe std::vector overrideExcludes = { ".inlineExclude" }; const auto& respathsMap = namedSection.GetResolvedRespathsMap(); - const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto* resfileMap = namedSection.GetResolvedResfileMap(); const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); ASSERT_EQ( respathsMap.size(), 1 ); // "/path1/foo/bar" + "[ * ]" MapContainsPaths( respathsPaths, respathsMap, "ResolvedRespathsMap" ); ValidatePathMap( respathsPaths, respathsMap, defaultIncludes, defaultExcludes, "ResolvedRespathsMap" ); - ASSERT_EQ( resfileMap.size(), 1 ); // "/path1/loo/bar" + "[ *, .inlineInclude ] ![ .inlineExclude ]" - MapContainsPaths( resfilesPaths, resfileMap, "ResolvedResfileMap" ); - ValidatePathMap( resfilesPaths, resfileMap, overrideIncludes, overrideExcludes, "ResolvedResfileMap" ); + ASSERT_TRUE( resfileMap != nullptr ); + ASSERT_EQ( resfileMap->size(), 1 ); // "/path1/loo/bar" + "[ *, .inlineInclude ] ![ .inlineExclude ]" + MapContainsPaths( resfilesPaths, *resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( resfilesPaths, *resfileMap, overrideIncludes, overrideExcludes, "ResolvedResfileMap" ); ASSERT_EQ( combinedMap.size(), 2 ); // both MapContainsPaths( combinedPaths, combinedMap, "ResolvedCombinedMap" ); @@ -1390,16 +1395,17 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_OnlyExcludeTopLevelFilter std::vector overrideExcludes = { ".toplevelExclude", ".inlineExclude" }; const auto& respathsMap = namedSection.GetResolvedRespathsMap(); - const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto* resfileMap = namedSection.GetResolvedResfileMap(); const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); ASSERT_EQ( respathsMap.size(), 1 ); // "/path1/foo/bar" + "[ *, .inlineInclude ] ![ .toplevelExclude .inlineExclude ]" MapContainsPaths( respathsPaths, respathsMap, "ResolvedRespathsMap" ); ValidatePathMap( respathsPaths, respathsMap, overrideIncludes, overrideExcludes, "ResolvedRespathsMap" ); - ASSERT_EQ( resfileMap.size(), 1 ); // "/path1/loo/bar" + "[ * ]" - MapContainsPaths( resfilesPaths, resfileMap, "ResolvedResfileMap" ); - ValidatePathMap( resfilesPaths, resfileMap, defaultIncludes, defaultExcludes, "ResolvedResfileMap" ); + ASSERT_TRUE( resfileMap != nullptr ); + ASSERT_EQ( resfileMap->size(), 1 ); // "/path1/loo/bar" + "[ * ]" + MapContainsPaths( resfilesPaths, *resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( resfilesPaths, *resfileMap, defaultIncludes, defaultExcludes, "ResolvedResfileMap" ); ASSERT_EQ( combinedMap.size(), 2 ); // both MapContainsPaths( combinedPaths, combinedMap, "ResolvedCombinedMap" ); @@ -1454,16 +1460,17 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_OnlyExcludeTopLevelFilter std::vector overrideExcludes = { ".toplevelExclude", ".inlineExclude" }; const auto& respathsMap = namedSection.GetResolvedRespathsMap(); - const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto* resfileMap = namedSection.GetResolvedResfileMap(); const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); ASSERT_EQ( respathsMap.size(), 1 ); // "/path1/foo/bar" + "[ * ]" MapContainsPaths( respathsPaths, respathsMap, "ResolvedRespathsMap" ); ValidatePathMap( respathsPaths, respathsMap, defaultIncludes, defaultExcludes, "ResolvedRespathsMap" ); - ASSERT_EQ( resfileMap.size(), 1 ); // "/path1/loo/bar" + "[ *, .inlineInclude ] ![ .toplevelExclude .inlineExclude ]" - MapContainsPaths( resfilesPaths, resfileMap, "ResolvedResfileMap" ); - ValidatePathMap( resfilesPaths, resfileMap, overrideIncludes, overrideExcludes, "ResolvedResfileMap" ); + ASSERT_TRUE( resfileMap != nullptr ); + ASSERT_EQ( resfileMap->size(), 1 ); // "/path1/loo/bar" + "[ *, .inlineInclude ] ![ .toplevelExclude .inlineExclude ]" + MapContainsPaths( resfilesPaths, *resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( resfilesPaths, *resfileMap, overrideIncludes, overrideExcludes, "ResolvedResfileMap" ); ASSERT_EQ( combinedMap.size(), 2 ); // both MapContainsPaths( combinedPaths, combinedMap, "ResolvedCombinedMap" ); @@ -1516,15 +1523,16 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_CombinedMapWithResfileOve const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); const auto& respathsMap = namedSection.GetResolvedRespathsMap(); - const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto* resfileMap = namedSection.GetResolvedResfileMap(); ASSERT_EQ( respathsMap.size(), 2 ); MapContainsPaths( allPaths, respathsMap, "ResolvedRespathsMap" ); ValidatePathMap( allPaths, respathsMap, defaultIncludes, allExcludes, "ResolvedRespathsMap" ); - ASSERT_EQ( resfileMap.size(), 2 ); - MapContainsPaths( allPaths, resfileMap, "ResolvedResfileMap" ); - ValidatePathMap( allPaths, resfileMap, overrideIncludes, allExcludes, "ResolvedResfileMap" ); + ASSERT_TRUE( resfileMap != nullptr ); + ASSERT_EQ( resfileMap->size(), 2 ); + MapContainsPaths( allPaths, *resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( allPaths, *resfileMap, overrideIncludes, allExcludes, "ResolvedResfileMap" ); ASSERT_EQ( combinedMap.size(), 2 ); // Both, same count but now with overrides MapContainsPaths( allPaths, combinedMap, "ResolvedCombinedMap" ); @@ -1559,15 +1567,16 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_NoTopLevelFilterSameCombi const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); const auto& respathsMap = namedSection.GetResolvedRespathsMap(); - const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto* resfileMap = namedSection.GetResolvedResfileMap(); ASSERT_EQ( respathsMap.size(), 2 ); MapContainsPaths( allPaths, respathsMap, "ResolvedRespathsMap" ); ValidatePathMap( allPaths, respathsMap, defaultIncludes, allExcludes, "ResolvedRespathsMap" ); - ASSERT_EQ( resfileMap.size(), 2 ); - MapContainsPaths( allPaths, resfileMap, "ResolvedResfileMap" ); - ValidatePathMap( allPaths, resfileMap, overrideIncludes, allExcludes, "ResolvedResfileMap" ); + ASSERT_TRUE( resfileMap != nullptr ); + ASSERT_EQ( resfileMap->size(), 2 ); + MapContainsPaths( allPaths, *resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( allPaths, *resfileMap, overrideIncludes, allExcludes, "ResolvedResfileMap" ); ASSERT_EQ( combinedMap.size(), 2 ); // Both, same count but now with overrides MapContainsPaths( allPaths, combinedMap, "ResolvedCombinedMap" ); @@ -1611,15 +1620,16 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_NoTopLevelFilterSameCombi const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); const auto& respathsMap = namedSection.GetResolvedRespathsMap(); - const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto* resfileMap = namedSection.GetResolvedResfileMap(); ASSERT_EQ( respathsMap.size(), 2 ); MapContainsPaths( allPaths, respathsMap, "ResolvedRespathsMap" ); ValidatePathMap( allPaths, respathsMap, overrideIncludes, allExcludes, "ResolvedRespathsMap" ); - ASSERT_EQ( resfileMap.size(), 2 ); - MapContainsPaths( allPaths, resfileMap, "ResolvedResfileMap" ); - ValidatePathMap( allPaths, resfileMap, defaultIncludes, allExcludes, "ResolvedResfileMap" ); + ASSERT_TRUE( resfileMap != nullptr ); + ASSERT_EQ( resfileMap->size(), 2 ); + MapContainsPaths( allPaths, *resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( allPaths, *resfileMap, defaultIncludes, allExcludes, "ResolvedResfileMap" ); ASSERT_EQ( combinedMap.size(), 2 ); // Both, same count but now with overrides MapContainsPaths( allPaths, combinedMap, "ResolvedCombinedMap" ); @@ -1639,9 +1649,11 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_NoTopLevelFilterSameCombi MapContainsPaths( allPaths, respathsAgainMap, "ResolvedRespathsMap-Again" ); ValidatePathMap( allPaths, respathsAgainMap, overrideIncludes, allExcludes, "ResolvedRespathsMap-Again" ); - const auto& resfileAgainMap = namedSection.GetResolvedResfileMap(); - MapContainsPaths( allPaths, resfileAgainMap, "ResolvedResfileMap-Again" ); - ValidatePathMap( allPaths, resfileAgainMap, defaultIncludes, allExcludes, "ResolvedResfileMap-Again" ); + const auto* resfileAgainMap = namedSection.GetResolvedResfileMap(); + ASSERT_TRUE( resfileAgainMap != nullptr ); + ASSERT_EQ( resfileAgainMap->size(), 2 ); + MapContainsPaths( allPaths, *resfileAgainMap, "ResolvedResfileMap-Again" ); + ValidatePathMap( allPaths, *resfileAgainMap, defaultIncludes, allExcludes, "ResolvedResfileMap-Again" ); } TEST_F( ResourceToolsTest, FilterNamedSection_Validate_OnlyExcludeFilterSameCombinedMapWithResfileOverrideIsAllowed ) @@ -1667,15 +1679,16 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_OnlyExcludeFilterSameComb const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); const auto& respathsMap = namedSection.GetResolvedRespathsMap(); - const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto* resfileMap = namedSection.GetResolvedResfileMap(); ASSERT_EQ( respathsMap.size(), 2 ); MapContainsPaths( allPaths, respathsMap, "ResolvedRespathsMap" ); ValidatePathMap( allPaths, respathsMap, defaultIncludes, allExcludes, "ResolvedRespathsMap" ); - ASSERT_EQ( resfileMap.size(), 2 ); - MapContainsPaths( allPaths, resfileMap, "ResolvedResfileMap" ); - ValidatePathMap( allPaths, resfileMap, overrideIncludes, allExcludes, "ResolvedResfileMap" ); + ASSERT_TRUE( resfileMap != nullptr ); + ASSERT_EQ( resfileMap->size(), 2 ); + MapContainsPaths( allPaths, *resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( allPaths, *resfileMap, overrideIncludes, allExcludes, "ResolvedResfileMap" ); ASSERT_EQ( combinedMap.size(), 2 ); // Both, same count but now with overrides MapContainsPaths( allPaths, combinedMap, "ResolvedCombinedMap" ); @@ -1719,15 +1732,16 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_OnlyExcludeFilterSameComb const auto& combinedMap = namedSection.GetCombinedResolvedPathMap(); const auto& respathsMap = namedSection.GetResolvedRespathsMap(); - const auto& resfileMap = namedSection.GetResolvedResfileMap(); + const auto* resfileMap = namedSection.GetResolvedResfileMap(); ASSERT_EQ( respathsMap.size(), 2 ); MapContainsPaths( allPaths, respathsMap, "ResolvedRespathsMap" ); ValidatePathMap( allPaths, respathsMap, overrideIncludes, allExcludes, "ResolvedRespathsMap" ); - ASSERT_EQ( resfileMap.size(), 2 ); - MapContainsPaths( allPaths, resfileMap, "ResolvedResfileMap" ); - ValidatePathMap( allPaths, resfileMap, defaultIncludes, allExcludes, "ResolvedResfileMap" ); + ASSERT_TRUE( resfileMap != nullptr ); + ASSERT_EQ( resfileMap->size(), 2 ); + MapContainsPaths( allPaths, *resfileMap, "ResolvedResfileMap" ); + ValidatePathMap( allPaths, *resfileMap, defaultIncludes, allExcludes, "ResolvedResfileMap" ); ASSERT_EQ( combinedMap.size(), 2 ); // Both, same count but now with overrides MapContainsPaths( allPaths, combinedMap, "ResolvedCombinedMap" ); @@ -1747,9 +1761,11 @@ TEST_F( ResourceToolsTest, FilterNamedSection_Validate_OnlyExcludeFilterSameComb MapContainsPaths( allPaths, respathsAgainMap, "ResolvedRespathsMap-Again" ); ValidatePathMap( allPaths, respathsAgainMap, overrideIncludes, allExcludes, "ResolvedRespathsMap-Again" ); - const auto& resfileAgainMap = namedSection.GetResolvedResfileMap(); - MapContainsPaths( allPaths, resfileAgainMap, "ResolvedResfileMap-Again" ); - ValidatePathMap( allPaths, resfileAgainMap, defaultIncludes, allExcludes, "ResolvedResfileMap-Again" ); + const auto* resfileAgainMap = namedSection.GetResolvedResfileMap(); + ASSERT_TRUE( resfileAgainMap != nullptr ); + ASSERT_EQ( resfileAgainMap->size(), 2 ); + MapContainsPaths( allPaths, *resfileAgainMap, "ResolvedResfileMap-Again" ); + ValidatePathMap( allPaths, *resfileAgainMap, defaultIncludes, allExcludes, "ResolvedResfileMap-Again" ); } // ------------------------------------------ diff --git a/tools/include/FilterNamedSection.h b/tools/include/FilterNamedSection.h index 13b721b..42201d5 100644 --- a/tools/include/FilterNamedSection.h +++ b/tools/include/FilterNamedSection.h @@ -42,8 +42,8 @@ class FilterNamedSection // Return the resolved path map from the "respaths" attribute. Only used in tests to verify correctness of data. const std::map& GetResolvedRespathsMap() const; - // Return the resolved path map from the optional "resfile" attribute. Only used in tests to verify correctness of data. - const std::map& GetResolvedResfileMap() const; + // Return a pointer to the resolved path map from the optional "resfile" attribute. Only used in tests to verify correctness of data. + const std::map* GetResolvedResfileMap() const; private: // The name of this section (e.g: [SomeSectionName]) from the .ini file. diff --git a/tools/src/FilterNamedSection.cpp b/tools/src/FilterNamedSection.cpp index 4a3611b..4ac485d 100644 --- a/tools/src/FilterNamedSection.cpp +++ b/tools/src/FilterNamedSection.cpp @@ -114,22 +114,21 @@ const std::map& FilterNamedSection::GetResolv // Description: // Gets the resolved path map from the optional "resfile" attribute only. // Return Value: -// Map of resolved paths to their associated FilterResourceFilter objects. +// Pointer to a map of resolved paths to their associated FilterResourceFilter objects, +// or nullptr if no resfile is present. This is a pointer (and not a reference) because of the optional m_resfile member. // Note: // Users of this class should use the GetCombinedResolvedPathMap() // function instead of this one to get the "full combined" result. // This function is exposed only to enable tests to verify correctness of data. // ------------------------------------------------------------- -const std::map& FilterNamedSection::GetResolvedResfileMap() const +const std::map* FilterNamedSection::GetResolvedResfileMap() const { if( m_resfile ) { - return m_resfile->GetResolvedPathMap(); + return &m_resfile->GetResolvedPathMap(); } - // Return empty map if no resfile present - static const std::map emptyResfileMap; - return emptyResfileMap; + return nullptr; } } From 93b27fa6b13156301db912b1006854effc7465c8 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 11 Feb 2026 16:47:03 +0000 Subject: [PATCH 107/124] Optimize dealing with CLI filter files (as per code review suggestion) --- cli/src/CreateResourceGroupCliOperation.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/cli/src/CreateResourceGroupCliOperation.cpp b/cli/src/CreateResourceGroupCliOperation.cpp index 7bdd317..65928f4 100644 --- a/cli/src/CreateResourceGroupCliOperation.cpp +++ b/cli/src/CreateResourceGroupCliOperation.cpp @@ -86,7 +86,7 @@ bool CreateResourceGroupCliOperation::Execute( std::string& returnErrorMessage ) exportParams.outputDocumentVersion = createResourceGroupParams.outputDocumentVersion; - if( m_argumentParser->is_used( m_createResourceGroupIniFilterFilesArgumentId )) + if( m_argumentParser->is_used( m_createResourceGroupIniFilterFilesArgumentId ) ) { std::vector filterIniFilePaths; auto iniFileStringVector = m_argumentParser->get>( m_createResourceGroupIniFilterFilesArgumentId ); @@ -95,13 +95,9 @@ bool CreateResourceGroupCliOperation::Execute( std::string& returnErrorMessage ) { if( !iniPathStr.empty() ) { - filterIniFilePaths.push_back( iniPathStr ); + createResourceGroupParams.resourceFilterIniFiles.emplace_back( iniPathStr ); } } - if ( !filterIniFilePaths.empty() ) - { - createResourceGroupParams.resourceFilterIniFiles = filterIniFilePaths; - } } PrintStartBanner( createResourceGroupParams, exportParams ); From 4ed3794e36d503b0e89a599dc9866b34a3450bc5 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 11 Feb 2026 17:37:52 +0000 Subject: [PATCH 108/124] Change how NamedSection is populated and its getter function. --- tools/include/FilterNamedSection.h | 5 +- tools/src/FilterNamedSection.cpp | 86 +++++++++++++++++------------- 2 files changed, 53 insertions(+), 38 deletions(-) diff --git a/tools/include/FilterNamedSection.h b/tools/include/FilterNamedSection.h index 42201d5..c063040 100644 --- a/tools/include/FilterNamedSection.h +++ b/tools/include/FilterNamedSection.h @@ -37,7 +37,7 @@ class FilterNamedSection // Return the combined resolved path map from both the "respaths" and optional "resfile" attributes. // This is the main function to use to get the final resolved paths and their associated filters for this section. - const std::map& GetCombinedResolvedPathMap(); + const std::map& GetCombinedResolvedPathMap() const; // Return the resolved path map from the "respaths" attribute. Only used in tests to verify correctness of data. const std::map& GetResolvedRespathsMap() const; @@ -46,6 +46,9 @@ class FilterNamedSection const std::map* GetResolvedResfileMap() const; private: + // Populate the combined resolved path map from both "respaths" and optional "resfile" attributes. + void PopulateCombinedResolvedPathMap(); + // The name of this section (e.g: [SomeSectionName]) from the .ini file. std::string m_sectionName; diff --git a/tools/src/FilterNamedSection.cpp b/tools/src/FilterNamedSection.cpp index 4ac485d..67a7fd7 100644 --- a/tools/src/FilterNamedSection.cpp +++ b/tools/src/FilterNamedSection.cpp @@ -18,6 +18,9 @@ namespace ResourceTools // resfile - the optional "resfile" attribute of this section (e.g: "prefix:/pathToSomeFile.txt") // parentPrefixMap - the FilterPrefixMap from the [DEFAULT] section of the .ini file, // used to resolve prefixes in "respaths" and "resfile" attributes to actual paths. +// Note: +// The combined map from both the "respaths" and "resfile" attributes +// is populated on construction of this object. // ------------------------------------------------------------- FilterNamedSection::FilterNamedSection( const std::string& sectionName, const std::string& rawFilter, @@ -33,6 +36,9 @@ FilterNamedSection::FilterNamedSection( const std::string& sectionName, { throw std::invalid_argument( "Respaths attribute is empty for section: " + m_sectionName ); } + + // Populate the combined resolved path map from both "respaths" and optional "resfile" attributes. + PopulateCombinedResolvedPathMap(); } // ------------------------------------------------------------- @@ -53,45 +59,9 @@ const std::string& FilterNamedSection::GetSectionName() const // and their associated filters from within this section. // Return Value: // Map of resolved paths to their associated FilterResourceFilter objects. -// Note: -// The combined map from both the "respaths" and "resfile" attributes -// is populated during the first call to this function (and then cached). // ------------------------------------------------------------- -const std::map& FilterNamedSection::GetCombinedResolvedPathMap() +const std::map& FilterNamedSection::GetCombinedResolvedPathMap() const { - // Only populate the Combined map if not already done so. - if( m_resolvedCombinedPathMap.empty() ) - { - // Populate the combined map with "respaths" attribute entries first (as they are non-optional) - for( const auto& kv : m_respaths.GetResolvedPathMap() ) - { - m_resolvedCombinedPathMap.insert_or_assign( kv.first, kv.second ); - } - - // Add the "resfile" attribute entries to the combined map (in case there are any). - // Make sure to combine any filters if the same key already exists from the "respaths" attribute. - if( m_resfile ) - { - // Allow "resfile" to contain multiple entries (future proofing it, not currently utilized as such in existing .ini filter files) - for( const auto& kv : m_resfile->GetResolvedPathMap() ) - { - // Combine filters of both if same key already exists (using the raw filter strings). - // Else just add the "resfile" attribue entry to the combined map as is. - auto it = m_resolvedCombinedPathMap.find( kv.first ); - if( it != m_resolvedCombinedPathMap.end() ) - { - std::string combinedRawFilter = it->second.GetRawFilter() + " " + kv.second.GetRawFilter(); - FilterResourceFilter combinedFilter( combinedRawFilter ); - m_resolvedCombinedPathMap.insert_or_assign( kv.first, combinedFilter ); - } - else - { - m_resolvedCombinedPathMap.insert_or_assign( kv.first, kv.second ); - } - } - } - } - return m_resolvedCombinedPathMap; } @@ -131,4 +101,46 @@ const std::map* FilterNamedSection::GetResolv return nullptr; } +// ------------------------------------------------------------- +// Description: +// Populates the combined resolved path map from both "respaths" and optional "resfile" attributes. +// Return Value: +// None (void) +// ------------------------------------------------------------- +void FilterNamedSection::PopulateCombinedResolvedPathMap() +{ + // Only populate the Combined map if not already done so. + if( m_resolvedCombinedPathMap.empty() ) + { + // Populate the combined map with "respaths" attribute entries first (as they are non-optional) + for( const auto& kv : m_respaths.GetResolvedPathMap() ) + { + m_resolvedCombinedPathMap.insert( {kv.first, kv.second } ); + } + + // Add the "resfile" attribute entries to the combined map (in case there are any). + // Make sure to combine any filters if the same key already exists from the "respaths" attribute. + if( m_resfile ) + { + // Allow "resfile" to contain multiple entries (future proofing it, not currently utilized as such in existing .ini filter files) + for( const auto& kv : m_resfile->GetResolvedPathMap() ) + { + // Combine filters of both if same key already exists (using the raw filter strings). + // Else just add the "resfile" attribue entry to the combined map as is. + auto it = m_resolvedCombinedPathMap.find( kv.first ); + if( it != m_resolvedCombinedPathMap.end() ) + { + std::string combinedRawFilter = it->second.GetRawFilter() + " " + kv.second.GetRawFilter(); + FilterResourceFilter combinedFilter( combinedRawFilter ); + m_resolvedCombinedPathMap.insert_or_assign( kv.first, combinedFilter ); + } + else + { + m_resolvedCombinedPathMap.insert( {kv.first, kv.second } ); + } + } + } + } +} + } From 828ae1077e1301df9469768bbf21bad7d8b16157 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 11 Feb 2026 17:53:18 +0000 Subject: [PATCH 109/124] Change population of FilterResourceFile members and signature of getter function. --- tools/include/FilterResourceFile.h | 7 ++- tools/src/FilterResourceFile.cpp | 68 +++++++++++++++++------------- 2 files changed, 44 insertions(+), 31 deletions(-) diff --git a/tools/include/FilterResourceFile.h b/tools/include/FilterResourceFile.h index 9552378..ae89e93 100644 --- a/tools/include/FilterResourceFile.h +++ b/tools/include/FilterResourceFile.h @@ -24,15 +24,18 @@ class FilterResourceFile // Construct a FilterResourceFile object by parsing the supplied resource .ini file. explicit FilterResourceFile( const std::filesystem::path& iniFilePath ); - // Generate, cache and returns the fully resolved PathMaps for all named sections + // Returns the fully resolved PathMaps for all named sections // within this resource .ini file. // Key = "resolved path", Value = associated include/exclude filters - const std::map& GetIniFileResolvedPathMap(); + const std::map& GetIniFileResolvedPathMap() const; private: // Parses the resource .ini file and populates the m_defaultSection and m_namedSections members. void ParseIniFile( const std::filesystem::path& iniFilePath ); + // Populates the m_iniFileResolvedPathMap member by combining the resolved path maps from all named sections in this INI file. + void PopulateIniFileResolvedPathMap(); + // The parsed [DEFAULT] section of the resource .ini file FilterDefaultSection m_defaultSection; diff --git a/tools/src/FilterResourceFile.cpp b/tools/src/FilterResourceFile.cpp index f48f18c..85c2eca 100644 --- a/tools/src/FilterResourceFile.cpp +++ b/tools/src/FilterResourceFile.cpp @@ -18,44 +18,19 @@ namespace ResourceTools FilterResourceFile::FilterResourceFile( const std::filesystem::path& iniFilePath ) { ParseIniFile( iniFilePath ); + PopulateIniFileResolvedPathMap(); } // ------------------------------------------------------------- // Description: -// Generate (on first call to function), cache and returns the fully -// resolved PathMaps for all named sections within .ini file. +// Returns the fully resolved PathMaps for all named sections within this .ini file. // Return Value: -// Map of resolved paths to their associated filters for all named sections in the .ini file. +// Map of resolved paths to their associated filters: // - Key = "resolved path" // - Value = associated include/exclude filters // ------------------------------------------------------------- -const std::map& FilterResourceFile::GetIniFileResolvedPathMap() +const std::map& FilterResourceFile::GetIniFileResolvedPathMap() const { - if( m_iniFileResolvedPathMap.empty() ) - { - // Populate the full resolved path map from all named sections in this INI file - for( auto& namedSection : m_namedSections ) - { - auto& sectionPathMap = namedSection.GetCombinedResolvedPathMap(); - for( const auto& kv : sectionPathMap ) - { - // Combine filters if the same path already exists - auto it = m_iniFileResolvedPathMap.find( kv.first ); - if( it != m_iniFileResolvedPathMap.end() ) - { - // Combine the filters (using raw filter strings) - std::string combinedRawFilter = it->second.GetRawFilter() + " " + kv.second.GetRawFilter(); - FilterResourceFilter combinedFilter( combinedRawFilter ); - m_iniFileResolvedPathMap.insert_or_assign( kv.first, combinedFilter ); - } - else - { - m_iniFileResolvedPathMap.insert_or_assign( kv.first, kv.second ); - } - } - } - } - return m_iniFileResolvedPathMap; } @@ -115,4 +90,39 @@ void FilterResourceFile::ParseIniFile( const std::filesystem::path& iniFilePath } } +// ------------------------------------------------------------- +// Description: +// Populate the fully resolved PathMaps for all [namedSections] +// in this .ini file. +// Return Value: +// None (void). +// ------------------------------------------------------------- +void FilterResourceFile::PopulateIniFileResolvedPathMap() +{ + if( m_iniFileResolvedPathMap.empty() ) + { + // Populate the full resolved path map from all named sections in this INI file + for( auto& namedSection : m_namedSections ) + { + auto& sectionPathMap = namedSection.GetCombinedResolvedPathMap(); + for( const auto& kv : sectionPathMap ) + { + // Combine filters if the same path already exists + auto it = m_iniFileResolvedPathMap.find( kv.first ); + if( it != m_iniFileResolvedPathMap.end() ) + { + // Combine the filters (using raw filter strings) + std::string combinedRawFilter = it->second.GetRawFilter() + " " + kv.second.GetRawFilter(); + FilterResourceFilter combinedFilter( combinedRawFilter ); + m_iniFileResolvedPathMap.insert_or_assign( kv.first, combinedFilter ); + } + else + { + m_iniFileResolvedPathMap.insert( { kv.first, kv.second } ); + } + } + } + } +} + } From 51ecead1f40deeb61e5bff9331166fa5da0fc80a Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 11 Feb 2026 18:05:05 +0000 Subject: [PATCH 110/124] Change initialization of ResourceFilter class and signature of GetFullResolvedPathMap() function. --- tools/include/ResourceFilter.h | 5 ++- tools/src/ResourceFilter.cpp | 70 +++++++++++++++++++--------------- 2 files changed, 44 insertions(+), 31 deletions(-) diff --git a/tools/include/ResourceFilter.h b/tools/include/ResourceFilter.h index 8e3c0af..06e45ca 100644 --- a/tools/include/ResourceFilter.h +++ b/tools/include/ResourceFilter.h @@ -36,13 +36,16 @@ class ResourceFilter // Returns the full resolved relative PathMaps from all resource .ini file(s). // Key = "resolved relative path", Value = FilterResourceFilter (include/exclude filters) - const std::map& GetFullResolvedPathMap(); + const std::map& GetFullResolvedPathMap() const; // Check if the inFilePath should be included or excluded based on // filtering rules from all the filter .ini file(s) bool FilePathMatchesIncludeFilterRules( const std::filesystem::path& inFilePath ); private: + // Populate the full resolved path map from all .ini file(s) in this ResourceFilter. + void PopulateFullResolvedPathMap(); + // Static helper function for wildcard matching path strings (supports "*" and "...") static bool WildcardMatch( std::string pattern, const std::string& checkStr ); diff --git a/tools/src/ResourceFilter.cpp b/tools/src/ResourceFilter.cpp index 362d4a8..c54a1c9 100644 --- a/tools/src/ResourceFilter.cpp +++ b/tools/src/ResourceFilter.cpp @@ -47,6 +47,10 @@ void ResourceFilter::Initialize( const std::vector& iniFi } } + // Now we can populate the full resolved path map from all .ini file(s) in + // this ResourceFilter (combining filters for any overlapping paths). + PopulateFullResolvedPathMap(); + m_initialized = true; } @@ -71,37 +75,9 @@ bool ResourceFilter::HasFilters() const // FilterResourceFile(s) // Key = "resolved relative path" // Value = FilterResourceFilter (with include/exclude filters) -// Note: -// The full resolved path map is generated and then cached on -// the first call to this function. // ------------------------------------------------------------- -const std::map& ResourceFilter::GetFullResolvedPathMap() +const std::map& ResourceFilter::GetFullResolvedPathMap() const { - if( m_fullResolvedPathMap.empty() ) - { - // Populate the full resolved path map from all Filter INI files - for( auto& iniFile : m_filterFiles ) - { - auto& iniFilePathMap = iniFile->GetIniFileResolvedPathMap(); - for( const auto& kv : iniFilePathMap ) - { - // Combine filters if the same path already exists - auto it = m_fullResolvedPathMap.find( kv.first ); - if( it != m_fullResolvedPathMap.end() ) - { - // Combine the filters (using raw filter strings) - std::string combinedRawFilter = it->second.GetRawFilter() + " " + kv.second.GetRawFilter(); - FilterResourceFilter combinedFilter( combinedRawFilter ); - m_fullResolvedPathMap.insert_or_assign( kv.first, combinedFilter ); - } - else - { - m_fullResolvedPathMap.insert_or_assign( kv.first, kv.second ); - } - } - } - } - return m_fullResolvedPathMap; } @@ -234,6 +210,40 @@ bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::p return false; } +// ------------------------------------------------------------- +// Description: +// Populates the full resolved path map from all .ini file(s) in this ResourceFilter. +// Return Value: +// None (void). +// ------------------------------------------------------------- +void ResourceFilter::PopulateFullResolvedPathMap() +{ + if( m_fullResolvedPathMap.empty() ) + { + // Populate the full resolved path map from all Filter INI files + for( auto& iniFile : m_filterFiles ) + { + auto& iniFilePathMap = iniFile->GetIniFileResolvedPathMap(); + for( const auto& kv : iniFilePathMap ) + { + // Combine filters if the same path already exists + auto it = m_fullResolvedPathMap.find( kv.first ); + if( it != m_fullResolvedPathMap.end() ) + { + // Combine the filters (using raw filter strings) + std::string combinedRawFilter = it->second.GetRawFilter() + " " + kv.second.GetRawFilter(); + FilterResourceFilter combinedFilter( combinedRawFilter ); + m_fullResolvedPathMap.insert_or_assign( kv.first, combinedFilter ); + } + else + { + m_fullResolvedPathMap.insert( { kv.first, kv.second } ); + } + } + } + } +} + // ------------------------------------------------------------- // Description: // Static helper function for wildcard matching path strings. @@ -254,7 +264,7 @@ bool ResourceFilter::WildcardMatch( std::string pattern, const std::string& chec size_t pos; while( ( pos = pattern.find( "..." ) ) != std::string::npos ) { - pattern.replace( pos, 3, std::string(1, RECURSIVE_FOLDER_ELLIPSES_WILDCARD) ); + pattern.replace( pos, 3, std::string( 1, RECURSIVE_FOLDER_ELLIPSES_WILDCARD ) ); } // Escape special characters and deal with wildcards ("*" and "..." i.e. RECURSIVE_FOLDER_ELLIPSES_WILDCARD) From c294a8e7eaa45407ab499b92d481e2ebf10260c0 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 12 Feb 2026 11:13:37 +0000 Subject: [PATCH 111/124] Stop using absolute paths for comparison, using relative ones instead. - Change the FilePathMatchesIncludeFilterRules() function to use relative paths for comparison on the incoming file vs the resolved path from the filter .ini file. - Make sure that any path sent into the function or resolved from a .ini file are also force converted to their relative version, (from std::filesystem::current_path) --- tools/src/ResourceFilter.cpp | 42 ++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/tools/src/ResourceFilter.cpp b/tools/src/ResourceFilter.cpp index c54a1c9..203fed5 100644 --- a/tools/src/ResourceFilter.cpp +++ b/tools/src/ResourceFilter.cpp @@ -94,9 +94,9 @@ const std::map& ResourceFilter::GetFullResolv // ------------------------------------------------------------- bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::path& inFilePath ) { - // Make sure we work with the absolute path representation of the input file - std::filesystem::path inFilePathAbs = std::filesystem::absolute( inFilePath ); - std::string inFileNormalAbsPathStr = NormalizePath( inFilePathAbs.generic_string() ); + // Make sure we work with relative paths (no matter if the inFilePath is relative or absolute) + std::filesystem::path inFilePathRel = std::filesystem::relative( inFilePath, std::filesystem::current_path() ); + std::string inFileNormalRelPathStr = NormalizePath( inFilePathRel.generic_string() ); // Priority: lower is higher priority: // -1 = exact match on filename (or folder) @@ -109,12 +109,12 @@ bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::p const auto& resolvedPathMap = GetFullResolvedPathMap(); for( const auto& [resolvedRelativePathStr, filter] : resolvedPathMap ) { - // Use normalized absolute paths for comparison + // Use normalized relative paths for comparison (make sure to convert resolved path to relative, in case there was absolute path in an .ini file) std::filesystem::path resolvedRelativePath( resolvedRelativePathStr ); - std::filesystem::path resolvedPathAbs = std::filesystem::absolute( resolvedRelativePath ); - std::string resolvedNormalAbsPathStr = NormalizePath( resolvedPathAbs.generic_string() ); + std::filesystem::path resolvedPathRel = std::filesystem::relative( resolvedRelativePath, std::filesystem::current_path() ); + std::string resolvedNormalRelPathStr = NormalizePath( resolvedPathRel.generic_string() ); - if( resolvedNormalAbsPathStr == inFileNormalAbsPathStr ) + if( resolvedNormalRelPathStr == inFileNormalRelPathStr ) { // If there is an exact match on the full filename path, this means highest priority // and the file path should be considered an "include". This is even though resolvedPath @@ -124,36 +124,32 @@ bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::p continue; } - // Make sure the resolvedNormalAbsPathStr contains the "..." (recursive folder wildcard) if the - // original resolvedRelativePathStr had it. + // Make sure the resolvedNormalRelPathStr contains the "..." (recursive folder wildcard) + // if the original resolvedRelativePathStr had it. // Only check the end of the string for any combination of ["/...", "...", ".../"]. if( resolvedRelativePathStr.find( "...", resolvedRelativePathStr.size() - 4 ) != std::string::npos ) { - if( resolvedNormalAbsPathStr.find( "...", resolvedNormalAbsPathStr.size() - 4 ) == std::string::npos ) + if( resolvedNormalRelPathStr.find( "...", resolvedNormalRelPathStr.size() - 4 ) == std::string::npos ) { - if( resolvedNormalAbsPathStr.back() != '/' ) + if( resolvedNormalRelPathStr.back() != '/' ) { - resolvedNormalAbsPathStr += '/'; + resolvedNormalRelPathStr += '/'; } - resolvedNormalAbsPathStr += "..."; + resolvedNormalRelPathStr += "..."; } } - // Perform Wildcard matching on the normalized absolute paths - if( !WildcardMatch( resolvedNormalAbsPathStr, inFileNormalAbsPathStr ) ) + // Perform Wildcard matching on the normalized relative paths + if( !WildcardMatch( resolvedNormalRelPathStr, inFileNormalRelPathStr ) ) { // There was NO wildcard match on paths, ignore this resolvedRelativePath entry continue; } // There is a Wildcard match - determine the folder depth difference - // Reset the path variables to their Normalized absolute path components (in case they differ from non-Normalized version) - inFilePathAbs = std::filesystem::absolute( inFileNormalAbsPathStr ); - resolvedPathAbs = std::filesystem::absolute( resolvedNormalAbsPathStr ); - - auto inFileIt = inFilePathAbs.begin(); - auto resolvedIt = resolvedPathAbs.begin(); - while( inFileIt != inFilePathAbs.end() && resolvedIt != resolvedPathAbs.end() && *inFileIt == *resolvedIt ) + auto inFileIt = inFilePathRel.begin(); + auto resolvedIt = resolvedPathRel.begin(); + while( inFileIt != inFilePathRel.end() && resolvedIt != resolvedPathRel.end() && *inFileIt == *resolvedIt ) { ++inFileIt; ++resolvedIt; @@ -161,7 +157,7 @@ bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::p std::filesystem::path remainingPath; int folderDiffDepthPriority = -1; // Start at -1 (making first iteration priority 0 = same folder level) - while( inFileIt != inFilePathAbs.end() ) + while( inFileIt != inFilePathRel.end() ) { ++folderDiffDepthPriority; remainingPath /= *inFileIt; From 9e6c75fa2fa8190ecf5691d8d2f31a0a0a5c2bdd Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 12 Feb 2026 11:40:34 +0000 Subject: [PATCH 112/124] Reverting back to using absolute paths for comparison - Using relative paths for comparison can fail on Windows for a valid prefixmap entry of e.g. "rootRes:." where it has trouble interpreting e.g. a path of "./SomeFolder/*" --- tools/src/ResourceFilter.cpp | 38 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/tools/src/ResourceFilter.cpp b/tools/src/ResourceFilter.cpp index 203fed5..33f2101 100644 --- a/tools/src/ResourceFilter.cpp +++ b/tools/src/ResourceFilter.cpp @@ -94,9 +94,9 @@ const std::map& ResourceFilter::GetFullResolv // ------------------------------------------------------------- bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::path& inFilePath ) { - // Make sure we work with relative paths (no matter if the inFilePath is relative or absolute) - std::filesystem::path inFilePathRel = std::filesystem::relative( inFilePath, std::filesystem::current_path() ); - std::string inFileNormalRelPathStr = NormalizePath( inFilePathRel.generic_string() ); + // Make sure we work with the absolute path representation of the input file + std::filesystem::path inFilePathAbs = std::filesystem::absolute( inFilePath ); + std::string inFileNormalAbsPathStr = NormalizePath( inFilePathAbs.generic_string() ); // Priority: lower is higher priority: // -1 = exact match on filename (or folder) @@ -109,12 +109,12 @@ bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::p const auto& resolvedPathMap = GetFullResolvedPathMap(); for( const auto& [resolvedRelativePathStr, filter] : resolvedPathMap ) { - // Use normalized relative paths for comparison (make sure to convert resolved path to relative, in case there was absolute path in an .ini file) + // Use normalized absolute paths for comparison std::filesystem::path resolvedRelativePath( resolvedRelativePathStr ); - std::filesystem::path resolvedPathRel = std::filesystem::relative( resolvedRelativePath, std::filesystem::current_path() ); - std::string resolvedNormalRelPathStr = NormalizePath( resolvedPathRel.generic_string() ); + std::filesystem::path resolvedPathAbs = std::filesystem::absolute( resolvedRelativePath ); + std::string resolvedNormalAbsPathStr = NormalizePath( resolvedPathAbs.generic_string() ); - if( resolvedNormalRelPathStr == inFileNormalRelPathStr ) + if( resolvedNormalAbsPathStr == inFileNormalAbsPathStr ) { // If there is an exact match on the full filename path, this means highest priority // and the file path should be considered an "include". This is even though resolvedPath @@ -124,32 +124,32 @@ bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::p continue; } - // Make sure the resolvedNormalRelPathStr contains the "..." (recursive folder wildcard) - // if the original resolvedRelativePathStr had it. + // Make sure the resolvedNormalAbsPathStr contains the "..." (recursive folder wildcard) if the + // original resolvedRelativePathStr had it. // Only check the end of the string for any combination of ["/...", "...", ".../"]. if( resolvedRelativePathStr.find( "...", resolvedRelativePathStr.size() - 4 ) != std::string::npos ) { - if( resolvedNormalRelPathStr.find( "...", resolvedNormalRelPathStr.size() - 4 ) == std::string::npos ) + if( resolvedNormalAbsPathStr.find( "...", resolvedNormalAbsPathStr.size() - 4 ) == std::string::npos ) { - if( resolvedNormalRelPathStr.back() != '/' ) + if( resolvedNormalAbsPathStr.back() != '/' ) { - resolvedNormalRelPathStr += '/'; + resolvedNormalAbsPathStr += '/'; } - resolvedNormalRelPathStr += "..."; + resolvedNormalAbsPathStr += "..."; } } - // Perform Wildcard matching on the normalized relative paths - if( !WildcardMatch( resolvedNormalRelPathStr, inFileNormalRelPathStr ) ) + // Perform Wildcard matching on the normalized absolute paths + if( !WildcardMatch( resolvedNormalAbsPathStr, inFileNormalAbsPathStr ) ) { // There was NO wildcard match on paths, ignore this resolvedRelativePath entry continue; } // There is a Wildcard match - determine the folder depth difference - auto inFileIt = inFilePathRel.begin(); - auto resolvedIt = resolvedPathRel.begin(); - while( inFileIt != inFilePathRel.end() && resolvedIt != resolvedPathRel.end() && *inFileIt == *resolvedIt ) + auto inFileIt = inFilePathAbs.begin(); + auto resolvedIt = resolvedPathAbs.begin(); + while( inFileIt != inFilePathAbs.end() && resolvedIt != resolvedPathAbs.end() && *inFileIt == *resolvedIt ) { ++inFileIt; ++resolvedIt; @@ -157,7 +157,7 @@ bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::p std::filesystem::path remainingPath; int folderDiffDepthPriority = -1; // Start at -1 (making first iteration priority 0 = same folder level) - while( inFileIt != inFilePathRel.end() ) + while( inFileIt != inFilePathAbs.end() ) { ++folderDiffDepthPriority; remainingPath /= *inFileIt; From 7254135ab7a894daa3f10537bedfe0f1de8b4861 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 12 Feb 2026 12:01:46 +0000 Subject: [PATCH 113/124] Fully reverting back to original absolute paths for comparison - Make sure to reset inFilePathAbs and resolvedPathAbs path variables to their normalized versions --- tools/src/ResourceFilter.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/src/ResourceFilter.cpp b/tools/src/ResourceFilter.cpp index 33f2101..c54a1c9 100644 --- a/tools/src/ResourceFilter.cpp +++ b/tools/src/ResourceFilter.cpp @@ -147,6 +147,10 @@ bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::p } // There is a Wildcard match - determine the folder depth difference + // Reset the path variables to their Normalized absolute path components (in case they differ from non-Normalized version) + inFilePathAbs = std::filesystem::absolute( inFileNormalAbsPathStr ); + resolvedPathAbs = std::filesystem::absolute( resolvedNormalAbsPathStr ); + auto inFileIt = inFilePathAbs.begin(); auto resolvedIt = resolvedPathAbs.begin(); while( inFileIt != inFilePathAbs.end() && resolvedIt != resolvedPathAbs.end() && *inFileIt == *resolvedIt ) From 6e6ee174d6c6f8d4accdd15510b246cc197c4872 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Thu, 12 Feb 2026 15:22:02 +0000 Subject: [PATCH 114/124] Adding CLI filter tests for old-style document version (0.0.0) - This should work for tests running on Windows ("gold" file comparison) - Hope is that the macOS "gold" files will work as well (else re-run and generate those on a macOS machine and submit those) --- tests/src/ResourcesCliTest.cpp | 156 ++++++++++++++++-- ...ingFilter_validComplexExample1_Windows.txt | 10 ++ ...UsingFilter_validComplexExample1_macOS.txt | 10 ++ ..._validSimpleAndComplexExample1_Windows.txt | 12 ++ ...er_validSimpleAndComplexExample1_macOS.txt | 12 ++ ...singFilter_validSimpleExample1_Windows.txt | 2 + ..._UsingFilter_validSimpleExample1_macOS.txt | 2 + 7 files changed, 189 insertions(+), 15 deletions(-) create mode 100644 tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_Windows.txt create mode 100644 tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_macOS.txt create mode 100644 tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_Windows.txt create mode 100644 tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_macOS.txt create mode 100644 tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_Windows.txt create mode 100644 tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_macOS.txt diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index d9cdc58..b25f328 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -288,7 +288,7 @@ TEST_F( ResourcesCliTest, CreateResourceGroupFromDirectoryOldDocumentFormatWithP //--------------------------------------- -TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) +TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1_YamlOutput ) { // Setup test parameters std::string output; @@ -302,17 +302,15 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) RemoveFiles( { outputFilePath } ); arguments.push_back( "create-group" ); - arguments.push_back( inputDirectoryPath.lexically_normal().string() ); - arguments.push_back( "--verbosity-level" ); arguments.push_back( "3" ); - arguments.push_back( "--filter-file" ); arguments.push_back( filterIniFilePath.lexically_normal().string() ); - arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); + arguments.push_back( "--document-version" ); + arguments.push_back( "0.1.0" ); // This is default YAML document version (setting it explicitly for clarity in test) int res = RunCli( arguments, &output, &errorOutput, TEST_DATA_BASE_PATH ); std::cout << "--- RunCli() output: ---" << std::endl; @@ -332,7 +330,49 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1 ) EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ) << " Output file does not match expected gold file."; } -TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) +TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1_CsvTxtOutput ) +{ + // Setup test parameters + std::string output; + std::string errorOutput; + std::vector arguments; + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_validSimpleExample1.txt" ); + std::filesystem::path filterIniFilePath = "ExampleIniFiles/validSimpleExample1.ini"; + + // Ensure any previous test output files are removed + RemoveFiles( { outputFilePath } ); + + arguments.push_back( "create-group" ); + arguments.push_back( inputDirectoryPath.lexically_normal().string() ); + arguments.push_back( "--verbosity-level" ); + arguments.push_back( "3" ); + arguments.push_back( "--filter-file" ); + arguments.push_back( filterIniFilePath.lexically_normal().string() ); + arguments.push_back( "--output-file" ); + arguments.push_back( outputFilePath.lexically_normal().string() ); + arguments.push_back( "--document-version" ); + arguments.push_back( "0.0.0" ); // This is the "old style" csv (txt) document version + + int res = RunCli( arguments, &output, &errorOutput, TEST_DATA_BASE_PATH ); + std::cout << "--- RunCli() output: ---" << std::endl; + std::cout << output << std::endl; + std::cout << "------------------------" << std::endl; + + ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; + + // Check expected outcome +#if _WIN64 + std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_Windows.txt" ); +#elif __APPLE__ + std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_macOS.txt" ); +#else +#error Unsupported platform +#endif + EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ) << " Output file does not match expected gold file."; +} + +TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1_YamlOutput ) { // Setup test parameters std::string output; @@ -346,17 +386,15 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) RemoveFiles( { outputFilePath } ); arguments.push_back( "create-group" ); - arguments.push_back( inputDirectoryPath.lexically_normal().string() ); - arguments.push_back( "--verbosity-level" ); arguments.push_back( "3" ); - arguments.push_back( "--filter-file" ); arguments.push_back( filterIniFilePath.lexically_normal().string() ); - arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); + arguments.push_back( "--document-version" ); + arguments.push_back( "0.1.0" ); // This is default YAML document version (setting it explicitly for clarity in test) int res = RunCli( arguments, &output, &errorOutput, TEST_DATA_BASE_PATH ); std::cout << "--- RunCli() output: ---" << std::endl; @@ -376,7 +414,49 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1 ) EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ) << " Output file does not match expected gold file."; } -TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 ) +TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1_CsvTxtOutput ) +{ + // Setup test parameters + std::string output; + std::string errorOutput; + std::vector arguments; + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_validComplexExample1.txt" ); + std::filesystem::path filterIniFilePath = "ExampleIniFiles/validComplexExample1.ini"; + + // Ensure any previous test output files are removed + RemoveFiles( { outputFilePath } ); + + arguments.push_back( "create-group" ); + arguments.push_back( inputDirectoryPath.lexically_normal().string() ); + arguments.push_back( "--verbosity-level" ); + arguments.push_back( "3" ); + arguments.push_back( "--filter-file" ); + arguments.push_back( filterIniFilePath.lexically_normal().string() ); + arguments.push_back( "--output-file" ); + arguments.push_back( outputFilePath.lexically_normal().string() ); + arguments.push_back( "--document-version" ); + arguments.push_back( "0.0.0" ); // This is the "old style" csv (txt) document version + + int res = RunCli( arguments, &output, &errorOutput, TEST_DATA_BASE_PATH ); + std::cout << "--- RunCli() output: ---" << std::endl; + std::cout << output << std::endl; + std::cout << "------------------------" << std::endl; + + ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; + + // Check expected outcome +#if _WIN64 + std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_Windows.txt" ); +#elif __APPLE__ + std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_macOS.txt" ); +#else +#error Unsupported platform +#endif + EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ) << " Output file does not match expected gold file."; +} + +TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1_YamlOutput ) { // Setup test parameters std::string output; @@ -393,20 +473,18 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 RemoveFiles( { outputFilePath } ); arguments.push_back( "create-group" ); - arguments.push_back( inputDirectoryPath.lexically_normal().string() ); - arguments.push_back( "--verbosity-level" ); arguments.push_back( "3" ); - for( auto filterFilePath : filterIniFilePaths ) { arguments.push_back( "--filter-file" ); arguments.push_back( filterFilePath.lexically_normal().string() ); } - arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); + arguments.push_back( "--document-version" ); + arguments.push_back( "0.1.0" ); // This is default YAML document version (setting it explicitly for clarity in test) int res = RunCli( arguments, &output, &errorOutput, TEST_DATA_BASE_PATH ); std::cout << "--- RunCli() output: ---" << std::endl; @@ -426,6 +504,54 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1 EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ) << " Output file does not match expected gold file."; } +TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1_CsvTxtOutput ) +{ + // Setup test parameters + std::string output; + std::string errorOutput; + std::vector arguments; + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_validSimpleAndComplexExample1.txt" ); + std::vector filterIniFilePaths = { + "ExampleIniFiles/validSimpleExample1.ini", + "ExampleIniFiles/validComplexExample1.ini" + }; + + // Ensure any previous test output files are removed + RemoveFiles( { outputFilePath } ); + + arguments.push_back( "create-group" ); + arguments.push_back( inputDirectoryPath.lexically_normal().string() ); + arguments.push_back( "--verbosity-level" ); + arguments.push_back( "3" ); + for( auto filterFilePath : filterIniFilePaths ) + { + arguments.push_back( "--filter-file" ); + arguments.push_back( filterFilePath.lexically_normal().string() ); + } + arguments.push_back( "--output-file" ); + arguments.push_back( outputFilePath.lexically_normal().string() ); + arguments.push_back( "--document-version" ); + arguments.push_back( "0.0.0" ); // This is the "old style" csv (txt) document version + + int res = RunCli( arguments, &output, &errorOutput, TEST_DATA_BASE_PATH ); + std::cout << "--- RunCli() output: ---" << std::endl; + std::cout << output << std::endl; + std::cout << "------------------------" << std::endl; + + ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; + + // Check expected outcome +#if _WIN64 + std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_Windows.txt" ); +#elif __APPLE__ + std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_macOS.txt" ); +#else +#error Unsupported platform +#endif + EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ) << " Output file does not match expected gold file."; +} + TEST_F( ResourcesCliTest, CreateGroup_ConfirmFailureParsingWronglyFormattedIniFile_UsingFilter_invalidMissingNamedSection_ini ) { // Test parameters: diff --git a/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_Windows.txt b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_Windows.txt new file mode 100644 index 0000000..afdda51 --- /dev/null +++ b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_Windows.txt @@ -0,0 +1,10 @@ +PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt,dc/dca8056adced9237_0fb2bed9d164ad014bcb060b95df7ba1,0fb2bed9d164ad014bcb060b95df7ba1,9425,3409,33206 +PatchWithInputChunk/NextBuildResources/testResource2.txt,82/8264e4640cf94b94_271c8036e4eae5515053e924a0a39e0f,271c8036e4eae5515053e924a0a39e0f,29,47,33206 +PatchWithInputChunk/NextBuildResources/videoCardCategories.yaml,02/02b2b2627ca28b62_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,33206 +PatchWithInputChunk/PatchResourceGroup_previousBuild_latestBuild.yaml,44/446d5f683db0d467_b7431ad6d859bfd67a4c3fae337b0b6e,b7431ad6d859bfd67a4c3fae337b0b6e,3902,765,33206 +PatchWithInputChunk/PreviousBuildResources/introMovie.txt,fd/fd2a47b3d5faa494_e9fadf6f2d386a0a0786bc863f20fa34,e9fadf6f2d386a0a0786bc863f20fa34,9091,3232,33206 +PatchWithInputChunk/PreviousBuildResources/introMoviePrefixed.txt,5d/5d2aadfa1344ac63_5e631fd37d3350e30095d1251b178f2c,5e631fd37d3350e30095d1251b178f2c,9117,3259,33206 +PatchWithInputChunk/PreviousBuildResources/introMovieSomewhatChanged.txt,60/6054e5e0cf24763e_e9fadf6f2d386a0a0786bc863f20fa34,e9fadf6f2d386a0a0786bc863f20fa34,9091,3232,33206 +PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml,fb/fbc1cb8895f6c396_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,33206 +PatchWithInputChunk/resFileIndexShort_build_next.txt,1f/1f23be4b8bf6d2ae_4d398902b75611f7ae19903e6b461514,4d398902b75611f7ae19903e6b461514,612,324,33206 +PatchWithInputChunk/resFileIndexShort_build_previous.txt,32/326b0449af10b716_849821e0c98e37de4edc5f7156cf5887,849821e0c98e37de4edc5f7156cf5887,611,299,33206 diff --git a/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_macOS.txt b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_macOS.txt new file mode 100644 index 0000000..d0147c9 --- /dev/null +++ b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_macOS.txt @@ -0,0 +1,10 @@ +PatchWithInputChunk/resFileIndexShort_build_next.txt,1f/1f23be4b8bf6d2ae_4d398902b75611f7ae19903e6b461514,4d398902b75611f7ae19903e6b461514,612,324,33188 +PatchWithInputChunk/NextBuildResources/testResource2.txt,82/8264e4640cf94b94_271c8036e4eae5515053e924a0a39e0f,271c8036e4eae5515053e924a0a39e0f,29,47,33188 +PatchWithInputChunk/NextBuildResources/videoCardCategories.yaml,02/02b2b2627ca28b62_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,33188 +PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt,dc/dca8056adced9237_0fb2bed9d164ad014bcb060b95df7ba1,0fb2bed9d164ad014bcb060b95df7ba1,9425,3409,33188 +PatchWithInputChunk/resFileIndexShort_build_previous.txt,32/326b0449af10b716_849821e0c98e37de4edc5f7156cf5887,849821e0c98e37de4edc5f7156cf5887,611,299,33188 +PatchWithInputChunk/PatchResourceGroup_previousBuild_latestBuild.yaml,44/446d5f683db0d467_b7431ad6d859bfd67a4c3fae337b0b6e,b7431ad6d859bfd67a4c3fae337b0b6e,3902,765,33188 +PatchWithInputChunk/PreviousBuildResources/introMovie.txt,fd/fd2a47b3d5faa494_e9fadf6f2d386a0a0786bc863f20fa34,e9fadf6f2d386a0a0786bc863f20fa34,9091,3232,33188 +PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml,fb/fbc1cb8895f6c396_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,33188 +PatchWithInputChunk/PreviousBuildResources/introMoviePrefixed.txt,5d/5d2aadfa1344ac63_5e631fd37d3350e30095d1251b178f2c,5e631fd37d3350e30095d1251b178f2c,9117,3259,33188 +PatchWithInputChunk/PreviousBuildResources/introMovieSomewhatChanged.txt,60/6054e5e0cf24763e_e9fadf6f2d386a0a0786bc863f20fa34,e9fadf6f2d386a0a0786bc863f20fa34,9091,3232,33188 diff --git a/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_Windows.txt b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_Windows.txt new file mode 100644 index 0000000..163658a --- /dev/null +++ b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_Windows.txt @@ -0,0 +1,12 @@ +PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt,dc/dca8056adced9237_0fb2bed9d164ad014bcb060b95df7ba1,0fb2bed9d164ad014bcb060b95df7ba1,9425,3409,33206 +PatchWithInputChunk/NextBuildResources/testResource2.txt,82/8264e4640cf94b94_271c8036e4eae5515053e924a0a39e0f,271c8036e4eae5515053e924a0a39e0f,29,47,33206 +PatchWithInputChunk/NextBuildResources/videoCardCategories.yaml,02/02b2b2627ca28b62_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,33206 +PatchWithInputChunk/PatchResourceGroup_previousBuild_latestBuild.yaml,44/446d5f683db0d467_b7431ad6d859bfd67a4c3fae337b0b6e,b7431ad6d859bfd67a4c3fae337b0b6e,3902,765,33206 +PatchWithInputChunk/PreviousBuildResources/introMovie.txt,fd/fd2a47b3d5faa494_e9fadf6f2d386a0a0786bc863f20fa34,e9fadf6f2d386a0a0786bc863f20fa34,9091,3232,33206 +PatchWithInputChunk/PreviousBuildResources/introMoviePrefixed.txt,5d/5d2aadfa1344ac63_5e631fd37d3350e30095d1251b178f2c,5e631fd37d3350e30095d1251b178f2c,9117,3259,33206 +PatchWithInputChunk/PreviousBuildResources/introMovieSomewhatChanged.txt,60/6054e5e0cf24763e_e9fadf6f2d386a0a0786bc863f20fa34,e9fadf6f2d386a0a0786bc863f20fa34,9091,3232,33206 +PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml,fb/fbc1cb8895f6c396_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,33206 +PatchWithInputChunk/resFileIndexShort_build_next.txt,1f/1f23be4b8bf6d2ae_4d398902b75611f7ae19903e6b461514,4d398902b75611f7ae19903e6b461514,612,324,33206 +PatchWithInputChunk/resFileIndexShort_build_previous.txt,32/326b0449af10b716_849821e0c98e37de4edc5f7156cf5887,849821e0c98e37de4edc5f7156cf5887,611,299,33206 +resourcesOnBranch/introMovie.txt,1c/1c6368ffb440041a_e9fadf6f2d386a0a0786bc863f20fa34,e9fadf6f2d386a0a0786bc863f20fa34,9091,3232,33206 +resourcesOnBranch/videoCardCategories.yaml,ff/ffe4c396c9c935d4_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,33206 diff --git a/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_macOS.txt b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_macOS.txt new file mode 100644 index 0000000..5a932be --- /dev/null +++ b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_macOS.txt @@ -0,0 +1,12 @@ +resourcesOnBranch/introMovie.txt,1c/1c6368ffb440041a_e9fadf6f2d386a0a0786bc863f20fa34,e9fadf6f2d386a0a0786bc863f20fa34,9091,3232,33188 +resourcesOnBranch/videoCardCategories.yaml,ff/ffe4c396c9c935d4_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,33188 +PatchWithInputChunk/resFileIndexShort_build_next.txt,1f/1f23be4b8bf6d2ae_4d398902b75611f7ae19903e6b461514,4d398902b75611f7ae19903e6b461514,612,324,33188 +PatchWithInputChunk/NextBuildResources/testResource2.txt,82/8264e4640cf94b94_271c8036e4eae5515053e924a0a39e0f,271c8036e4eae5515053e924a0a39e0f,29,47,33188 +PatchWithInputChunk/NextBuildResources/videoCardCategories.yaml,02/02b2b2627ca28b62_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,33188 +PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt,dc/dca8056adced9237_0fb2bed9d164ad014bcb060b95df7ba1,0fb2bed9d164ad014bcb060b95df7ba1,9425,3409,33188 +PatchWithInputChunk/resFileIndexShort_build_previous.txt,32/326b0449af10b716_849821e0c98e37de4edc5f7156cf5887,849821e0c98e37de4edc5f7156cf5887,611,299,33188 +PatchWithInputChunk/PatchResourceGroup_previousBuild_latestBuild.yaml,44/446d5f683db0d467_b7431ad6d859bfd67a4c3fae337b0b6e,b7431ad6d859bfd67a4c3fae337b0b6e,3902,765,33188 +PatchWithInputChunk/PreviousBuildResources/introMovie.txt,fd/fd2a47b3d5faa494_e9fadf6f2d386a0a0786bc863f20fa34,e9fadf6f2d386a0a0786bc863f20fa34,9091,3232,33188 +PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml,fb/fbc1cb8895f6c396_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,3188 +PatchWithInputChunk/PreviousBuildResources/introMoviePrefixed.txt,5d/5d2aadfa1344ac63_5e631fd37d3350e30095d1251b178f2c,5e631fd37d3350e30095d1251b178f2c,9117,3259,33188 +PatchWithInputChunk/PreviousBuildResources/introMovieSomewhatChanged.txt,60/6054e5e0cf24763e_e9fadf6f2d386a0a0786bc863f20fa34,e9fadf6f2d386a0a0786bc863f20fa34,9091,3232,33188 diff --git a/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_Windows.txt b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_Windows.txt new file mode 100644 index 0000000..acf7b4d --- /dev/null +++ b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_Windows.txt @@ -0,0 +1,2 @@ +resourcesOnBranch/introMovie.txt,1c/1c6368ffb440041a_e9fadf6f2d386a0a0786bc863f20fa34,e9fadf6f2d386a0a0786bc863f20fa34,9091,3232,33206 +resourcesOnBranch/videoCardCategories.yaml,ff/ffe4c396c9c935d4_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,33206 diff --git a/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_macOS.txt b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_macOS.txt new file mode 100644 index 0000000..41dc43e --- /dev/null +++ b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_macOS.txt @@ -0,0 +1,2 @@ +resourcesOnBranch/introMovie.txt,1c/1c6368ffb440041a_e9fadf6f2d386a0a0786bc863f20fa34,e9fadf6f2d386a0a0786bc863f20fa34,9091,3232,33188 +resourcesOnBranch/videoCardCategories.yaml,ff/ffe4c396c9c935d4_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,33188 From 18bfb5a810e9f97e595ec1f1fef81ae6e594fae3 Mon Sep 17 00:00:00 2001 From: hrafng <5185245+hrafn@users.noreply.github.com> Date: Thu, 12 Feb 2026 15:50:23 +0000 Subject: [PATCH 115/124] Update macOS gold comparison files for create-group with txt output --- ...eGroup_UsingFilter_validComplexExample1_macOS.txt | 8 ++++---- ...ingFilter_validSimpleAndComplexExample1_macOS.txt | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_macOS.txt b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_macOS.txt index d0147c9..607c6f7 100644 --- a/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_macOS.txt +++ b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validComplexExample1_macOS.txt @@ -1,10 +1,10 @@ -PatchWithInputChunk/resFileIndexShort_build_next.txt,1f/1f23be4b8bf6d2ae_4d398902b75611f7ae19903e6b461514,4d398902b75611f7ae19903e6b461514,612,324,33188 +PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt,dc/dca8056adced9237_0fb2bed9d164ad014bcb060b95df7ba1,0fb2bed9d164ad014bcb060b95df7ba1,9425,3409,33188 PatchWithInputChunk/NextBuildResources/testResource2.txt,82/8264e4640cf94b94_271c8036e4eae5515053e924a0a39e0f,271c8036e4eae5515053e924a0a39e0f,29,47,33188 PatchWithInputChunk/NextBuildResources/videoCardCategories.yaml,02/02b2b2627ca28b62_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,33188 -PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt,dc/dca8056adced9237_0fb2bed9d164ad014bcb060b95df7ba1,0fb2bed9d164ad014bcb060b95df7ba1,9425,3409,33188 -PatchWithInputChunk/resFileIndexShort_build_previous.txt,32/326b0449af10b716_849821e0c98e37de4edc5f7156cf5887,849821e0c98e37de4edc5f7156cf5887,611,299,33188 PatchWithInputChunk/PatchResourceGroup_previousBuild_latestBuild.yaml,44/446d5f683db0d467_b7431ad6d859bfd67a4c3fae337b0b6e,b7431ad6d859bfd67a4c3fae337b0b6e,3902,765,33188 PatchWithInputChunk/PreviousBuildResources/introMovie.txt,fd/fd2a47b3d5faa494_e9fadf6f2d386a0a0786bc863f20fa34,e9fadf6f2d386a0a0786bc863f20fa34,9091,3232,33188 -PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml,fb/fbc1cb8895f6c396_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,33188 PatchWithInputChunk/PreviousBuildResources/introMoviePrefixed.txt,5d/5d2aadfa1344ac63_5e631fd37d3350e30095d1251b178f2c,5e631fd37d3350e30095d1251b178f2c,9117,3259,33188 PatchWithInputChunk/PreviousBuildResources/introMovieSomewhatChanged.txt,60/6054e5e0cf24763e_e9fadf6f2d386a0a0786bc863f20fa34,e9fadf6f2d386a0a0786bc863f20fa34,9091,3232,33188 +PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml,fb/fbc1cb8895f6c396_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,33188 +PatchWithInputChunk/resFileIndexShort_build_next.txt,1f/1f23be4b8bf6d2ae_4d398902b75611f7ae19903e6b461514,4d398902b75611f7ae19903e6b461514,612,324,33188 +PatchWithInputChunk/resFileIndexShort_build_previous.txt,32/326b0449af10b716_849821e0c98e37de4edc5f7156cf5887,849821e0c98e37de4edc5f7156cf5887,611,299,33188 diff --git a/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_macOS.txt b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_macOS.txt index 5a932be..64c0010 100644 --- a/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_macOS.txt +++ b/tests/testData/ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleAndComplexExample1_macOS.txt @@ -1,12 +1,12 @@ -resourcesOnBranch/introMovie.txt,1c/1c6368ffb440041a_e9fadf6f2d386a0a0786bc863f20fa34,e9fadf6f2d386a0a0786bc863f20fa34,9091,3232,33188 -resourcesOnBranch/videoCardCategories.yaml,ff/ffe4c396c9c935d4_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,33188 -PatchWithInputChunk/resFileIndexShort_build_next.txt,1f/1f23be4b8bf6d2ae_4d398902b75611f7ae19903e6b461514,4d398902b75611f7ae19903e6b461514,612,324,33188 +PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt,dc/dca8056adced9237_0fb2bed9d164ad014bcb060b95df7ba1,0fb2bed9d164ad014bcb060b95df7ba1,9425,3409,33188 PatchWithInputChunk/NextBuildResources/testResource2.txt,82/8264e4640cf94b94_271c8036e4eae5515053e924a0a39e0f,271c8036e4eae5515053e924a0a39e0f,29,47,33188 PatchWithInputChunk/NextBuildResources/videoCardCategories.yaml,02/02b2b2627ca28b62_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,33188 -PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt,dc/dca8056adced9237_0fb2bed9d164ad014bcb060b95df7ba1,0fb2bed9d164ad014bcb060b95df7ba1,9425,3409,33188 -PatchWithInputChunk/resFileIndexShort_build_previous.txt,32/326b0449af10b716_849821e0c98e37de4edc5f7156cf5887,849821e0c98e37de4edc5f7156cf5887,611,299,33188 PatchWithInputChunk/PatchResourceGroup_previousBuild_latestBuild.yaml,44/446d5f683db0d467_b7431ad6d859bfd67a4c3fae337b0b6e,b7431ad6d859bfd67a4c3fae337b0b6e,3902,765,33188 PatchWithInputChunk/PreviousBuildResources/introMovie.txt,fd/fd2a47b3d5faa494_e9fadf6f2d386a0a0786bc863f20fa34,e9fadf6f2d386a0a0786bc863f20fa34,9091,3232,33188 -PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml,fb/fbc1cb8895f6c396_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,3188 PatchWithInputChunk/PreviousBuildResources/introMoviePrefixed.txt,5d/5d2aadfa1344ac63_5e631fd37d3350e30095d1251b178f2c,5e631fd37d3350e30095d1251b178f2c,9117,3259,33188 PatchWithInputChunk/PreviousBuildResources/introMovieSomewhatChanged.txt,60/6054e5e0cf24763e_e9fadf6f2d386a0a0786bc863f20fa34,e9fadf6f2d386a0a0786bc863f20fa34,9091,3232,33188 +PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml,fb/fbc1cb8895f6c396_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,33188 +PatchWithInputChunk/resFileIndexShort_build_next.txt,1f/1f23be4b8bf6d2ae_4d398902b75611f7ae19903e6b461514,4d398902b75611f7ae19903e6b461514,612,324,33188 +PatchWithInputChunk/resFileIndexShort_build_previous.txt,32/326b0449af10b716_849821e0c98e37de4edc5f7156cf5887,849821e0c98e37de4edc5f7156cf5887,611,299,33188 +resourcesOnBranch/introMovie.txt,1c/1c6368ffb440041a_e9fadf6f2d386a0a0786bc863f20fa34,e9fadf6f2d386a0a0786bc863f20fa34,9091,3232,33188 +resourcesOnBranch/videoCardCategories.yaml,ff/ffe4c396c9c935d4_90d25dac9f4ed3233c5fda72bee3dfe6,90d25dac9f4ed3233c5fda72bee3dfe6,32815,5003,33188 From 2daee24c810890d161613aeaa3e99c08a2f2b22d Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 16 Feb 2026 13:34:49 +0000 Subject: [PATCH 116/124] Remove reliance on the WorkingDirectory when filter files from the CLI --- cli/src/CreateResourceGroupCliOperation.cpp | 9 +- cli/src/CreateResourceGroupCliOperation.h | 2 + include/ResourceGroup.h | 15 +- src/ResourceGroupImpl.cpp | 2 +- tests/src/CliTestFixture.cpp | 5 +- tests/src/ResourceToolsFilterTest.cpp | 221 ++++++---------- tests/src/ResourcesCliTest.cpp | 276 +++++++++++++++++++- tools/include/ResourceFilter.h | 25 +- tools/src/ResourceFilter.cpp | 73 ++++-- 9 files changed, 452 insertions(+), 176 deletions(-) diff --git a/cli/src/CreateResourceGroupCliOperation.cpp b/cli/src/CreateResourceGroupCliOperation.cpp index 65928f4..fbdcd03 100644 --- a/cli/src/CreateResourceGroupCliOperation.cpp +++ b/cli/src/CreateResourceGroupCliOperation.cpp @@ -16,7 +16,8 @@ CreateResourceGroupCliOperation::CreateResourceGroupCliOperation() : m_createResourceGroupExportResourcesId( "--export-resources" ), m_createResourceGroupExportResourcesDestinationTypeId( "--export-resources-destination-type" ), m_createResourceGroupExportResourcesDestinationPathId( "--export-resources-destination-path" ), - m_createResourceGroupIniFilterFilesArgumentId( "--filter-file" ) + m_createResourceGroupIniFilterFilesArgumentId( "--filter-file" ), + m_createResourceGroupIniFilterFilesBasePathArgumentId( "--filter-file-basepath" ) { AddRequiredPositionalArgument( m_createResourceGroupPathArgumentId, "Base directory to create resource group from." ); @@ -43,6 +44,8 @@ CreateResourceGroupCliOperation::CreateResourceGroupCliOperation() : AddArgument( m_createResourceGroupExportResourcesDestinationPathId, "Represents the base path where the exported resources will be saved. Requires --export-resources", false, false, defaultImportParams.exportResourcesDestinationSettings.basePath.string() ); AddArgument( m_createResourceGroupIniFilterFilesArgumentId, "Path to INI file(s) for resource filtering.", false, true, "" ); + + AddArgument( m_createResourceGroupIniFilterFilesBasePathArgumentId, "Base directory for resolving relative paths contained within filter INI file(s).", false, false, "" ); } bool CreateResourceGroupCliOperation::Execute( std::string& returnErrorMessage ) const @@ -98,6 +101,8 @@ bool CreateResourceGroupCliOperation::Execute( std::string& returnErrorMessage ) createResourceGroupParams.resourceFilterIniFiles.emplace_back( iniPathStr ); } } + + createResourceGroupParams.resourceFilterIniFilesBaseDirectory = m_argumentParser->get( m_createResourceGroupIniFilterFilesBasePathArgumentId ); } PrintStartBanner( createResourceGroupParams, exportParams ); @@ -156,6 +161,8 @@ void CreateResourceGroupCliOperation::PrintStartBanner( { std::cout << " - " << iniPath.generic_string() << std::endl; } + + std::cout << "Base Directory for Resolving Relative Paths in Filter INI File(s): " << createResourceGroupFromDirectoryParams.resourceFilterIniFilesBaseDirectory << std::endl; } else { diff --git a/cli/src/CreateResourceGroupCliOperation.h b/cli/src/CreateResourceGroupCliOperation.h index d40f982..f43566e 100644 --- a/cli/src/CreateResourceGroupCliOperation.h +++ b/cli/src/CreateResourceGroupCliOperation.h @@ -44,6 +44,8 @@ class CreateResourceGroupCliOperation : public CliOperation std::string m_createResourceGroupExportResourcesDestinationPathId; std::string m_createResourceGroupIniFilterFilesArgumentId; + + std::string m_createResourceGroupIniFilterFilesBasePathArgumentId; }; #endif // CreateResourceGroupCliOperation_H \ No newline at end of file diff --git a/include/ResourceGroup.h b/include/ResourceGroup.h index e000ac2..67463a5 100644 --- a/include/ResourceGroup.h +++ b/include/ResourceGroup.h @@ -205,7 +205,9 @@ struct ResourceGroupExportToFileParams * If export resources is set, specifies where the produced PatchResourceGroup will be saved. * @see CreateResourceGroupFromDirectoryParams::exportResources * @var CreateResourceGroupFromDirectoryParams::resourceFilterIniFiles - * List of INI file(s) containing include/exclude rules for resource filtering + * List of INI file(s) containing include/exclude rules for resource filtering + * @var CreateResourceGroupFromDirectoryParams::resourceFilterIniFilesBaseDirectory + * Base directory to resolve any relative paths contained within resourceFilterIniFiles. */ struct CreateResourceGroupFromDirectoryParams { @@ -219,13 +221,18 @@ struct CreateResourceGroupFromDirectoryParams std::string resourcePrefix = ""; - bool calculateCompressions = true; + bool calculateCompressions = true; - bool exportResources = false; + bool exportResources = false; - ResourceDestinationSettings exportResourcesDestinationSettings = { CarbonResources::ResourceDestinationType::LOCAL_CDN, "ExportedResources" }; + ResourceDestinationSettings exportResourcesDestinationSettings = { + CarbonResources::ResourceDestinationType::LOCAL_CDN, + "ExportedResources" + }; std::vector resourceFilterIniFiles = {}; + + std::filesystem::path resourceFilterIniFilesBaseDirectory = ""; }; /** @struct ResourceGroupMergeParams diff --git a/src/ResourceGroupImpl.cpp b/src/ResourceGroupImpl.cpp index 64731cb..892dec9 100644 --- a/src/ResourceGroupImpl.cpp +++ b/src/ResourceGroupImpl.cpp @@ -74,7 +74,7 @@ Result ResourceGroup::ResourceGroupImpl::CreateFromDirectory( const CreateResour { try { - resourceFilter.Initialize( params.resourceFilterIniFiles ); + resourceFilter.Initialize( params.resourceFilterIniFiles, params.resourceFilterIniFilesBaseDirectory, params.directory ); } catch( const std::exception& e ) { diff --git a/tests/src/CliTestFixture.cpp b/tests/src/CliTestFixture.cpp index 1748ebc..1c83f29 100644 --- a/tests/src/CliTestFixture.cpp +++ b/tests/src/CliTestFixture.cpp @@ -6,7 +6,10 @@ #include -int CliTestFixture::RunCli( std::vector& arguments, std::string* standardOutput /* = nullptr */, std::string* errorOutput /* = nullptr */, const std::string& workingDirectory /* = "" (empty = do not alter it) */ ) +int CliTestFixture::RunCli( std::vector& arguments, + std::string* standardOutput /* = nullptr */, + std::string* errorOutput /* = nullptr */, + const std::string& workingDirectory /* = "" (empty = do not alter it) */ ) { arguments.insert( arguments.begin(), CARBON_RESOURCES_CLI_EXE_FULLPATH ); diff --git a/tests/src/ResourceToolsFilterTest.cpp b/tests/src/ResourceToolsFilterTest.cpp index 72b80c2..cbe32d4 100644 --- a/tests/src/ResourceToolsFilterTest.cpp +++ b/tests/src/ResourceToolsFilterTest.cpp @@ -1961,7 +1961,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_ConfirmFileLoadFailure_SingleIniFileTh ResourceTools::ResourceFilter resourceFilter; try { - resourceFilter.Initialize( paths ); + resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); FAIL() << "Expected this test to fail: ResourceFilter_Load_SingleFile_ThatDoesNotExist"; } catch( const std::exception& e ) @@ -1987,7 +1987,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_ConfirmFileLoadFailure_MultipleFilesOn ResourceTools::ResourceFilter resourceFilter; try { - resourceFilter.Initialize( paths ); + resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); FAIL() << "Expected this test to fail: ResourceFilter_Load_MultipleFiles_OneThatDoesNotExist"; } catch( const std::exception& e ) @@ -2012,7 +2012,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_ConfirmSuccessfulFileLoad_example1_ini ResourceTools::ResourceFilter resourceFilter; try { - resourceFilter.Initialize( paths ); + resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); ASSERT_TRUE( true ); // If we got here, the file loaded successfully without any exceptions } catch( const std::exception& e ) @@ -2025,75 +2025,28 @@ TEST_F( ResourceToolsTest, ResourceFilter_ConfirmSuccessfulFileLoad_example1_ini } } -TEST_F( ResourceToolsTest, ResourceFilter_Validate_RaiiClassCurrentWorkingDirectoryChanger_ChangesWorkingDirectoryForDurationOfTest ) -{ - // This test validates that the RAII CurrentWorkingDirectoryChanger class correctly changes - // the current working directory for the duration of the test. - std::filesystem::path pathBeforeChange = std::filesystem::current_path(); - std::filesystem::path testDataPath = TEST_DATA_BASE_PATH; - - { - // RAII class to change the current working directory for the duration of this test - // Needed so both relative paths in the .ini file resolve correctly, - // based on paths to the location of the example .ini files - CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); - - const std::filesystem::path iniPath1 = "ExampleIniFiles/example1.ini"; - std::vector paths = { iniPath1 }; - ResourceTools::ResourceFilter resourceFilter; - try - { - resourceFilter.Initialize( paths ); - ASSERT_EQ( resourceFilter.HasFilters(), true ); - ASSERT_EQ( resourceFilter.GetFullResolvedPathMap().size(), 7 ); - - // Check that the "binaryFileIndex_v0_0_0.txt" file (and it's path) is included correctly - std::filesystem::path oneValidRelativePath = "./Indicies/binaryFileIndex_v0_0_0.txt"; - ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( oneValidRelativePath ), true ); - } - catch( const std::exception& e ) - { - FAIL() << "Exception in test, should not have failed: " << e.what(); - } - catch( ... ) - { - FAIL() << "Unknown exception thrown while initializing ResourceFilter with example1.ini"; - } - - // Make sure the working directory is set to the testDataPath and not the original path - ASSERT_EQ( std::filesystem::current_path().lexically_normal().string(), testDataPath.lexically_normal().string() ) << "Current working directory should be the TEST_DATA_BASE_PATH"; - ASSERT_NE( std::filesystem::current_path().lexically_normal().string(), pathBeforeChange.lexically_normal().string() ) << "Current working directory should not be same as at start of test"; - } - - // After the RAII object goes out of scope, the working directory should be restored to the original path - ASSERT_EQ( std::filesystem::current_path().lexically_normal().string(), pathBeforeChange.lexically_normal().string() ) << "Current working directory should have been restored"; - ASSERT_NE( std::filesystem::current_path().lexically_normal().string(), testDataPath.lexically_normal().string() ) << "Current working directory should not be the TEST_DATA_BASE_PATH"; -} - TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingRelativePaths_validSimpleExample1_ini ) { // This test validates that initializing a ResourceFilter with a valid ini file (validSimpleExample1.ini), - // using relative paths, loads successfully and the expected paths and filters are present. - - // Alter the current working directory for the duration of this test - CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); + // using relative paths to .ini file, loads successfully and the expected paths and filters are present. try { const std::filesystem::path iniPath1 = "ExampleIniFiles/validSimpleExample1.ini"; std::vector paths = { iniPath1 }; ResourceTools::ResourceFilter resourceFilter; - resourceFilter.Initialize( paths ); + resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); - // Validate correct included paths via the resourceFilter: + // Validate correct included paths via the resourceFilter + // Always compare those based on absolute paths: std::set validResolvedRelativePaths = { - "resourcesOnBranch/introMovie.txt", - "resourcesOnBranch/videoCardCategories.yaml" + GetTestFileFileAbsolutePath( "resourcesOnBranch/introMovie.txt" ) , + GetTestFileFileAbsolutePath( "resourcesOnBranch/videoCardCategories.yaml" ) }; ASSERT_EQ( resourceFilter.HasFilters(), true ); for( const auto& resolvedRelativePath : validResolvedRelativePaths ) { - ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedRelativePath ), true ) << "Should have included relative path: " << resolvedRelativePath.generic_string(); + ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedRelativePath ), true ) << "Should have included absolute path: " << resolvedRelativePath.generic_string(); } // Additional check to make sure the FullResolvedPathMap contains correct data (either include or exclude): @@ -2109,6 +2062,10 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingRelativ MapContainsPaths( expectedPaths, fullPathMap, "FullResolvedPathMap from validSimpleExample1.ini" ); ValidatePathMap( expectedPaths, fullPathMap, expectedIncludes, expectedExcludes, "FullResolvedPathMap from validSimpleExample1.ini" ); } + catch( const std::exception& e ) + { + FAIL() << "Test failed with: " << e.what(); + } catch( ... ) { FAIL() << "Test failed when it should have passed."; @@ -2118,22 +2075,19 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingRelativ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingAbsolutePaths_validSimpleExample1_ini ) { // This test validates that initializing a ResourceFilter with a valid ini file (validSimpleExample1.ini), - // using absolute paths, loads successfully and the expected paths and filters are present. - - // Alter the current working directory for the duration of this test - CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); + // using absolute path to .ini file, loads successfully and the expected paths and filters are present. try { - const std::filesystem::path iniPath1 = "ExampleIniFiles/validSimpleExample1.ini"; - std::filesystem::path iniPath1Abs = std::filesystem::absolute( iniPath1 ); - std::vector paths = { iniPath1Abs }; + const std::filesystem::path iniPath1 = GetTestFileFileAbsolutePath( "ExampleIniFiles/validSimpleExample1.ini" ); + //std::filesystem::path iniPath1Abs = std::filesystem::absolute( iniPath1 ); + std::vector paths = { iniPath1 }; ResourceTools::ResourceFilter resourceFilter; - resourceFilter.Initialize( paths ); + resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); - // Validate correct included paths via the resourceFilter: + // Validate correct included paths via the resourceFilter (compare absolute paths): std::set validResolvedAbsolutePaths = { - std::filesystem::absolute( "resourcesOnBranch/introMovie.txt" ), - std::filesystem::absolute( "resourcesOnBranch/videoCardCategories.yaml" ) + GetTestFileFileAbsolutePath( "resourcesOnBranch/introMovie.txt" ), + GetTestFileFileAbsolutePath( "resourcesOnBranch/videoCardCategories.yaml" ) }; ASSERT_EQ( resourceFilter.HasFilters(), true ); @@ -2155,6 +2109,10 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingAbsolut MapContainsPaths( expectedPaths, fullPathMap, "FullResolvedPathMap from validSimpleExample1.ini" ); ValidatePathMap( expectedPaths, fullPathMap, expectedIncludes, expectedExcludes, "FullResolvedPathMap from validSimpleExample1.ini" ); } + catch( const std::exception& e ) + { + FAIL() << "Test failed with: " << e.what(); + } catch( ... ) { FAIL() << "Test failed when it should have passed."; @@ -2164,38 +2122,35 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingAbsolut TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingRelativePaths_validComplexExample1_ini ) { // This test validates that initializing a ResourceFilter with a valid ini file (validComplexExample1.ini), - // using relative paths, loads successfully and the expected paths and filters are present. - - // Alter the current working directory for the duration of this test - CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); + // using relative path to .ini file, loads successfully and the expected paths and filters are present. try { const std::filesystem::path iniPath1 = "ExampleIniFiles/validComplexExample1.ini"; std::vector paths = { iniPath1 }; ResourceTools::ResourceFilter resourceFilter; - resourceFilter.Initialize( paths ); + resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); - // Validate correct included paths via the resourceFilter: + // Validate correct included paths via the resourceFilter (compare absolute paths): std::set validResolvedRelativePaths = { //"PatchWithInputChunk/NextBuildResources/introMovie.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] - "PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + resLocalCDN:/../NextBuildResources/introMoviePrefixed.txt + GetTestFileFileAbsolutePath( "PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resLocalCDN:/../NextBuildResources/introMoviePrefixed.txt //"PatchWithInputChunk/NextBuildResources/introMovieSomewhatChanged.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] - "PatchWithInputChunk/NextBuildResources/testResource2.txt", - "PatchWithInputChunk/NextBuildResources/videoCardCategories.yaml", - "PatchWithInputChunk/PreviousBuildResources/introMovie.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] - "PatchWithInputChunk/PreviousBuildResources/introMoviePrefixed.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] - "PatchWithInputChunk/PreviousBuildResources/introMovieSomewhatChanged.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] + GetTestFileFileAbsolutePath( "PatchWithInputChunk/NextBuildResources/testResource2.txt" ) , + GetTestFileFileAbsolutePath( "PatchWithInputChunk/NextBuildResources/videoCardCategories.yaml" ), + GetTestFileFileAbsolutePath( "PatchWithInputChunk/PreviousBuildResources/introMovie.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] + GetTestFileFileAbsolutePath( "PatchWithInputChunk/PreviousBuildResources/introMoviePrefixed.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] + GetTestFileFileAbsolutePath( "PatchWithInputChunk/PreviousBuildResources/introMovieSomewhatChanged.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] //"PatchWithInputChunk/PreviousBuildResources/testResource.txt", // resRoot:/PatchWithInputChunk/PreviousBuildResources/* ![ testResource.txt ] - "PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml", - "PatchWithInputChunk/PatchResourceGroup_previousBuild_latestBuild.yaml", - "PatchWithInputChunk/resFileIndexShort_build_next.txt", - "PatchWithInputChunk/resFileIndexShort_build_previous.txt", + GetTestFileFileAbsolutePath( "PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml" ), + GetTestFileFileAbsolutePath( "PatchWithInputChunk/PatchResourceGroup_previousBuild_latestBuild.yaml" ), + GetTestFileFileAbsolutePath( "PatchWithInputChunk/resFileIndexShort_build_next.txt" ), + GetTestFileFileAbsolutePath( "PatchWithInputChunk/resFileIndexShort_build_previous.txt" ), }; ASSERT_EQ( resourceFilter.HasFilters(), true ); for( const auto& resolvedRelativePath : validResolvedRelativePaths ) { - ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedRelativePath ), true ) << "Should have included relative path: " << resolvedRelativePath.generic_string(); + ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedRelativePath ), true ) << "Should have included absolute path: " << resolvedRelativePath.generic_string(); } // Additional check to make sure the FullResolvedPathMap contains correct data (either include or exclude): @@ -2275,38 +2230,35 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingRelativ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingAbsolutePath_validComplexExample1_ini ) { // This test validates that initializing a ResourceFilter with a valid ini file (validComplexExample1.ini), - // using absolute paths, loads successfully and the expected paths and filters are present. - - // Alter the current working directory for the duration of this test - CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); + // using absolute path to .ini file, loads successfully and the expected paths and filters are present. try { - const std::filesystem::path iniPath1 = "ExampleIniFiles/validComplexExample1.ini"; - std::vector pathsAbs = { std::filesystem::absolute( iniPath1 ) }; + const std::filesystem::path iniPath1 = GetTestFileFileAbsolutePath( "ExampleIniFiles/validComplexExample1.ini" ); + std::vector pathsAbs = { iniPath1 }; ResourceTools::ResourceFilter resourceFilter; - resourceFilter.Initialize( pathsAbs ); + resourceFilter.Initialize( pathsAbs, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); // Validate correct included paths via the resourceFilter: std::set validResolvedRelativePaths = { //"PatchWithInputChunk/NextBuildResources/introMovie.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] - std::filesystem::absolute( "PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resLocalCDN:/../NextBuildResources/introMoviePrefixed.txt + GetTestFileFileAbsolutePath( "PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resLocalCDN:/../NextBuildResources/introMoviePrefixed.txt //"PatchWithInputChunk/NextBuildResources/introMovieSomewhatChanged.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] - std::filesystem::absolute( "PatchWithInputChunk/NextBuildResources/testResource2.txt" ), - std::filesystem::absolute( "PatchWithInputChunk/NextBuildResources/videoCardCategories.yaml" ), - std::filesystem::absolute( "PatchWithInputChunk/PreviousBuildResources/introMovie.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] - std::filesystem::absolute( "PatchWithInputChunk/PreviousBuildResources/introMoviePrefixed.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] - std::filesystem::absolute( "PatchWithInputChunk/PreviousBuildResources/introMovieSomewhatChanged.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] + GetTestFileFileAbsolutePath( "PatchWithInputChunk/NextBuildResources/testResource2.txt" ), + GetTestFileFileAbsolutePath( "PatchWithInputChunk/NextBuildResources/videoCardCategories.yaml" ), + GetTestFileFileAbsolutePath( "PatchWithInputChunk/PreviousBuildResources/introMovie.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] + GetTestFileFileAbsolutePath( "PatchWithInputChunk/PreviousBuildResources/introMoviePrefixed.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] + GetTestFileFileAbsolutePath( "PatchWithInputChunk/PreviousBuildResources/introMovieSomewhatChanged.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] //"PatchWithInputChunk/PreviousBuildResources/testResource.txt", // resRoot:/PatchWithInputChunk/PreviousBuildResources/* ![ testResource.txt ] - std::filesystem::absolute( "PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml" ), - std::filesystem::absolute( "PatchWithInputChunk/PatchResourceGroup_previousBuild_latestBuild.yaml" ), - std::filesystem::absolute( "PatchWithInputChunk/resFileIndexShort_build_next.txt" ), - std::filesystem::absolute( "PatchWithInputChunk/resFileIndexShort_build_previous.txt" ), + GetTestFileFileAbsolutePath( "PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml" ), + GetTestFileFileAbsolutePath( "PatchWithInputChunk/PatchResourceGroup_previousBuild_latestBuild.yaml" ), + GetTestFileFileAbsolutePath( "PatchWithInputChunk/resFileIndexShort_build_next.txt" ), + GetTestFileFileAbsolutePath( "PatchWithInputChunk/resFileIndexShort_build_previous.txt" ), }; ASSERT_EQ( resourceFilter.HasFilters(), true ); for( const auto& resolvedAbsPath : validResolvedRelativePaths ) { - ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedAbsPath ), true ) << "Should have included relative path: " << resolvedAbsPath.generic_string(); + ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedAbsPath ), true ) << "Should have included absolute path: " << resolvedAbsPath.generic_string(); } // Additional check to make sure the FullResolvedPathMap contains correct data (either include or exclude): @@ -2386,46 +2338,43 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingAbsolut TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulLoadOf2IniFiles_validComplexExample1_and_validSimpleExample1 ) { // This test validates that initializing a ResourceFilter with two valid ini files - // (validComplexExample1.ini and validSimpleExample1.ini), using relative paths, + // (validComplexExample1.ini and validSimpleExample1.ini), using absolute paths, // loads successfully and the expected paths and filters from both ini files are // present in the resulting ResourceFilter. - - // Alter the current working directory for the duration of this test - CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); try { std::vector paths = { - "ExampleIniFiles/validComplexExample1.ini", - "ExampleIniFiles/validSimpleExample1.ini" + GetTestFileFileAbsolutePath( "ExampleIniFiles/validComplexExample1.ini" ), + GetTestFileFileAbsolutePath( "ExampleIniFiles/validSimpleExample1.ini" ) }; ResourceTools::ResourceFilter resourceFilter; - resourceFilter.Initialize( paths ); + resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); // Validate correct included paths via the resourceFilter: std::set validResolvedRelativePaths = { // From validComplexExample1: //"PatchWithInputChunk/NextBuildResources/introMovie.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] - "PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + resLocalCDN:/../NextBuildResources/introMoviePrefixed.txt + GetTestFileFileAbsolutePath( "PatchWithInputChunk/NextBuildResources/introMoviePrefixed.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resLocalCDN:/../NextBuildResources/introMoviePrefixed.txt //"PatchWithInputChunk/NextBuildResources/introMovieSomewhatChanged.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] - "PatchWithInputChunk/NextBuildResources/testResource2.txt", - "PatchWithInputChunk/NextBuildResources/videoCardCategories.yaml", - "PatchWithInputChunk/PreviousBuildResources/introMovie.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] - "PatchWithInputChunk/PreviousBuildResources/introMoviePrefixed.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] - "PatchWithInputChunk/PreviousBuildResources/introMovieSomewhatChanged.txt", // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] + GetTestFileFileAbsolutePath( "PatchWithInputChunk/NextBuildResources/testResource2.txt" ), + GetTestFileFileAbsolutePath( "PatchWithInputChunk/NextBuildResources/videoCardCategories.yaml" ), + GetTestFileFileAbsolutePath( "PatchWithInputChunk/PreviousBuildResources/introMovie.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] + GetTestFileFileAbsolutePath( "PatchWithInputChunk/PreviousBuildResources/introMoviePrefixed.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] + GetTestFileFileAbsolutePath( "PatchWithInputChunk/PreviousBuildResources/introMovieSomewhatChanged.txt" ), // resRoot:/PatchWithInputChunk/... ![ Movie ] + resPrevious:/* [ Movie ] //"PatchWithInputChunk/PreviousBuildResources/testResource.txt", // resRoot:/PatchWithInputChunk/PreviousBuildResources/* ![ testResource.txt ] - "PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml", - "PatchWithInputChunk/PatchResourceGroup_previousBuild_latestBuild.yaml", - "PatchWithInputChunk/resFileIndexShort_build_next.txt", - "PatchWithInputChunk/resFileIndexShort_build_previous.txt", + GetTestFileFileAbsolutePath( "PatchWithInputChunk/PreviousBuildResources/videoCardCategories.yaml" ), + GetTestFileFileAbsolutePath( "PatchWithInputChunk/PatchResourceGroup_previousBuild_latestBuild.yaml" ), + GetTestFileFileAbsolutePath( "PatchWithInputChunk/resFileIndexShort_build_next.txt" ), + GetTestFileFileAbsolutePath( "PatchWithInputChunk/resFileIndexShort_build_previous.txt" ), // From validSimpleExample1.ini: - "resourcesOnBranch/introMovie.txt", - "resourcesOnBranch/videoCardCategories.yaml" + GetTestFileFileAbsolutePath( "resourcesOnBranch/introMovie.txt" ), + GetTestFileFileAbsolutePath( "resourcesOnBranch/videoCardCategories.yaml" ) }; ASSERT_EQ( resourceFilter.HasFilters(), true ); for( const auto& resolvedRelativePath : validResolvedRelativePaths ) { - ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedRelativePath ), true ) << "Should have included relative path: " << resolvedRelativePath.generic_string(); + ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedRelativePath ), true ) << "Should have included absolute path: " << resolvedRelativePath.generic_string(); } // Additional check to make sure the FullResolvedPathMap contains correct data (either include or exclude): @@ -2516,15 +2465,12 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccess_FileLoadOverrideUsingD // This test validates that initializing a ResourceFilter with a valid ini file (validOverrideDifferentRelativeSameAbsolutePaths.ini), // using two distinct relative paths (which result in the same absolute path), loads successfully and the expected paths and filters are present. // See notes in the validOverrideDifferentRelativeSameAbsolutePaths.ini file for further details. - - // Alter the current working directory for the duration of this test - CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); try { - const std::filesystem::path iniPath = "ExampleIniFiles/validOverrideDifferentRelativeSameAbsolutePaths.ini"; + const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/validOverrideDifferentRelativeSameAbsolutePaths.ini" ); std::vector paths = { iniPath }; ResourceTools::ResourceFilter resourceFilter; - resourceFilter.Initialize( paths ); + resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); // Validate correct included and exclude paths via the resourceFilter: // - Because of the two distinct relative paths (./resourcesOnBranch/* and /resourcesOnBranch/*) @@ -2532,18 +2478,18 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccess_FileLoadOverrideUsingD // both of the files should be EXCLUDED (i.e. equal priority means that exclude wins) std::set validIncludeResolvedRelativePaths = {}; std::set validExcludeResolvedRelativePaths = { - "resourcesOnBranch/introMovie.txt", - "resourcesOnBranch/videoCardCategories.yaml" + GetTestFileFileAbsolutePath( "resourcesOnBranch/introMovie.txt" ), + GetTestFileFileAbsolutePath( "resourcesOnBranch/videoCardCategories.yaml" ) }; ASSERT_EQ( resourceFilter.HasFilters(), true ); for( const auto& resolvedRelativeIncludePath : validIncludeResolvedRelativePaths ) { - ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedRelativeIncludePath ), true ) << "Should have included relative path: " << resolvedRelativeIncludePath.generic_string(); + ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedRelativeIncludePath ), true ) << "Should have included absolute path: " << resolvedRelativeIncludePath.generic_string(); } for( const auto& resolvedRelativeExcludePath : validExcludeResolvedRelativePaths ) { - ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedRelativeExcludePath ), false ) << "Should have excluded relative path: " << resolvedRelativeExcludePath.generic_string(); + ASSERT_EQ( resourceFilter.FilePathMatchesIncludeFilterRules( resolvedRelativeExcludePath ), false ) << "Should have excluded absolute path: " << resolvedRelativeExcludePath.generic_string(); } // Additional checks to make sure the FullResolvedPathMap contains correct include/exclude filter data @@ -2589,22 +2535,19 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccess_FileLoadRespathInlineO // This test validates that initializing a ResourceFilter with a valid ini file (validInlineFilterOverrideOnSameRelativePath.ini), using two // identical "respath" path entries with distinct overriding include/exclude filters, loads successfully and the expected paths and filters are present. // See notes in the validInlineFilterOverrideOnSameRelativePath.ini file for further details. - - // Alter the current working directory for the duration of this test - CurrentWorkingDirectoryChanger cwdRAII( TEST_DATA_BASE_PATH ); try { - const std::filesystem::path iniPath = "ExampleIniFiles/validInlineFilterOverrideOnSameRelativePath.ini"; + const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/validInlineFilterOverrideOnSameRelativePath.ini" ); std::vector paths = { iniPath }; ResourceTools::ResourceFilter resourceFilter; - resourceFilter.Initialize( paths ); + resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); // Validate correct included and exclude paths via the resourceFilter: // - Because of the overriding rules for the identical "respath" path entries. // We should end up with includes of BOTH .txt and .yaml files with no excludes. std::set validIncludeResolvedRelativePaths = { - "resourcesOnBranch/introMovie.txt", - "resourcesOnBranch/videoCardCategories.yaml" + GetTestFileFileAbsolutePath( "resourcesOnBranch/introMovie.txt" ), + GetTestFileFileAbsolutePath( "resourcesOnBranch/videoCardCategories.yaml" ) }; std::set validExcludeResolvedRelativePaths = { }; diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index b25f328..0164fac 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -307,12 +307,14 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1_YamlOutput arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); arguments.push_back( filterIniFilePath.lexically_normal().string() ); + arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( TEST_DATA_BASE_PATH ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); arguments.push_back( "--document-version" ); arguments.push_back( "0.1.0" ); // This is default YAML document version (setting it explicitly for clarity in test) - int res = RunCli( arguments, &output, &errorOutput, TEST_DATA_BASE_PATH ); + int res = RunCli( arguments, &output, &errorOutput ); std::cout << "--- RunCli() output: ---" << std::endl; std::cout << output << std::endl; std::cout << "------------------------" << std::endl; @@ -349,12 +351,14 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1_CsvTxtOutp arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); arguments.push_back( filterIniFilePath.lexically_normal().string() ); + arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( TEST_DATA_BASE_PATH ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); arguments.push_back( "--document-version" ); arguments.push_back( "0.0.0" ); // This is the "old style" csv (txt) document version - int res = RunCli( arguments, &output, &errorOutput, TEST_DATA_BASE_PATH ); + int res = RunCli( arguments, &output, &errorOutput ); std::cout << "--- RunCli() output: ---" << std::endl; std::cout << output << std::endl; std::cout << "------------------------" << std::endl; @@ -372,6 +376,248 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1_CsvTxtOutp EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ) << " Output file does not match expected gold file."; } +TEST_F( ResourcesCliTest, CreateGroup_ConfirmWorks_UsingFilter_validSimpleExample1_WithRelativeFilterFilePathAndBasePathSet ) +{ + // Setup test parameters + std::string output; + std::string errorOutput; + std::vector arguments; + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_ConfirmWorks_UsingFilter_validSimpleExample1_WithRelativeFilterFilePathAndBasePathSet.txt" ); + std::filesystem::path filterIniFilePath = "ExampleIniFiles/validSimpleExample1.ini"; + + // Ensure any previous test output files are removed + RemoveFiles( { outputFilePath } ); + + arguments.push_back( "create-group" ); + arguments.push_back( inputDirectoryPath.lexically_normal().string() ); + arguments.push_back( "--verbosity-level" ); + arguments.push_back( "3" ); + arguments.push_back( "--filter-file" ); + arguments.push_back( filterIniFilePath.lexically_normal().string() ); + arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( TEST_DATA_BASE_PATH ); + arguments.push_back( "--output-file" ); + arguments.push_back( outputFilePath.lexically_normal().string() ); + arguments.push_back( "--document-version" ); + arguments.push_back( "0.0.0" ); // This is the "old style" csv (txt) document version + + int res = RunCli( arguments, &output, &errorOutput ); + std::cout << "--- RunCli() output: ---" << std::endl; + std::cout << output << std::endl; + std::cout << "------------------------" << std::endl; + + ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; + + // Check expected outcome +#if _WIN64 + std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_Windows.txt" ); +#elif __APPLE__ + std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_macOS.txt" ); +#else +#error Unsupported platform +#endif + EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ) << " Output file does not match expected gold file."; +} + +TEST_F( ResourcesCliTest, CreateGroup_ConfirmWorks_UsingFilter_validSimpleExample1_WithAbsoluteFilterFilePathAndBasePathSet ) +{ + // Setup test parameters + std::string output; + std::string errorOutput; + std::vector arguments; + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_ConfirmWorks_UsingFilter_validSimpleExample1_WithAbsoluteFilterFilePathAndBasePathSet.txt" ); + std::filesystem::path filterIniFilePath = GetTestFileFileAbsolutePath( "ExampleIniFiles/validSimpleExample1.ini" ); + + // Ensure any previous test output files are removed + RemoveFiles( { outputFilePath } ); + + arguments.push_back( "create-group" ); + arguments.push_back( inputDirectoryPath.lexically_normal().string() ); + arguments.push_back( "--verbosity-level" ); + arguments.push_back( "3" ); + arguments.push_back( "--filter-file" ); + arguments.push_back( filterIniFilePath.lexically_normal().string() ); + arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( TEST_DATA_BASE_PATH ); + arguments.push_back( "--output-file" ); + arguments.push_back( outputFilePath.lexically_normal().string() ); + arguments.push_back( "--document-version" ); + arguments.push_back( "0.0.0" ); // This is the "old style" csv (txt) document version + + int res = RunCli( arguments, &output, &errorOutput ); + std::cout << "--- RunCli() output: ---" << std::endl; + std::cout << output << std::endl; + std::cout << "------------------------" << std::endl; + + ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; + + // Check expected outcome +#if _WIN64 + std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_Windows.txt" ); +#elif __APPLE__ + std::filesystem::path goldFile = GetTestFileFileAbsolutePath( "ExpectedTestOutputFiles/CreateGroup_UsingFilter_validSimpleExample1_macOS.txt" ); +#else +#error Unsupported platform +#endif + EXPECT_TRUE( FilesMatch( goldFile, outputFilePath ) ) << " Output file does not match expected gold file."; +} + +TEST_F( ResourcesCliTest, CreateGroup_ConfirmFails_UsingFilter_validSimpleExample1_WithRelativeFilterFilePathAndWrongBasePathSet ) +{ + // Setup test parameters + std::string output; + std::string errorOutput; + std::vector arguments; + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_ConfirmFails_UsingFilter_validSimpleExample1_WithRelativeFilterFilePathAndWrongBasePathSet.txt" ); + std::filesystem::path filterIniFilePath = "ExampleIniFiles/validSimpleExample1.ini"; + + // Ensure any previous test output files are removed + RemoveFiles( { outputFilePath } ); + + arguments.push_back( "create-group" ); + arguments.push_back( inputDirectoryPath.lexically_normal().string() ); + arguments.push_back( "--verbosity-level" ); + arguments.push_back( "3" ); + arguments.push_back( "--filter-file" ); + arguments.push_back( filterIniFilePath.lexically_normal().string() ); + arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( "Some/Incorrect/Base/Path" ); + arguments.push_back( "--output-file" ); + arguments.push_back( outputFilePath.lexically_normal().string() ); + arguments.push_back( "--document-version" ); + arguments.push_back( "0.0.0" ); // This is the "old style" csv (txt) document version + + int res = RunCli( arguments, &output, &errorOutput ); + std::cout << "--- RunCli() output: ---" << std::endl; + std::cout << output << std::endl; + std::cout << "------------------------" << std::endl; + + // Should fail, expecting non-zero exit code + ASSERT_EQ( res, 1 ) << "CLI operation should fail for a reading relative filter .ini files with wrong --filter-base-path parameter - with resultCode=1"; + // Check for expected error message + EXPECT_TRUE( errorOutput.find( "[ERROR: Failed to initialize ResourceFilter from .ini file]" ) != std::string::npos ) + << "Expected generic (top-level) error message about failure to initialize ResourceFilter from .ini file. Actual error: " << errorOutput; +} + +TEST_F( ResourcesCliTest, CreateGroup_ConfirmFails_UsingFilter_validSimpleExample1_WithAbsoluteFilterFilePathAndWrongBasePathSet ) +{ + // Setup test parameters + std::string output; + std::string errorOutput; + std::vector arguments; + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_ConfirmFails_UsingFilter_validSimpleExample1_WithAbsoluteFilterFilePathAndWrongBasePathSet.txt" ); + std::filesystem::path filterIniFilePath = GetTestFileFileAbsolutePath( "ExampleIniFiles/validSimpleExample1.ini" ); + + // Ensure any previous test output files are removed + RemoveFiles( { outputFilePath } ); + + arguments.push_back( "create-group" ); + arguments.push_back( inputDirectoryPath.lexically_normal().string() ); + arguments.push_back( "--verbosity-level" ); + arguments.push_back( "3" ); + arguments.push_back( "--filter-file" ); + arguments.push_back( filterIniFilePath.lexically_normal().string() ); + arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( "Some/Incorrect/Base/Path" ); + arguments.push_back( "--output-file" ); + arguments.push_back( outputFilePath.lexically_normal().string() ); + arguments.push_back( "--document-version" ); + arguments.push_back( "0.0.0" ); // This is the "old style" csv (txt) document version + + int res = RunCli( arguments, &output, &errorOutput ); + std::cout << "--- RunCli() output: ---" << std::endl; + std::cout << output << std::endl; + std::cout << "------------------------" << std::endl; + + ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; + + // Because of the absolute path to the .ini file, the operation will be successful even though the + // basepath parameter is wrong (the .ini file can still be found and read successfully). + // However, the rules applied from the filter .ini file will resolve to absolute paths that do not exist. + // Therefore, no files will match the filter criteria and the output file will be empty. + EXPECT_TRUE( std::filesystem::exists( outputFilePath ) ) << " Empty output file [" << outputFilePath.generic_string() << "] was not created, when it should have been."; + EXPECT_TRUE( std::filesystem::is_empty( outputFilePath ) ) << " Output file should be empty due to filter .ini file rules not matching any files because of wrong basepath."; +} + +TEST_F( ResourcesCliTest, CreateGroup_ConfirmFails_UsingFilter_validSimpleExample1_WithRelativeFilterFilePathAndNoBasePath ) +{ + // Setup test parameters + std::string output; + std::string errorOutput; + std::vector arguments; + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_ConfirmFails_UsingFilter_validSimpleExample1_WithRelativeFilterFilePathAndNoBasePath.txt" ); + std::filesystem::path filterIniFilePath = "ExampleIniFiles/validSimpleExample1.ini"; + + // Ensure any previous test output files are removed + RemoveFiles( { outputFilePath } ); + + arguments.push_back( "create-group" ); + arguments.push_back( inputDirectoryPath.lexically_normal().string() ); + arguments.push_back( "--verbosity-level" ); + arguments.push_back( "3" ); + arguments.push_back( "--filter-file" ); + arguments.push_back( filterIniFilePath.lexically_normal().string() ); + arguments.push_back( "--output-file" ); + arguments.push_back( outputFilePath.lexically_normal().string() ); + arguments.push_back( "--document-version" ); + arguments.push_back( "0.0.0" ); // This is the "old style" csv (txt) document version + + int res = RunCli( arguments, &output, &errorOutput ); + std::cout << "--- RunCli() output: ---" << std::endl; + std::cout << output << std::endl; + std::cout << "------------------------" << std::endl; + + // Should fail, expecting non-zero exit code + ASSERT_EQ( res, 1 ) << "CLI operation should fail for a reading relative filter .ini files with wrong --filter-base-path parameter - with resultCode=1"; + // Check for expected error message + EXPECT_TRUE( errorOutput.find( "[ERROR: Failed to initialize ResourceFilter from .ini file]" ) != std::string::npos ) + << "Expected generic (top-level) error message about failure to initialize ResourceFilter from .ini file. Actual error: " << errorOutput; +} + +TEST_F( ResourcesCliTest, CreateGroup_ConfirmFails_UsingFilter_validSimpleExample1_WithAbsoluteFilterFilePathAndNoBasePath ) +{ + // Setup test parameters + std::string output; + std::string errorOutput; + std::vector arguments; + std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_ConfirmFails_UsingFilter_validSimpleExample1_WithAbsoluteFilterFilePathAndNoBasePath.txt" ); + std::filesystem::path filterIniFilePath = GetTestFileFileAbsolutePath( "ExampleIniFiles/validSimpleExample1.ini" ); + + // Ensure any previous test output files are removed + RemoveFiles( { outputFilePath } ); + + arguments.push_back( "create-group" ); + arguments.push_back( inputDirectoryPath.lexically_normal().string() ); + arguments.push_back( "--verbosity-level" ); + arguments.push_back( "3" ); + arguments.push_back( "--filter-file" ); + arguments.push_back( filterIniFilePath.lexically_normal().string() ); + arguments.push_back( "--output-file" ); + arguments.push_back( outputFilePath.lexically_normal().string() ); + arguments.push_back( "--document-version" ); + arguments.push_back( "0.0.0" ); // This is the "old style" csv (txt) document version + + int res = RunCli( arguments, &output, &errorOutput ); + std::cout << "--- RunCli() output: ---" << std::endl; + std::cout << output << std::endl; + std::cout << "------------------------" << std::endl; + + ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; + + // Because of the absolute path to the .ini file, the operation will be successful even though the + // basepath parameter is wrong (the .ini file can still be found and read successfully). + // However, the rules applied from the filter .ini file will resolve to absolute paths that do not exist. + // Therefore, no files will match the filter criteria and the output file will be empty. + EXPECT_TRUE( std::filesystem::exists( outputFilePath ) ) << " Empty output file [" << outputFilePath.generic_string() << "] was not created, when it should have been."; + EXPECT_TRUE( std::filesystem::is_empty( outputFilePath ) ) << " Output file should be empty due to filter .ini file rules not matching any files because of wrong basepath."; +} + TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1_YamlOutput ) { // Setup test parameters @@ -391,12 +637,14 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1_YamlOutpu arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); arguments.push_back( filterIniFilePath.lexically_normal().string() ); + arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( TEST_DATA_BASE_PATH ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); arguments.push_back( "--document-version" ); arguments.push_back( "0.1.0" ); // This is default YAML document version (setting it explicitly for clarity in test) - int res = RunCli( arguments, &output, &errorOutput, TEST_DATA_BASE_PATH ); + int res = RunCli( arguments, &output, &errorOutput ); std::cout << "--- RunCli() output: ---" << std::endl; std::cout << output << std::endl; std::cout << "------------------------" << std::endl; @@ -433,12 +681,14 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1_CsvTxtOut arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); arguments.push_back( filterIniFilePath.lexically_normal().string() ); + arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( TEST_DATA_BASE_PATH ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); arguments.push_back( "--document-version" ); arguments.push_back( "0.0.0" ); // This is the "old style" csv (txt) document version - int res = RunCli( arguments, &output, &errorOutput, TEST_DATA_BASE_PATH ); + int res = RunCli( arguments, &output, &errorOutput ); std::cout << "--- RunCli() output: ---" << std::endl; std::cout << output << std::endl; std::cout << "------------------------" << std::endl; @@ -481,12 +731,14 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1_ arguments.push_back( "--filter-file" ); arguments.push_back( filterFilePath.lexically_normal().string() ); } + arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( TEST_DATA_BASE_PATH ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); arguments.push_back( "--document-version" ); arguments.push_back( "0.1.0" ); // This is default YAML document version (setting it explicitly for clarity in test) - int res = RunCli( arguments, &output, &errorOutput, TEST_DATA_BASE_PATH ); + int res = RunCli( arguments, &output, &errorOutput ); std::cout << "--- RunCli() output: ---" << std::endl; std::cout << output << std::endl; std::cout << "------------------------" << std::endl; @@ -529,12 +781,14 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1_ arguments.push_back( "--filter-file" ); arguments.push_back( filterFilePath.lexically_normal().string() ); } + arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( TEST_DATA_BASE_PATH ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); arguments.push_back( "--document-version" ); arguments.push_back( "0.0.0" ); // This is the "old style" csv (txt) document version - int res = RunCli( arguments, &output, &errorOutput, TEST_DATA_BASE_PATH ); + int res = RunCli( arguments, &output, &errorOutput ); std::cout << "--- RunCli() output: ---" << std::endl; std::cout << output << std::endl; std::cout << "------------------------" << std::endl; @@ -571,12 +825,15 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFailureParsingWronglyFormattedIniFi arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); arguments.push_back( filterIniFilePath.lexically_normal().string() ); + arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( TEST_DATA_BASE_PATH ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); - int res = RunCli( arguments, &output, &errorOutput, TEST_DATA_BASE_PATH ); + int res = RunCli( arguments, &output, &errorOutput ); std::cout << "--- RunCli() output: ---" << std::endl; std::cout << output << std::endl; + std::cout << "errorOutput: " << errorOutput << std::endl; std::cout << "------------------------" << std::endl; // Should fail, expecting non-zero exit code @@ -605,12 +862,15 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFailureUsingNoExistentFilterFile_in arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); arguments.push_back( filterIniFilePath.lexically_normal().string() ); + arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( TEST_DATA_BASE_PATH ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); - int res = RunCli( arguments, &output, &errorOutput, TEST_DATA_BASE_PATH ); + int res = RunCli( arguments, &output, &errorOutput ); std::cout << "--- RunCli() output: ---" << std::endl; std::cout << output << std::endl; + std::cout << "errorOutput: " << errorOutput << std::endl; std::cout << "------------------------" << std::endl; // Should fail, expecting non-zero exit code diff --git a/tools/include/ResourceFilter.h b/tools/include/ResourceFilter.h index 06e45ca..c01b472 100644 --- a/tools/include/ResourceFilter.h +++ b/tools/include/ResourceFilter.h @@ -25,11 +25,15 @@ class ResourceFilter public: ResourceFilter() = default; - // Construct a ResourceFilter object by passing filter .ini file(s). - explicit ResourceFilter( const std::vector& iniFilePaths ); + // Construct a ResourceFilter object by passing it a list of filter .ini file(s) and relevant paths. + explicit ResourceFilter( const std::vector& iniFilePaths, + const std::filesystem::path& filterFilesAbsoluteBasePath, + const std::filesystem::path& operationalBasePath ); - // Initializes the ResourceFilter by passing in and parsing the supplied filter .ini file(s). - void Initialize( const std::vector& iniFilePaths ); + // Initializes the ResourceFilter by passing in and parsing the supplied filter .ini file(s) and relevant paths. + void Initialize( const std::vector& iniFilePaths, + const std::filesystem::path& filterFilesAbsoluteBasePath, + const std::filesystem::path& operationalBasePath ); // Returns true if this ResourceFilter has any filter .ini files, false otherwise. bool HasFilters() const; @@ -52,9 +56,22 @@ class ResourceFilter // Static helper function to normalize paths (i.e. deal with \ / .. . etc) static std::string NormalizePath( const std::string& path ); + // Static helper function to get an absolute path by combining a relative path with an absolute base path (if not already absolute) + static std::filesystem::path GetAbsolutePathFromBase( const std::filesystem::path& relativeOrAbsolutePath, + const std::filesystem::path& absoluteBasePath ); + // A flag used to prevent multiple initializations of the ResourceFilter bool m_initialized{ false }; + // The absolute base file path to the location of filter .ini file(s) + // Needed when resolving the relative paths contained within those files. + std::filesystem::path m_filterFilesAbsoluteBasePath; + + // The "operational base path" is the base path for callers initializing this object. + // E.g. in case of an orign call from ResourceGroup::ResourceGroupImpl::CreateFromDirectory() + // this corresponds to the "params.directory" parameter. + std::filesystem::path m_operationalBasePath; + // Vector of all the filter .ini files (wrapped in FilterResourceFile objects) std::vector> m_filterFiles; diff --git a/tools/src/ResourceFilter.cpp b/tools/src/ResourceFilter.cpp index c54a1c9..9f7a5c8 100644 --- a/tools/src/ResourceFilter.cpp +++ b/tools/src/ResourceFilter.cpp @@ -10,10 +10,17 @@ namespace ResourceTools // ------------------------------------------------------------- // Description: // Construct a ResourceFilter object by passing in filter .ini file(s). +// Arguments: +// iniFilePaths - vector of file paths to the filter .ini files +// filterFilesAbsoluteBasePath - absolute base path to the location of filter .ini file(s) +// operationalBasePath - absolute base path to use for resolving relative input file paths when checking against filter rules // ------------------------------------------------------------- -ResourceFilter::ResourceFilter( const std::vector& iniFilePaths ) +ResourceFilter::ResourceFilter( + const std::vector& iniFilePaths, + const std::filesystem::path& filterFilesAbsoluteBasePath, + const std::filesystem::path& operationalBasePath ) { - Initialize( iniFilePaths ); + Initialize( iniFilePaths, filterFilesAbsoluteBasePath, operationalBasePath ); } // ------------------------------------------------------------- @@ -22,22 +29,30 @@ ResourceFilter::ResourceFilter( const std::vector& iniFil // FilterResourceFile objects for each of the supplied filter .ini file(s). // Arguments: // iniFilePaths - vector of file paths to the filter .ini files +// filterFilesAbsoluteBasePath - absolute base path to the location of filter .ini file(s) +// operationalBasePath - absolute base path to use for resolving relative input file paths when checking against filter rules // Return Value: // None (void). // ------------------------------------------------------------- -void ResourceFilter::Initialize( const std::vector& iniFilePaths ) +void ResourceFilter::Initialize( const std::vector& iniFilePaths, + const std::filesystem::path& filterFilesAbsoluteBasePath, + const std::filesystem::path& operationalBasePath ) { if( m_initialized ) { throw std::runtime_error( "ResourceFilter is already initialized." ); } + m_filterFilesAbsoluteBasePath = filterFilesAbsoluteBasePath; + m_operationalBasePath = operationalBasePath; + std::set uniquePaths( iniFilePaths.begin(), iniFilePaths.end() ); for( const auto& path : uniquePaths ) { try { - m_filterFiles.emplace_back( std::make_unique( path ) ); + // Make sure we initialize the FilterResourceFile based on the absolute path of the .ini file + m_filterFiles.emplace_back( std::make_unique( GetAbsolutePathFromBase( path, m_filterFilesAbsoluteBasePath ) ) ); } catch( const std::exception& e ) { @@ -86,17 +101,21 @@ const std::map& ResourceFilter::GetFullResolv // Check if the inFilePath should be included or excluded based on // the filtering rules from all .ini file(s) in this ResourceFilter. // Arguments: -// inFilePath - the file path to check against the filter rules, -// can be relative or absolute path. +// inFilePath - the absolute file path to check against the filter rules // Return Value: // True = the file path matches the include filter rules and should be included // False = the file path does not match the include filter rules and should be excluded // ------------------------------------------------------------- bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::path& inFilePath ) { - // Make sure we work with the absolute path representation of the input file - std::filesystem::path inFilePathAbs = std::filesystem::absolute( inFilePath ); - std::string inFileNormalAbsPathStr = NormalizePath( inFilePathAbs.generic_string() ); + // Make sure we work with the lexically normalized absolute path representation of the input file + // In case the inFilePath is not already absolute, we'll set the base path to the "params.directory". + // - This is because when called from ResourceGroup::ResourceGroupImpl::CreateFromDirectory() function + // it will be iterating over files from that location. + // If it is already absolute, then fine. If not, we will resolve it relative to whatever the + // "operational base path" is for this ResourceFilter (i.e. "params.directory" when called from CLI). + std::string inFileNormalAbsPathStr = NormalizePath( GetAbsolutePathFromBase( inFilePath, m_operationalBasePath ).generic_string() ); + std::filesystem::path inFilePathAbs = std::filesystem::absolute( inFileNormalAbsPathStr ); // Priority: lower is higher priority: // -1 = exact match on filename (or folder) @@ -109,10 +128,9 @@ bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::p const auto& resolvedPathMap = GetFullResolvedPathMap(); for( const auto& [resolvedRelativePathStr, filter] : resolvedPathMap ) { - // Use normalized absolute paths for comparison - std::filesystem::path resolvedRelativePath( resolvedRelativePathStr ); - std::filesystem::path resolvedPathAbs = std::filesystem::absolute( resolvedRelativePath ); - std::string resolvedNormalAbsPathStr = NormalizePath( resolvedPathAbs.generic_string() ); + // Convert the relative path to a lexically normalized absolute path for comparison + std::string resolvedNormalAbsPathStr = NormalizePath( GetAbsolutePathFromBase( resolvedRelativePathStr, m_filterFilesAbsoluteBasePath ).generic_string() ); + std::filesystem::path resolvedPathAbs = std::filesystem::absolute( resolvedNormalAbsPathStr ); if( resolvedNormalAbsPathStr == inFileNormalAbsPathStr ) { @@ -142,15 +160,11 @@ bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::p // Perform Wildcard matching on the normalized absolute paths if( !WildcardMatch( resolvedNormalAbsPathStr, inFileNormalAbsPathStr ) ) { - // There was NO wildcard match on paths, ignore this resolvedRelativePath entry + // There was NO wildcard match on paths, ignore it continue; } // There is a Wildcard match - determine the folder depth difference - // Reset the path variables to their Normalized absolute path components (in case they differ from non-Normalized version) - inFilePathAbs = std::filesystem::absolute( inFileNormalAbsPathStr ); - resolvedPathAbs = std::filesystem::absolute( resolvedNormalAbsPathStr ); - auto inFileIt = inFilePathAbs.begin(); auto resolvedIt = resolvedPathAbs.begin(); while( inFileIt != inFilePathAbs.end() && resolvedIt != resolvedPathAbs.end() && *inFileIt == *resolvedIt ) @@ -324,4 +338,27 @@ std::string ResourceFilter::NormalizePath( const std::string& path ) return p.lexically_normal().generic_string(); } +// ------------------------------------------------------------- +// Description: +// Static helper function to get an absolute path by combining a +// relative path with an absolute base path (if not already absolute). +// Arguments: +// relativeOrAbsolutePath - the input file path that is either relative or absolute +// absoluteBasePath - the absolute base path to use (if needed) +// Return Value: +// Guaranteed absolute file path +// ------------------------------------------------------------- +std::filesystem::path ResourceFilter::GetAbsolutePathFromBase( const std::filesystem::path& relativeOrAbsolutePath, + const std::filesystem::path& absoluteBasePath ) +{ + if( relativeOrAbsolutePath.is_absolute() ) + { + // Path is already absolute, just return it + return relativeOrAbsolutePath; + } + + // Path is relative, combine it with the absolute base path + return absoluteBasePath / relativeOrAbsolutePath; +} + } // namespace ResourceTools From 4ffe5eb2886922b3f91d9190e823c18c4e71a81b Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Mon, 16 Feb 2026 13:38:45 +0000 Subject: [PATCH 117/124] Remove the CurrentWorkingDirectoryChanger helper class. - It is no longer needed after reliance on current working directory was removed. --- tests/src/ResourcesTestFixture.h | 56 -------------------------------- 1 file changed, 56 deletions(-) diff --git a/tests/src/ResourcesTestFixture.h b/tests/src/ResourcesTestFixture.h index 6198fb3..94fbf0e 100644 --- a/tests/src/ResourcesTestFixture.h +++ b/tests/src/ResourcesTestFixture.h @@ -25,60 +25,4 @@ struct ResourcesTestFixture : public ::testing::Test bool DirectoryIsSubset( const std::filesystem::path& dir1, const std::filesystem::path& dir2 ); // Test that all files in dir1 exist in dir2, and the contents of the files in both directories are the same. }; -// ------------------------------------------------------------- -// Description: -// CurrentWorkingDirectoryChanger is a RAII helper class that changes -// the current working directory to a specified path on construction -// and restores the original working directory once destructed. -// As long as the CurrentWorkingDirectoryChanger object is in scope, -// the working directory remains changed to its new value. -// ------------------------------------------------------------- -class CurrentWorkingDirectoryChanger -{ -public: - // Constructor saves the current working directory and changes it to the new path - explicit CurrentWorkingDirectoryChanger( const std::filesystem::path& new_path ) : - m_original_path( std::filesystem::current_path() ) - { - try - { - std::cout << "WorkingDirectory - before change: " << m_original_path.generic_string() << std::endl; - std::filesystem::current_path( new_path ); - std::cout << "WorkingDirectory - after change: " << std::filesystem::current_path().generic_string() - << std::endl; - } - catch( const std::filesystem::filesystem_error& e ) - { - std::cerr << "Error changing working directory: " << e.what() << std::endl; - } - } - - // Destructor restores working directory to the original path - ~CurrentWorkingDirectoryChanger() - { - try - { - std::filesystem::current_path( m_original_path ); - std::cout << "WorkingDirectory - restored to: " << m_original_path.generic_string() << std::endl; - } - catch( const std::filesystem::filesystem_error& e ) - { - std::cerr << "Error restoring working directory: " << e.what() << std::endl; - } - } - - // Disable copy and move operations - CurrentWorkingDirectoryChanger( const CurrentWorkingDirectoryChanger& ) = delete; - - CurrentWorkingDirectoryChanger& operator=( const CurrentWorkingDirectoryChanger& ) = delete; - - CurrentWorkingDirectoryChanger( CurrentWorkingDirectoryChanger&& ) = delete; - - CurrentWorkingDirectoryChanger& operator=( CurrentWorkingDirectoryChanger&& ) = delete; - -private: - // Store the original working directory path to restore upon class destruction - std::filesystem::path m_original_path; -}; - #endif // CarbonResourcesTestFixture_H \ No newline at end of file From 6d75e6e3c8f81a856088e25ebcae80ea6b4df111 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 17 Feb 2026 11:23:32 +0000 Subject: [PATCH 118/124] Rename cli argument for clarity - Rename --filter-file-basepath to --filter-file-prefixmap-basepath - Fix plural to singular typo. --- cli/src/CreateResourceGroupCliOperation.cpp | 6 +++--- tests/src/ResourcesCliTest.cpp | 24 ++++++++++----------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/cli/src/CreateResourceGroupCliOperation.cpp b/cli/src/CreateResourceGroupCliOperation.cpp index ff34045..4273c13 100644 --- a/cli/src/CreateResourceGroupCliOperation.cpp +++ b/cli/src/CreateResourceGroupCliOperation.cpp @@ -17,7 +17,7 @@ CreateResourceGroupCliOperation::CreateResourceGroupCliOperation() : m_createResourceGroupExportResourcesDestinationTypeId( "--export-resources-destination-type" ), m_createResourceGroupExportResourcesDestinationPathId( "--export-resources-destination-path" ), m_createResourceGroupIniFilterFilesArgumentId( "--filter-file" ), - m_createResourceGroupIniFilterFilesBasePathArgumentId( "--filter-file-basepath" ) + m_createResourceGroupIniFilterFilesBasePathArgumentId( "--filter-file-prefixmap-basepath" ) { AddRequiredPositionalArgument( m_createResourceGroupPathArgumentId, "Base directory to create resource group from." ); @@ -43,9 +43,9 @@ CreateResourceGroupCliOperation::CreateResourceGroupCliOperation() : AddArgument( m_createResourceGroupExportResourcesDestinationPathId, "Represents the base path where the exported resources will be saved. Requires --export-resources", false, false, defaultImportParams.exportResourcesDestinationSettings.basePath.string() ); - AddArgument( m_createResourceGroupIniFilterFilesArgumentId, "Path to INI file(s) for resource filtering.", false, true, "" ); + AddArgument( m_createResourceGroupIniFilterFilesArgumentId, "Path to INI file for resource filtering.", false, true, "" ); - AddArgument( m_createResourceGroupIniFilterFilesBasePathArgumentId, "Base directory for resolving relative paths contained within filter INI file(s).", false, false, "" ); + AddArgument( m_createResourceGroupIniFilterFilesBasePathArgumentId, "Base directory for resolving relative paths contained within filter INI file(s) prefixmap attribute.", false, false, "" ); } bool CreateResourceGroupCliOperation::Execute( std::string& returnErrorMessage ) const diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index 62d2502..bf88d3d 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -307,7 +307,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1_YamlOutput arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); arguments.push_back( filterIniFilePath.lexically_normal().string() ); - arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( "--filter-file-prefixmap-basepath" ); arguments.push_back( TEST_DATA_BASE_PATH ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); @@ -351,7 +351,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1_CsvTxtOutp arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); arguments.push_back( filterIniFilePath.lexically_normal().string() ); - arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( "--filter-file-prefixmap-basepath" ); arguments.push_back( TEST_DATA_BASE_PATH ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); @@ -395,7 +395,7 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmWorks_UsingFilter_validSimpleExampl arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); arguments.push_back( filterIniFilePath.lexically_normal().string() ); - arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( "--filter-file-prefixmap-basepath" ); arguments.push_back( TEST_DATA_BASE_PATH ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); @@ -439,7 +439,7 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmWorks_UsingFilter_validSimpleExampl arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); arguments.push_back( filterIniFilePath.lexically_normal().string() ); - arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( "--filter-file-prefixmap-basepath" ); arguments.push_back( TEST_DATA_BASE_PATH ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); @@ -483,7 +483,7 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFails_UsingFilter_validSimpleExampl arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); arguments.push_back( filterIniFilePath.lexically_normal().string() ); - arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( "--filter-file-prefixmap-basepath" ); arguments.push_back( "Some/Incorrect/Base/Path" ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); @@ -521,7 +521,7 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFails_UsingFilter_validSimpleExampl arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); arguments.push_back( filterIniFilePath.lexically_normal().string() ); - arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( "--filter-file-prefixmap-basepath" ); arguments.push_back( "Some/Incorrect/Base/Path" ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); @@ -637,7 +637,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1_YamlOutpu arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); arguments.push_back( filterIniFilePath.lexically_normal().string() ); - arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( "--filter-file-prefixmap-basepath" ); arguments.push_back( TEST_DATA_BASE_PATH ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); @@ -681,7 +681,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1_CsvTxtOut arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); arguments.push_back( filterIniFilePath.lexically_normal().string() ); - arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( "--filter-file-prefixmap-basepath" ); arguments.push_back( TEST_DATA_BASE_PATH ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); @@ -731,7 +731,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1_ arguments.push_back( "--filter-file" ); arguments.push_back( filterFilePath.lexically_normal().string() ); } - arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( "--filter-file-prefixmap-basepath" ); arguments.push_back( TEST_DATA_BASE_PATH ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); @@ -781,7 +781,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1_ arguments.push_back( "--filter-file" ); arguments.push_back( filterFilePath.lexically_normal().string() ); } - arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( "--filter-file-prefixmap-basepath" ); arguments.push_back( TEST_DATA_BASE_PATH ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); @@ -825,7 +825,7 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFailureParsingWronglyFormattedIniFi arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); arguments.push_back( filterIniFilePath.lexically_normal().string() ); - arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( "--filter-file-prefixmap-basepath" ); arguments.push_back( TEST_DATA_BASE_PATH ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); @@ -862,7 +862,7 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFailureUsingNoExistentFilterFile_in arguments.push_back( "3" ); arguments.push_back( "--filter-file" ); arguments.push_back( filterIniFilePath.lexically_normal().string() ); - arguments.push_back( "--filter-file-basepath" ); + arguments.push_back( "--filter-file-prefixmap-basepath" ); arguments.push_back( TEST_DATA_BASE_PATH ); arguments.push_back( "--output-file" ); arguments.push_back( outputFilePath.lexically_normal().string() ); From 45de3985c39b735a6502d54f1d498034672cc81e Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 17 Feb 2026 11:43:05 +0000 Subject: [PATCH 119/124] Remove workingDirectory default parameter from RunCli() --- tests/src/CliTestFixture.cpp | 5 ++--- tests/src/CliTestFixture.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/src/CliTestFixture.cpp b/tests/src/CliTestFixture.cpp index 1c83f29..43fcdd2 100644 --- a/tests/src/CliTestFixture.cpp +++ b/tests/src/CliTestFixture.cpp @@ -8,8 +8,7 @@ int CliTestFixture::RunCli( std::vector& arguments, std::string* standardOutput /* = nullptr */, - std::string* errorOutput /* = nullptr */, - const std::string& workingDirectory /* = "" (empty = do not alter it) */ ) + std::string* errorOutput /* = nullptr */ ) { arguments.insert( arguments.begin(), CARBON_RESOURCES_CLI_EXE_FULLPATH ); @@ -23,7 +22,7 @@ int CliTestFixture::RunCli( std::vector& arguments, // Only populate the output and errorOutput if the caller provided non-nullptr for them, otherwise discard them TinyProcessLib::Process process1a( arguments, - workingDirectory, + "", [standardOutput]( const char* bytes, size_t n ) { if (standardOutput != nullptr) { *standardOutput += std::string( bytes, n ); } }, [errorOutput]( const char* bytes, size_t n ) { if (errorOutput != nullptr) { *errorOutput += std::string( bytes, n ); } } ); diff --git a/tests/src/CliTestFixture.h b/tests/src/CliTestFixture.h index 71bc6df..d904fe2 100644 --- a/tests/src/CliTestFixture.h +++ b/tests/src/CliTestFixture.h @@ -12,7 +12,7 @@ struct CliTestFixture : public ResourcesTestFixture { - int RunCli( std::vector& arguments, std::string* standardOutput = nullptr, std::string* errorOutput = nullptr, const std::string& workingDirectory = "" ); + int RunCli( std::vector& arguments, std::string* standardOutput = nullptr, std::string* errorOutput = nullptr ); // Helper to remove files as part of a test run and cleanup void RemoveFiles( const std::vector& filesToRemove ); From d0dc0ea156a681e36d115ec4e08833fd0caafe4b Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 17 Feb 2026 11:53:47 +0000 Subject: [PATCH 120/124] Simplify inputDirectoryPath in CreateGroup CLI tests --- tests/src/ResourcesCliTest.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index bf88d3d..43572fd 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -294,7 +294,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1_YamlOutput std::string output; std::string errorOutput; std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_validSimpleExample1.yaml" ); std::filesystem::path filterIniFilePath = "ExampleIniFiles/validSimpleExample1.ini"; @@ -338,7 +338,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1_CsvTxtOutp std::string output; std::string errorOutput; std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_validSimpleExample1.txt" ); std::filesystem::path filterIniFilePath = "ExampleIniFiles/validSimpleExample1.ini"; @@ -382,7 +382,7 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmWorks_UsingFilter_validSimpleExampl std::string output; std::string errorOutput; std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_ConfirmWorks_UsingFilter_validSimpleExample1_WithRelativeFilterFilePathAndBasePathSet.txt" ); std::filesystem::path filterIniFilePath = "ExampleIniFiles/validSimpleExample1.ini"; @@ -426,7 +426,7 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmWorks_UsingFilter_validSimpleExampl std::string output; std::string errorOutput; std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_ConfirmWorks_UsingFilter_validSimpleExample1_WithAbsoluteFilterFilePathAndBasePathSet.txt" ); std::filesystem::path filterIniFilePath = GetTestFileFileAbsolutePath( "ExampleIniFiles/validSimpleExample1.ini" ); @@ -470,7 +470,7 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFails_UsingFilter_validSimpleExampl std::string output; std::string errorOutput; std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_ConfirmFails_UsingFilter_validSimpleExample1_WithRelativeFilterFilePathAndWrongBasePathSet.txt" ); std::filesystem::path filterIniFilePath = "ExampleIniFiles/validSimpleExample1.ini"; @@ -508,7 +508,7 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFails_UsingFilter_validSimpleExampl std::string output; std::string errorOutput; std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_ConfirmFails_UsingFilter_validSimpleExample1_WithAbsoluteFilterFilePathAndWrongBasePathSet.txt" ); std::filesystem::path filterIniFilePath = GetTestFileFileAbsolutePath( "ExampleIniFiles/validSimpleExample1.ini" ); @@ -549,7 +549,7 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFails_UsingFilter_validSimpleExampl std::string output; std::string errorOutput; std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_ConfirmFails_UsingFilter_validSimpleExample1_WithRelativeFilterFilePathAndNoBasePath.txt" ); std::filesystem::path filterIniFilePath = "ExampleIniFiles/validSimpleExample1.ini"; @@ -585,7 +585,7 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFails_UsingFilter_validSimpleExampl std::string output; std::string errorOutput; std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_ConfirmFails_UsingFilter_validSimpleExample1_WithAbsoluteFilterFilePathAndNoBasePath.txt" ); std::filesystem::path filterIniFilePath = GetTestFileFileAbsolutePath( "ExampleIniFiles/validSimpleExample1.ini" ); @@ -624,7 +624,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1_YamlOutpu std::string output; std::string errorOutput; std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_validComplexExample1.yaml" ); std::filesystem::path filterIniFilePath = "ExampleIniFiles/validComplexExample1.ini"; @@ -668,7 +668,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1_CsvTxtOut std::string output; std::string errorOutput; std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_validComplexExample1.txt" ); std::filesystem::path filterIniFilePath = "ExampleIniFiles/validComplexExample1.ini"; @@ -712,7 +712,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1_ std::string output; std::string errorOutput; std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_validSimpleAndComplexExample1.yaml" ); std::vector filterIniFilePaths = { "ExampleIniFiles/validSimpleExample1.ini", @@ -762,7 +762,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1_ std::string output; std::string errorOutput; std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_validSimpleAndComplexExample1.txt" ); std::vector filterIniFilePaths = { "ExampleIniFiles/validSimpleExample1.ini", @@ -812,7 +812,7 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFailureParsingWronglyFormattedIniFi std::string output; std::string errorOutput; std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_invalidMissingNamedSection.yaml" ); std::filesystem::path filterIniFilePath = "ExampleIniFiles/invalidMissingNamedSection.ini"; @@ -849,7 +849,7 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFailureUsingNoExistentFilterFile_in std::string output; std::string errorOutput; std::vector arguments; - std::filesystem::path inputDirectoryPath = GetTestFileFileAbsolutePath( "" ); // The base testData directory + std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_iniFileDoesNotExist.yaml" ); std::filesystem::path filterIniFilePath = "ExampleIniFiles/iniFileNotFound.ini"; From 4977e2fcb0383ebc99dfe4a0b33088817fc9e5db Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 17 Feb 2026 12:26:07 +0000 Subject: [PATCH 121/124] Rename filterFilePrefixmapBaseDirectory members Remove ambiguity of the intention of those: - resourceFilterIniFilesPrefixmapBaseDirectory - m_createResourceGroupIniFilterFilesPrefixmapBasePathArgumentId --- cli/src/CreateResourceGroupCliOperation.cpp | 8 ++++---- cli/src/CreateResourceGroupCliOperation.h | 2 +- include/ResourceGroup.h | 6 +++--- src/ResourceGroupImpl.cpp | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cli/src/CreateResourceGroupCliOperation.cpp b/cli/src/CreateResourceGroupCliOperation.cpp index 4273c13..79f3b11 100644 --- a/cli/src/CreateResourceGroupCliOperation.cpp +++ b/cli/src/CreateResourceGroupCliOperation.cpp @@ -17,7 +17,7 @@ CreateResourceGroupCliOperation::CreateResourceGroupCliOperation() : m_createResourceGroupExportResourcesDestinationTypeId( "--export-resources-destination-type" ), m_createResourceGroupExportResourcesDestinationPathId( "--export-resources-destination-path" ), m_createResourceGroupIniFilterFilesArgumentId( "--filter-file" ), - m_createResourceGroupIniFilterFilesBasePathArgumentId( "--filter-file-prefixmap-basepath" ) + m_createResourceGroupIniFilterFilesPrefixmapBasePathArgumentId( "--filter-file-prefixmap-basepath" ) { AddRequiredPositionalArgument( m_createResourceGroupPathArgumentId, "Base directory to create resource group from." ); @@ -45,7 +45,7 @@ CreateResourceGroupCliOperation::CreateResourceGroupCliOperation() : AddArgument( m_createResourceGroupIniFilterFilesArgumentId, "Path to INI file for resource filtering.", false, true, "" ); - AddArgument( m_createResourceGroupIniFilterFilesBasePathArgumentId, "Base directory for resolving relative paths contained within filter INI file(s) prefixmap attribute.", false, false, "" ); + AddArgument( m_createResourceGroupIniFilterFilesPrefixmapBasePathArgumentId, "Base directory for resolving relative paths contained within filter INI file(s) prefixmap attribute.", false, false, "" ); } bool CreateResourceGroupCliOperation::Execute( std::string& returnErrorMessage ) const @@ -102,7 +102,7 @@ bool CreateResourceGroupCliOperation::Execute( std::string& returnErrorMessage ) } } - createResourceGroupParams.resourceFilterIniFilesBaseDirectory = m_argumentParser->get( m_createResourceGroupIniFilterFilesBasePathArgumentId ); + createResourceGroupParams.resourceFilterIniFilesPrefixmapBaseDirectory = m_argumentParser->get( m_createResourceGroupIniFilterFilesPrefixmapBasePathArgumentId ); } if (ShowCliStatusUpdates()) @@ -160,7 +160,7 @@ void CreateResourceGroupCliOperation::PrintStartBanner( std::cout << " - " << iniPath.generic_string() << std::endl; } - std::cout << "Base Directory for Resolving Relative Paths in Filter INI File(s): " << createResourceGroupFromDirectoryParams.resourceFilterIniFilesBaseDirectory << std::endl; + std::cout << "Base Directory for Resolving Relative Paths in Filter INI File(s): " << createResourceGroupFromDirectoryParams.resourceFilterIniFilesPrefixmapBaseDirectory << std::endl; } else { diff --git a/cli/src/CreateResourceGroupCliOperation.h b/cli/src/CreateResourceGroupCliOperation.h index f43566e..6a4f39a 100644 --- a/cli/src/CreateResourceGroupCliOperation.h +++ b/cli/src/CreateResourceGroupCliOperation.h @@ -45,7 +45,7 @@ class CreateResourceGroupCliOperation : public CliOperation std::string m_createResourceGroupIniFilterFilesArgumentId; - std::string m_createResourceGroupIniFilterFilesBasePathArgumentId; + std::string m_createResourceGroupIniFilterFilesPrefixmapBasePathArgumentId; }; #endif // CreateResourceGroupCliOperation_H \ No newline at end of file diff --git a/include/ResourceGroup.h b/include/ResourceGroup.h index ed1e85d..db3f09c 100644 --- a/include/ResourceGroup.h +++ b/include/ResourceGroup.h @@ -219,8 +219,8 @@ struct ResourceGroupExportToFileParams * @see CreateResourceGroupFromDirectoryParams::exportResources * @var CreateResourceGroupFromDirectoryParams::resourceFilterIniFiles * List of INI file(s) containing include/exclude rules for resource filtering - * @var CreateResourceGroupFromDirectoryParams::resourceFilterIniFilesBaseDirectory - * Base directory to resolve any relative paths contained within resourceFilterIniFiles. + * @var CreateResourceGroupFromDirectoryParams::resourceFilterIniFilesPrefixmapBaseDirectory + * Base directory for resolving relative paths contained within resourceFilterIniFiles prefixmap attribute */ struct CreateResourceGroupFromDirectoryParams { @@ -245,7 +245,7 @@ struct CreateResourceGroupFromDirectoryParams std::vector resourceFilterIniFiles = {}; - std::filesystem::path resourceFilterIniFilesBaseDirectory = ""; + std::filesystem::path resourceFilterIniFilesPrefixmapBaseDirectory = ""; }; /** @struct ResourceGroupMergeParams diff --git a/src/ResourceGroupImpl.cpp b/src/ResourceGroupImpl.cpp index db2fddb..4dad7e1 100644 --- a/src/ResourceGroupImpl.cpp +++ b/src/ResourceGroupImpl.cpp @@ -71,7 +71,7 @@ Result ResourceGroup::ResourceGroupImpl::CreateFromDirectory( const CreateResour { try { - resourceFilter.Initialize( params.resourceFilterIniFiles, params.resourceFilterIniFilesBaseDirectory, params.directory ); + resourceFilter.Initialize( params.resourceFilterIniFiles, params.resourceFilterIniFilesPrefixmapBaseDirectory, params.directory ); } catch( const std::exception& e ) { From 0a811ed09555c05535e19b78b9b9d18f82aab908 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 17 Feb 2026 15:10:23 +0000 Subject: [PATCH 122/124] Simplify ResourceFilter.Initialize() - Only send in the prefixmapAbsoluteBasePath along with the vector of .ini files. --- src/ResourceGroupImpl.cpp | 2 +- tests/src/ResourceToolsFilterTest.cpp | 21 +++++++------- tests/src/ResourcesCliTest.cpp | 28 ++++++++++-------- tools/include/ResourceFilter.h | 32 +++++++++------------ tools/src/FilterResourceFile.cpp | 2 +- tools/src/ResourceFilter.cpp | 41 ++++++++++++--------------- 6 files changed, 60 insertions(+), 66 deletions(-) diff --git a/src/ResourceGroupImpl.cpp b/src/ResourceGroupImpl.cpp index 4dad7e1..71312b0 100644 --- a/src/ResourceGroupImpl.cpp +++ b/src/ResourceGroupImpl.cpp @@ -71,7 +71,7 @@ Result ResourceGroup::ResourceGroupImpl::CreateFromDirectory( const CreateResour { try { - resourceFilter.Initialize( params.resourceFilterIniFiles, params.resourceFilterIniFilesPrefixmapBaseDirectory, params.directory ); + resourceFilter.Initialize( params.resourceFilterIniFiles, params.resourceFilterIniFilesPrefixmapBaseDirectory ); } catch( const std::exception& e ) { diff --git a/tests/src/ResourceToolsFilterTest.cpp b/tests/src/ResourceToolsFilterTest.cpp index cbe32d4..ba5be46 100644 --- a/tests/src/ResourceToolsFilterTest.cpp +++ b/tests/src/ResourceToolsFilterTest.cpp @@ -1961,7 +1961,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_ConfirmFileLoadFailure_SingleIniFileTh ResourceTools::ResourceFilter resourceFilter; try { - resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); + resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH ); FAIL() << "Expected this test to fail: ResourceFilter_Load_SingleFile_ThatDoesNotExist"; } catch( const std::exception& e ) @@ -1987,7 +1987,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_ConfirmFileLoadFailure_MultipleFilesOn ResourceTools::ResourceFilter resourceFilter; try { - resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); + resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH ); FAIL() << "Expected this test to fail: ResourceFilter_Load_MultipleFiles_OneThatDoesNotExist"; } catch( const std::exception& e ) @@ -2012,7 +2012,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_ConfirmSuccessfulFileLoad_example1_ini ResourceTools::ResourceFilter resourceFilter; try { - resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); + resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH ); ASSERT_TRUE( true ); // If we got here, the file loaded successfully without any exceptions } catch( const std::exception& e ) @@ -2034,7 +2034,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingRelativ const std::filesystem::path iniPath1 = "ExampleIniFiles/validSimpleExample1.ini"; std::vector paths = { iniPath1 }; ResourceTools::ResourceFilter resourceFilter; - resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); + resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH ); // Validate correct included paths via the resourceFilter // Always compare those based on absolute paths: @@ -2079,10 +2079,9 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingAbsolut try { const std::filesystem::path iniPath1 = GetTestFileFileAbsolutePath( "ExampleIniFiles/validSimpleExample1.ini" ); - //std::filesystem::path iniPath1Abs = std::filesystem::absolute( iniPath1 ); std::vector paths = { iniPath1 }; ResourceTools::ResourceFilter resourceFilter; - resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); + resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH ); // Validate correct included paths via the resourceFilter (compare absolute paths): std::set validResolvedAbsolutePaths = { @@ -2128,7 +2127,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingRelativ const std::filesystem::path iniPath1 = "ExampleIniFiles/validComplexExample1.ini"; std::vector paths = { iniPath1 }; ResourceTools::ResourceFilter resourceFilter; - resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); + resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH ); // Validate correct included paths via the resourceFilter (compare absolute paths): std::set validResolvedRelativePaths = { @@ -2236,7 +2235,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulFileLoadUsingAbsolut const std::filesystem::path iniPath1 = GetTestFileFileAbsolutePath( "ExampleIniFiles/validComplexExample1.ini" ); std::vector pathsAbs = { iniPath1 }; ResourceTools::ResourceFilter resourceFilter; - resourceFilter.Initialize( pathsAbs, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); + resourceFilter.Initialize( pathsAbs, TEST_DATA_BASE_PATH ); // Validate correct included paths via the resourceFilter: std::set validResolvedRelativePaths = { @@ -2348,7 +2347,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccessfulLoadOf2IniFiles_vali GetTestFileFileAbsolutePath( "ExampleIniFiles/validSimpleExample1.ini" ) }; ResourceTools::ResourceFilter resourceFilter; - resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); + resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH ); // Validate correct included paths via the resourceFilter: std::set validResolvedRelativePaths = { @@ -2470,7 +2469,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccess_FileLoadOverrideUsingD const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/validOverrideDifferentRelativeSameAbsolutePaths.ini" ); std::vector paths = { iniPath }; ResourceTools::ResourceFilter resourceFilter; - resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); + resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH ); // Validate correct included and exclude paths via the resourceFilter: // - Because of the two distinct relative paths (./resourcesOnBranch/* and /resourcesOnBranch/*) @@ -2540,7 +2539,7 @@ TEST_F( ResourceToolsTest, ResourceFilter_ValidateSuccess_FileLoadRespathInlineO const std::filesystem::path iniPath = GetTestFileFileAbsolutePath( "ExampleIniFiles/validInlineFilterOverrideOnSameRelativePath.ini" ); std::vector paths = { iniPath }; ResourceTools::ResourceFilter resourceFilter; - resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH, std::filesystem::current_path() ); + resourceFilter.Initialize( paths, TEST_DATA_BASE_PATH ); // Validate correct included and exclude paths via the resourceFilter: // - Because of the overriding rules for the identical "respath" path entries. diff --git a/tests/src/ResourcesCliTest.cpp b/tests/src/ResourcesCliTest.cpp index 43572fd..cb100fc 100644 --- a/tests/src/ResourcesCliTest.cpp +++ b/tests/src/ResourcesCliTest.cpp @@ -296,7 +296,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1_YamlOutput std::vector arguments; std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_validSimpleExample1.yaml" ); - std::filesystem::path filterIniFilePath = "ExampleIniFiles/validSimpleExample1.ini"; + std::filesystem::path filterIniFilePath = GetTestFileFileAbsolutePath( "ExampleIniFiles/validSimpleExample1.ini" ); // Ensure any previous test output files are removed RemoveFiles( { outputFilePath } ); @@ -340,7 +340,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleExample1_CsvTxtOutp std::vector arguments; std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_validSimpleExample1.txt" ); - std::filesystem::path filterIniFilePath = "ExampleIniFiles/validSimpleExample1.ini"; + std::filesystem::path filterIniFilePath = GetTestFileFileAbsolutePath( "ExampleIniFiles/validSimpleExample1.ini" ); // Ensure any previous test output files are removed RemoveFiles( { outputFilePath } ); @@ -493,10 +493,11 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFails_UsingFilter_validSimpleExampl int res = RunCli( arguments, &output, &errorOutput ); std::cout << "--- RunCli() output: ---" << std::endl; std::cout << output << std::endl; + std::cout << "errorOutput: " << errorOutput << std::endl; std::cout << "------------------------" << std::endl; // Should fail, expecting non-zero exit code - ASSERT_EQ( res, 1 ) << "CLI operation should fail for a reading relative filter .ini files with wrong --filter-base-path parameter - with resultCode=1"; + ASSERT_EQ( res, 1 ) << "CLI operation should fail for a reading relative filter .ini files with wrong --filter-file-prefixmap-basepath - with resultCode=1"; // Check for expected error message EXPECT_TRUE( errorOutput.find( "[ERROR: Failed to initialize ResourceFilter from .ini file]" ) != std::string::npos ) << "Expected generic (top-level) error message about failure to initialize ResourceFilter from .ini file. Actual error: " << errorOutput; @@ -531,6 +532,7 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFails_UsingFilter_validSimpleExampl int res = RunCli( arguments, &output, &errorOutput ); std::cout << "--- RunCli() output: ---" << std::endl; std::cout << output << std::endl; + std::cout << "errorOutput: " << errorOutput << std::endl; std::cout << "------------------------" << std::endl; ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; @@ -570,10 +572,11 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFails_UsingFilter_validSimpleExampl int res = RunCli( arguments, &output, &errorOutput ); std::cout << "--- RunCli() output: ---" << std::endl; std::cout << output << std::endl; + std::cout << "errorOutput: " << errorOutput << std::endl; std::cout << "------------------------" << std::endl; // Should fail, expecting non-zero exit code - ASSERT_EQ( res, 1 ) << "CLI operation should fail for a reading relative filter .ini files with wrong --filter-base-path parameter - with resultCode=1"; + ASSERT_EQ( res, 1 ) << "CLI operation should fail for a reading relative filter .ini files with missing --filter-file-prefixmap-basepath - with resultCode=1"; // Check for expected error message EXPECT_TRUE( errorOutput.find( "[ERROR: Failed to initialize ResourceFilter from .ini file]" ) != std::string::npos ) << "Expected generic (top-level) error message about failure to initialize ResourceFilter from .ini file. Actual error: " << errorOutput; @@ -606,6 +609,7 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFails_UsingFilter_validSimpleExampl int res = RunCli( arguments, &output, &errorOutput ); std::cout << "--- RunCli() output: ---" << std::endl; std::cout << output << std::endl; + std::cout << "errorOutput: " << errorOutput << std::endl; std::cout << "------------------------" << std::endl; ASSERT_EQ( res, 0 ) << "CLI operation failed, errorOutput: " << errorOutput; @@ -626,7 +630,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1_YamlOutpu std::vector arguments; std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_validComplexExample1.yaml" ); - std::filesystem::path filterIniFilePath = "ExampleIniFiles/validComplexExample1.ini"; + std::filesystem::path filterIniFilePath = GetTestFileFileAbsolutePath( "ExampleIniFiles/validComplexExample1.ini" ); // Ensure any previous test output files are removed RemoveFiles( { outputFilePath } ); @@ -670,7 +674,7 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validComplexExample1_CsvTxtOut std::vector arguments; std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_validComplexExample1.txt" ); - std::filesystem::path filterIniFilePath = "ExampleIniFiles/validComplexExample1.ini"; + std::filesystem::path filterIniFilePath = GetTestFileFileAbsolutePath( "ExampleIniFiles/validComplexExample1.ini" ); // Ensure any previous test output files are removed RemoveFiles( { outputFilePath } ); @@ -715,8 +719,8 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1_ std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_validSimpleAndComplexExample1.yaml" ); std::vector filterIniFilePaths = { - "ExampleIniFiles/validSimpleExample1.ini", - "ExampleIniFiles/validComplexExample1.ini" + GetTestFileFileAbsolutePath( "ExampleIniFiles/validSimpleExample1.ini" ), + GetTestFileFileAbsolutePath( "ExampleIniFiles/validComplexExample1.ini" ) }; // Ensure any previous test output files are removed @@ -765,8 +769,8 @@ TEST_F( ResourcesCliTest, CreateGroup_UsingFilter_validSimpleAndComplexExample1_ std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_validSimpleAndComplexExample1.txt" ); std::vector filterIniFilePaths = { - "ExampleIniFiles/validSimpleExample1.ini", - "ExampleIniFiles/validComplexExample1.ini" + GetTestFileFileAbsolutePath( "ExampleIniFiles/validSimpleExample1.ini" ), + GetTestFileFileAbsolutePath( "ExampleIniFiles/validComplexExample1.ini" ) }; // Ensure any previous test output files are removed @@ -814,7 +818,7 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFailureParsingWronglyFormattedIniFi std::vector arguments; std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_invalidMissingNamedSection.yaml" ); - std::filesystem::path filterIniFilePath = "ExampleIniFiles/invalidMissingNamedSection.ini"; + std::filesystem::path filterIniFilePath = GetTestFileFileAbsolutePath( "ExampleIniFiles/invalidMissingNamedSection.ini" ); // Ensure any previous test output files are removed RemoveFiles( { outputFilePath } ); @@ -851,7 +855,7 @@ TEST_F( ResourcesCliTest, CreateGroup_ConfirmFailureUsingNoExistentFilterFile_in std::vector arguments; std::filesystem::path inputDirectoryPath = TEST_DATA_BASE_PATH; std::filesystem::path outputFilePath = std::filesystem::absolute( "CliFilterCreateGroupOut/CreateGroup_UsingFilter_iniFileDoesNotExist.yaml" ); - std::filesystem::path filterIniFilePath = "ExampleIniFiles/iniFileNotFound.ini"; + std::filesystem::path filterIniFilePath = GetTestFileFileAbsolutePath( "ExampleIniFiles/iniFileNotFound.ini" ); // Ensure any previous test output files are removed RemoveFiles( { outputFilePath } ); diff --git a/tools/include/ResourceFilter.h b/tools/include/ResourceFilter.h index c01b472..4457a5a 100644 --- a/tools/include/ResourceFilter.h +++ b/tools/include/ResourceFilter.h @@ -25,15 +25,15 @@ class ResourceFilter public: ResourceFilter() = default; - // Construct a ResourceFilter object by passing it a list of filter .ini file(s) and relevant paths. - explicit ResourceFilter( const std::vector& iniFilePaths, - const std::filesystem::path& filterFilesAbsoluteBasePath, - const std::filesystem::path& operationalBasePath ); + // Construct a ResourceFilter object by passing it a list of filter .ini file(s) and prefixmap base path. + explicit ResourceFilter( + const std::vector& iniFilePaths, + const std::filesystem::path& prefixmapAbsoluteBasePath ); - // Initializes the ResourceFilter by passing in and parsing the supplied filter .ini file(s) and relevant paths. - void Initialize( const std::vector& iniFilePaths, - const std::filesystem::path& filterFilesAbsoluteBasePath, - const std::filesystem::path& operationalBasePath ); + // Initializes the ResourceFilter by passing in and parsing the supplied filter .ini file(s) and prefixmap base path. + void Initialize( + const std::vector& iniFilePaths, + const std::filesystem::path& prefixmapAbsoluteBasePath ); // Returns true if this ResourceFilter has any filter .ini files, false otherwise. bool HasFilters() const; @@ -57,20 +57,16 @@ class ResourceFilter static std::string NormalizePath( const std::string& path ); // Static helper function to get an absolute path by combining a relative path with an absolute base path (if not already absolute) - static std::filesystem::path GetAbsolutePathFromBase( const std::filesystem::path& relativeOrAbsolutePath, - const std::filesystem::path& absoluteBasePath ); + static std::filesystem::path GetAbsolutePathFromBase( + const std::filesystem::path& relativeOrAbsolutePath, + const std::filesystem::path& absoluteBasePath ); // A flag used to prevent multiple initializations of the ResourceFilter bool m_initialized{ false }; - // The absolute base file path to the location of filter .ini file(s) - // Needed when resolving the relative paths contained within those files. - std::filesystem::path m_filterFilesAbsoluteBasePath; - - // The "operational base path" is the base path for callers initializing this object. - // E.g. in case of an orign call from ResourceGroup::ResourceGroupImpl::CreateFromDirectory() - // this corresponds to the "params.directory" parameter. - std::filesystem::path m_operationalBasePath; + // The filter .ini file(s) prefixmap attribute absolute base path + // Needed in order to resolve any relative paths from within those files + std::filesystem::path m_prefixmapAbsoluteBasePath; // Vector of all the filter .ini files (wrapped in FilterResourceFile objects) std::vector> m_filterFiles; diff --git a/tools/src/FilterResourceFile.cpp b/tools/src/FilterResourceFile.cpp index 85c2eca..c547073 100644 --- a/tools/src/FilterResourceFile.cpp +++ b/tools/src/FilterResourceFile.cpp @@ -49,7 +49,7 @@ void FilterResourceFile::ParseIniFile( const std::filesystem::path& iniFilePath INIReader reader( iniFilePath.generic_string() ); if( reader.ParseError() != 0 ) { - throw std::runtime_error( "Failed to parse INI file: " + iniFilePath.generic_string() + " - " + reader.ParseErrorMessage() ); + throw std::runtime_error( "Failed to parse INI file: " + std::filesystem::absolute(iniFilePath ).generic_string() + " - " + reader.ParseErrorMessage() ); } // Parse the [DEFAULT] section diff --git a/tools/src/ResourceFilter.cpp b/tools/src/ResourceFilter.cpp index 9f7a5c8..4d612e5 100644 --- a/tools/src/ResourceFilter.cpp +++ b/tools/src/ResourceFilter.cpp @@ -12,15 +12,13 @@ namespace ResourceTools // Construct a ResourceFilter object by passing in filter .ini file(s). // Arguments: // iniFilePaths - vector of file paths to the filter .ini files -// filterFilesAbsoluteBasePath - absolute base path to the location of filter .ini file(s) -// operationalBasePath - absolute base path to use for resolving relative input file paths when checking against filter rules +// prefixmapAbsoluteBasePath - absolute base path for resolving prefixmap attribute within filter .ini file(s) // ------------------------------------------------------------- ResourceFilter::ResourceFilter( const std::vector& iniFilePaths, - const std::filesystem::path& filterFilesAbsoluteBasePath, - const std::filesystem::path& operationalBasePath ) + const std::filesystem::path& prefixmapAbsoluteBasePath ) { - Initialize( iniFilePaths, filterFilesAbsoluteBasePath, operationalBasePath ); + Initialize( iniFilePaths, prefixmapAbsoluteBasePath ); } // ------------------------------------------------------------- @@ -29,30 +27,31 @@ ResourceFilter::ResourceFilter( // FilterResourceFile objects for each of the supplied filter .ini file(s). // Arguments: // iniFilePaths - vector of file paths to the filter .ini files -// filterFilesAbsoluteBasePath - absolute base path to the location of filter .ini file(s) -// operationalBasePath - absolute base path to use for resolving relative input file paths when checking against filter rules +// prefixmapAbsoluteBasePath - absolute base path for resolving prefixmap attribute within filter .ini file(s) // Return Value: // None (void). // ------------------------------------------------------------- -void ResourceFilter::Initialize( const std::vector& iniFilePaths, - const std::filesystem::path& filterFilesAbsoluteBasePath, - const std::filesystem::path& operationalBasePath ) +void ResourceFilter::Initialize( + const std::vector& iniFilePaths, + const std::filesystem::path& prefixmapAbsoluteBasePath ) { if( m_initialized ) { throw std::runtime_error( "ResourceFilter is already initialized." ); } - m_filterFilesAbsoluteBasePath = filterFilesAbsoluteBasePath; - m_operationalBasePath = operationalBasePath; + m_prefixmapAbsoluteBasePath = prefixmapAbsoluteBasePath; std::set uniquePaths( iniFilePaths.begin(), iniFilePaths.end() ); for( const auto& path : uniquePaths ) { try { - // Make sure we initialize the FilterResourceFile based on the absolute path of the .ini file - m_filterFiles.emplace_back( std::make_unique( GetAbsolutePathFromBase( path, m_filterFilesAbsoluteBasePath ) ) ); + // Supplied filter .ini files should already be passed in with absolute path. + // In case they are not, fallback to resolving absolute path based on the + // m_prefixmapAbsoluteBasePath property insted of the current working directory. + // That property is otherwise intended for resolving relative paths within those filter .ini files. + m_filterFiles.emplace_back( std::make_unique( GetAbsolutePathFromBase( path, m_prefixmapAbsoluteBasePath ) ) ); } catch( const std::exception& e ) { @@ -108,13 +107,8 @@ const std::map& ResourceFilter::GetFullResolv // ------------------------------------------------------------- bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::path& inFilePath ) { - // Make sure we work with the lexically normalized absolute path representation of the input file - // In case the inFilePath is not already absolute, we'll set the base path to the "params.directory". - // - This is because when called from ResourceGroup::ResourceGroupImpl::CreateFromDirectory() function - // it will be iterating over files from that location. - // If it is already absolute, then fine. If not, we will resolve it relative to whatever the - // "operational base path" is for this ResourceFilter (i.e. "params.directory" when called from CLI). - std::string inFileNormalAbsPathStr = NormalizePath( GetAbsolutePathFromBase( inFilePath, m_operationalBasePath ).generic_string() ); + // Make sure we work with the lexically normalized (already absolute) path representation of the input file. + std::string inFileNormalAbsPathStr = NormalizePath( inFilePath.generic_string() ); std::filesystem::path inFilePathAbs = std::filesystem::absolute( inFileNormalAbsPathStr ); // Priority: lower is higher priority: @@ -128,8 +122,9 @@ bool ResourceFilter::FilePathMatchesIncludeFilterRules( const std::filesystem::p const auto& resolvedPathMap = GetFullResolvedPathMap(); for( const auto& [resolvedRelativePathStr, filter] : resolvedPathMap ) { - // Convert the relative path to a lexically normalized absolute path for comparison - std::string resolvedNormalAbsPathStr = NormalizePath( GetAbsolutePathFromBase( resolvedRelativePathStr, m_filterFilesAbsoluteBasePath ).generic_string() ); + // Convert the relative path from the filter rules .ini file(s) to a lexically normalized absolute path + // for comparison with the similarly lexically normalized absolute path of the input file (inFilePath). + std::string resolvedNormalAbsPathStr = NormalizePath( GetAbsolutePathFromBase( resolvedRelativePathStr, m_prefixmapAbsoluteBasePath ).generic_string() ); std::filesystem::path resolvedPathAbs = std::filesystem::absolute( resolvedNormalAbsPathStr ); if( resolvedNormalAbsPathStr == inFileNormalAbsPathStr ) From c14fab96226f9d2fd72f562f1ae1b37f8899d748 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Tue, 17 Feb 2026 16:01:04 +0000 Subject: [PATCH 123/124] Add fileProcessing update message for skipped filter files - Logged out message for every 25th file skipped. - Done to limit the "skip spam", but enough to see that there is progress. --- src/ResourceGroupImpl.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ResourceGroupImpl.cpp b/src/ResourceGroupImpl.cpp index 71312b0..c55ceeb 100644 --- a/src/ResourceGroupImpl.cpp +++ b/src/ResourceGroupImpl.cpp @@ -87,6 +87,7 @@ Result ResourceGroup::ResourceGroupImpl::CreateFromDirectory( const CreateResour StatusSettings fileProcessingInnerStatusSettings; statusSettings.Update( CarbonResources::StatusProgressType::PERCENTAGE, 10, 90, "Processing Files", &fileProcessingInnerStatusSettings ); + int fileSkipCount = 0; for( const std::filesystem::directory_entry& entry : recursiveDirectoryIter ) { if( entry.is_regular_file() ) @@ -100,6 +101,11 @@ Result ResourceGroup::ResourceGroupImpl::CreateFromDirectory( const CreateResour // Check if the file, i.e. entry.path() should be included or excluded based on filtering rules if( !resourceFilter.FilePathMatchesIncludeFilterRules( entry.path() ) ) { + if( fileSkipCount++ % 25 == 0 ) + { + std::string skipMessage = "Skipping file [" + std::to_string( fileSkipCount - 1 ) + "] as it doesn't match filters: " + entry.path().string(); + fileProcessingInnerStatusSettings.Update( CarbonResources::StatusProgressType::UNBOUNDED, 0, 0, skipMessage ); + } continue; } } From f5b24bddcb0e8bafd1e07b20a57d379dc5c5d173 Mon Sep 17 00:00:00 2001 From: CCP ChargeBack <35330827+ccp-chargeback@users.noreply.github.com> Date: Wed, 18 Feb 2026 11:58:39 +0000 Subject: [PATCH 124/124] Add documentation for create-group with filtering --- doc/source/DesignDocuments/filterIniFiles.rst | 80 +++++++++++++++++++ .../Guides/HowToCreateAResourceGroup.rst | 15 +++- doc/source/designDocuments.rst | 1 + 3 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 doc/source/DesignDocuments/filterIniFiles.rst diff --git a/doc/source/DesignDocuments/filterIniFiles.rst b/doc/source/DesignDocuments/filterIniFiles.rst new file mode 100644 index 0000000..e5b0a62 --- /dev/null +++ b/doc/source/DesignDocuments/filterIniFiles.rst @@ -0,0 +1,80 @@ +Filter .ini File Format +========================== + +When generating Resource Groups it is possible to control the included/excluded files and folders, with the help of a resource filter .ini file(s). + + +Example Filter .ini file +------------------------ + +.. code-block:: ini + + # =============== This is a comment =============== + # Every filter .ini file needs the following: + # - [DEFAULT] section containing: + # - "prefixmap" attribute (mandatory): + # This is a space separated list of prefixes and their associated relative paths (semicolon separated). + # Format is: prefixA:path1;path2 prefixB:path3 (see actual example below) + # + # - [OneOrMoreNamedSections] containing: + # - "filter" attribute (optional): + # A top-level include/exclude filter ruleset that is applied to any + # "respaths" and "resfile" attribute element within this [NamedSection]. + # Format is: [ .includeExtension1 IncludeFullOrPartialFileOrFolderName ] + # ![ .excludeExtension1 ExcludeFullOrPartialFileOrFolderName ] + # If there is no filter attribute specified (i.e. optional), then wild-card include all is implied ([ * ]). + # - "respaths" attribute (mandatory, multi-line): + # A multi-line list of resource path entries, with or without wild-cards (* or ...). + # The paths are relative to their basepath prefix from the "prefixmap". + # Each line is a separate resource path entry, with or without an optional in-line filter. + # Search paths are resolved for each entry using a lookup from the "prefixmap". + # Supported wildcards are: + # - * to match any file within the current folder + # - ... to match any file or folder recursively from the current folder + # Format is: prefix:some/path/possible_wildcard [ OptionalExtraInclude ] ![ OptionalExtraExclude ] + # - "resfile" attribute (optional, single-line): + # Identical to a "respaths" entry, but for a single file path entry only. + # Only supported for backwards compatibility of older filter .ini files. + # Any single, specific file entries can (and should) be represented in the "respaths" attribute instead. + ; =============== This is also a comment =============== + + [DEFAULT] + prefixmap = res:res;../common/res resbin:../bin + + [NamedSection1] + filter = [ .yaml .txt ] + respaths = res:/... + res:/SomeFolder/... ![ AlsoExclude ] + res:/SomeOtherFolder/* [ .red ] + resbin:/* [ .dll ] ![ .yaml .txt ] + res:/SomeFolder/FolderWithAlsoExcludeName/includeThisFile.csv ] + + # =============== For this example CLI create-group call =============== + # resources.exe + # create-group C:\Build + # --output-file ResourceGroup.yaml + # --document-version 0.1.0 + # --filter-file C:\Build\Resources\FolderForFilters\filterRules1.ini + # --filter-file-prefixmap-basepath C:\Build\Resources + # ================ The following rules would be applied ================ + # 1. Any .yaml or .txt file within: + # "res:/..." + # - C:\Build\Resources\res (and its subfolders "...") would be included. + # - C:\Build\common\res (and its subfolders "...") would be included. + # 2. Unless the .yaml or .txt file is within: + # "res:/SomeFolder/..." and "AlsoExclude" is part of its name, then it would be excluded. + # - C:\Build\Resources\res\SomeFolder (and its subfolders "...") + # - C:\Build\common\res\SomeFolder (and its subfolders "...") + # 3. Additionally, any file within: + # "res:/SomeOtherFolder/*" would be included, but only if it is a .red file. + # - C:\Build\Resources\res\SomeOtherFolder (only this folder) + # - C:\Build\common\res\SomeOtherFolder (only this folder) + # 4. Then any .dll file within: + # "resbin:/*" only .dll files would be included, excluding .yaml and .txt files. + # - C:\Build\bin (only this folder) + # 5. Finally, we should include this specific file rule: + # "res:/SomeFolder/FolderWithAlsoExcludeName/includeThisFile.csv" + # - C:\Build\Resources\res\SomeFolder\FolderWithAlsoExcludeName\includeThisFile.csv + # - C:\Build\common\res\SomeFolder\FolderWithAlsoExcludeName\includeThisFile.csv + # ====================================================================== + ... diff --git a/doc/source/Guides/HowToCreateAResourceGroup.rst b/doc/source/Guides/HowToCreateAResourceGroup.rst index aa8a67a..a3b0c85 100644 --- a/doc/source/Guides/HowToCreateAResourceGroup.rst +++ b/doc/source/Guides/HowToCreateAResourceGroup.rst @@ -5,16 +5,25 @@ Resource Groups can be created via the lib or CLI. This example uses the CLI. .. code:: - .\resources.exe create-group C:\Build --output-file ResourceGroup.yaml + .\resources.exe create-group C:\Build --output-file ResourceGroup.yaml --document-version 0.1.0 --filter-file C:\Build\Resources\FolderForFilters\filterRules1.ini --filter-file-prefixmap-basepath C:\Build\Resources **Arguments:** 1. Positional argument - Base directory to create resource group from. 2. ``--output-file`` - Filename for created resource group. +3. ``--document-version`` - The type of resource group to create/output. Valid values are: default=0.1.0 (yaml) and 0.0.0 (csv). +4. ``--filter-file`` - Absolute path to a .ini file containing include/exclude resource filtering rules. If not set = no filtering. Can be specified multiple times to combine filter rules from multiple files. See :doc:`../DesignDocuments/filterIniFiles` for more information on resource filter .ini files. +5. ``--filter-file-prefixmap-basepath`` - The absolute base directory for resolving relative paths contained within the supplied filter .ini file(s) **prefixmap** attribute. Ignored if the filter-file argument is not supplied. .. note:: See CLI help for more information regarding options. -This will create a ``ResourceGroup.yaml`` file representing the input directory ``C:\Build``. +The example CLI operation from the above example will: -The resource group files are human readable yaml files and quite self explanatory. For more information see :doc:`../DesignDocuments/filesystemDesign` \ No newline at end of file +* Create an output ``ResourceGroup.yaml`` file of the default document version 0.1.0 (yaml) format. +* Representing the contents of the input directory ``C:\Build`` +* Limiting it based on include/exclude filter rules specified in ``C:\Build\Resources\FolderForFilters\filterRules1.ini``. +* While resolving relative **prefixmap** paths within it using the supplied ``C:\Build\Resources`` base path. + + +The resource group files (in document-version 0.1.0) are human readable yaml files and quite self explanatory. For more information see :doc:`../DesignDocuments/filesystemDesign` diff --git a/doc/source/designDocuments.rst b/doc/source/designDocuments.rst index 3ab23d2..d1c7f88 100644 --- a/doc/source/designDocuments.rst +++ b/doc/source/designDocuments.rst @@ -8,3 +8,4 @@ Documents contained in this section detail design approaches in resources. DesignDocuments/filesystemDesign DesignDocuments/resourceGroupFileFormat + DesignDocuments/filterIniFiles